diff options
Diffstat (limited to 'drivers/media/dvb-frontends')
336 files changed, 210820 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig new file mode 100644 index 0000000000..2ef2ff2a38 --- /dev/null +++ b/drivers/media/dvb-frontends/Kconfig @@ -0,0 +1,957 @@ +# SPDX-License-Identifier: GPL-2.0 + +if MEDIA_DIGITAL_TV_SUPPORT + +comment "DVB Frontend drivers auto-selected by 'Autoselect ancillary drivers'" + depends on MEDIA_HIDE_ANCILLARY_SUBDRV + +menu "Customise DVB Frontends" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +comment "Multistandard (satellite) frontends" + depends on DVB_CORE + +config DVB_M88DS3103 + tristate "Montage Technology M88DS3103" + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_MXL5XX + tristate "MaxLinear MxL5xx based tuner-demodulators" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL5xx family of DVB-S/S2 tuners/demodulators. + + Say Y when you want to support these frontends. + +config DVB_STB0899 + tristate "STB0899 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want + to support this demodulator based frontends + +config DVB_STB6100 + tristate "STB6100 based tuners" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A Silicon tuner from ST used in conjunction with the STB0899 + demodulator. Say Y when you want to support this tuner. + +config DVB_STV090x + tristate "STV0900/STV0903(A/B) based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators. + Say Y when you want to support these frontends. + +config DVB_STV0910 + tristate "STV0910 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + ST STV0910 DVB-S/S2 demodulator driver. + + Say Y when you want to support these frontends. + +config DVB_STV6110x + tristate "STV6110/(A) based tuners" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A Silicon tuner that supports DVB-S and DVB-S2 modes + +config DVB_STV6111 + tristate "STV6111 based tuners" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A Silicon tuner that supports DVB-S and DVB-S2 modes + + Say Y when you want to support these frontends. + +comment "Multistandard (cable + terrestrial) frontends" + depends on DVB_CORE + +config DVB_DRXK + tristate "Micronas DRXK based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Micronas DRX-K DVB-C/T demodulator. + + Say Y when you want to support this frontend. + +config DVB_MN88472 + tristate "Panasonic MN88472" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_MN88473 + tristate "Panasonic MN88473" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_SI2165 + tristate "Silicon Labs si2165 based" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-C/T demodulator. + + Say Y when you want to support this frontend. + +config DVB_TDA18271C2DD + tristate "NXP TDA18271C2 silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + NXP TDA18271 silicon tuner. + + Say Y when you want to support this tuner. + +comment "DVB-S (satellite) frontends" + depends on DVB_CORE + +config DVB_CX24110 + tristate "Conexant CX24110 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_CX24116 + tristate "Conexant CX24116 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + +config DVB_CX24117 + tristate "Conexant CX24117 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend. + +config DVB_CX24120 + tristate "Conexant CX24120 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + +config DVB_CX24123 + tristate "Conexant CX24123 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_DS3000 + tristate "Montage Tehnology DS3000 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + +config DVB_MB86A16 + tristate "Fujitsu MB86A16 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/DSS Direct Conversion reveiver. + Say Y when you want to support this frontend. + +config DVB_MT312 + tristate "Zarlink VP310/MT312/ZL10313 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_S5H1420 + tristate "Samsung S5H1420 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_SI21XX + tristate "Silicon Labs SI21XX based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_STB6000 + tristate "ST STB6000 silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S silicon tuner module. Say Y when you want to support this tuner. + +config DVB_STV0288 + tristate "ST STV0288 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_STV0299 + tristate "ST STV0299 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_STV0900 + tristate "ST STV0900 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 demodulator. Say Y when you want to support this frontend. + +config DVB_STV6110 + tristate "ST STV6110 silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S silicon tuner module. Say Y when you want to support this tuner. + +config DVB_TDA10071 + tristate "NXP TDA10071" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_TDA10086 + tristate "Philips TDA10086 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_TDA8083 + tristate "Philips TDA8083 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_TDA8261 + tristate "Philips TDA8261 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_TDA826X + tristate "Philips TDA826X silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S silicon tuner module. Say Y when you want to support this tuner. + +config DVB_TS2020 + tristate "Montage Tehnology TS2020 based tuners" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. + +config DVB_TUA6100 + tristate "Infineon TUA6100 PLL" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S PLL chip. + +config DVB_TUNER_CX24113 + tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_TUNER_ITD1000 + tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_VES1X93 + tristate "VLSI VES1893 or VES1993 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_ZL10036 + tristate "Zarlink ZL10036 silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_ZL10039 + tristate "Zarlink ZL10039 silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +comment "DVB-T (terrestrial) frontends" + depends on DVB_CORE + +config DVB_AF9013 + tristate "Afatech AF9013 demodulator" + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_AS102_FE + tristate + depends on DVB_CORE + default DVB_AS102 + +config DVB_CX22700 + tristate "Conexant CX22700 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_CX22702 + tristate "Conexant cx22702 demodulator (OFDM)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_CXD2820R + tristate "Sony CXD2820R" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_CXD2841ER + tristate "Sony CXD2841ER" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_DIB3000MB + tristate "DiBcom 3000M-B" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + +config DVB_DIB3000MC + tristate "DiBcom 3000P/M-C" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + +config DVB_DIB7000M + tristate "DiBcom 7000MA/MB/PA/PB/MC" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + +config DVB_DIB7000P + tristate "DiBcom 7000PC" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + +config DVB_DIB9000 + tristate "DiBcom 9000" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + +config DVB_DRXD + tristate "Micronas DRXD driver" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + Note: this driver was based on vendor driver reference code (released + under the GPL) as opposed to the existing drx397xd driver, which + was written via reverse engineering. + +config DVB_EC100 + tristate "E3C EC100" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_GP8PSK_FE + tristate + depends on DVB_CORE + default DVB_USB_GP8PSK + +config DVB_L64781 + tristate "LSI L64781" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_MT352 + tristate "Zarlink MT352 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_NXT6000 + tristate "NxtWave Communications NXT6000 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_RTL2830 + tristate "Realtek RTL2830 DVB-T" + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_RTL2832 + tristate "Realtek RTL2832 DVB-T" + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_RTL2832_SDR + tristate "Realtek RTL2832 SDR" + depends on DVB_CORE && I2C && I2C_MUX && VIDEO_DEV && MEDIA_SDR_SUPPORT && USB + select DVB_RTL2832 + select VIDEOBUF2_VMALLOC + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this SDR module. + +config DVB_S5H1432 + tristate "Samsung s5h1432 demodulator (OFDM)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_SI2168 + tristate "Silicon Labs Si2168" + depends on DVB_CORE && I2C && I2C_MUX + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_SP887X + tristate "Spase sp887x based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + This driver needs external firmware. Please use the command + "<kerneldir>/scripts/get_dvb_firmware sp887x" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + +config DVB_STV0367 + tristate "ST STV0367 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T/C tuner module. Say Y when you want to support this frontend. + +config DVB_TDA10048 + tristate "Philips TDA10048HN based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_TDA1004X + tristate "Philips TDA10045H/TDA10046H based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + This driver needs external firmware. Please use the commands + "<kerneldir>/scripts/get_dvb_firmware tda10045", + "<kerneldir>/scripts/get_dvb_firmware tda10046" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + +config DVB_ZD1301_DEMOD + tristate "ZyDAS ZD1301" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_ZL10353 + tristate "Zarlink ZL10353 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +source "drivers/media/dvb-frontends/cxd2880/Kconfig" + +comment "DVB-C (cable) frontends" + depends on DVB_CORE + +config DVB_STV0297 + tristate "ST STV0297 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-C tuner module. Say Y when you want to support this frontend. + +config DVB_TDA10021 + tristate "Philips TDA10021 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-C tuner module. Say Y when you want to support this frontend. + +config DVB_TDA10023 + tristate "Philips TDA10023 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-C tuner module. Say Y when you want to support this frontend. + +config DVB_VES1820 + tristate "VLSI VES1820 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-C tuner module. Say Y when you want to support this frontend. + +comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" + depends on DVB_CORE + +config DVB_AU8522 + depends on DVB_CORE && I2C + tristate + +config DVB_AU8522_DTV + tristate "Auvitek AU8522 based DTV demod" + depends on DVB_CORE && I2C + select DVB_AU8522 + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable DTV demodulation support for this frontend. + +config DVB_AU8522_V4L + tristate "Auvitek AU8522 based ATV demod" + depends on VIDEO_DEV && DVB_CORE && I2C + select DVB_AU8522 + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable ATV demodulation support for this frontend. + +config DVB_BCM3510 + tristate "Broadcom BCM3510" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to + support this frontend. + +config DVB_LG2160 + tristate "LG Electronics LG216x based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC/MH demodulator module. Say Y when you want + to support this frontend. + +config DVB_LGDT3305 + tristate "LG Electronics LGDT3304 and LGDT3305 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + +config DVB_LGDT3306A + tristate "LG Electronics LGDT3306A based" + depends on DVB_CORE && I2C && I2C_MUX + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want + to support this frontend. + +config DVB_LGDT330X + tristate "LG Electronics LGDT3302/LGDT3303 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + +config DVB_MXL692 + tristate "MaxLinear MXL692 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL692 is a combo tuner-demodulator that + supports ATSC 8VSB and QAM modes. Say Y when you want to + support this frontend. + +config DVB_NXT200X + tristate "NxtWave Communications NXT2002/NXT2004 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + + This driver needs external firmware. Please use the commands + "<kerneldir>/scripts/get_dvb_firmware nxt2002" and + "<kerneldir>/scripts/get_dvb_firmware nxt2004" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + +config DVB_OR51132 + tristate "Oren OR51132 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + + This driver needs external firmware. Please use the commands + "<kerneldir>/scripts/get_dvb_firmware or51132_vsb" and/or + "<kerneldir>/scripts/get_dvb_firmware or51132_qam" to + download firmwares for 8VSB and QAM64/256, respectively. Copy them to + /usr/lib/hotplug/firmware or /lib/firmware (depending on + configuration of firmware hotplug). + +config DVB_OR51211 + tristate "Oren OR51211 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB tuner module. Say Y when you want to support this frontend. + + This driver needs external firmware. Please use the command + "<kerneldir>/scripts/get_dvb_firmware or51211" to + download it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + +config DVB_S5H1409 + tristate "Samsung S5H1409 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + +config DVB_S5H1411 + tristate "Samsung S5H1411 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + +comment "ISDB-T (terrestrial) frontends" + depends on DVB_CORE + +config DVB_DIB8000 + tristate "DiBcom 8000MB/MC" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. + Say Y when you want to support this frontend. + +config DVB_MB86A20S + tristate "Fujitsu mb86a20s" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. + Say Y when you want to support this frontend. + +config DVB_S921 + tristate "Sharp S921 frontend" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. + Say Y when you want to support this frontend. + +comment "ISDB-S (satellite) & ISDB-T (terrestrial) frontends" + depends on DVB_CORE + +config DVB_MN88443X + tristate "Socionext MN88443x" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for Socionext/Panasonic MN884433 and MN884434 + ISDB-S + ISDB-T demodulator. + Say Y when you want to support this frontend. + +config DVB_TC90522 + tristate "Toshiba TC90522" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator. + Say Y when you want to support this frontend. + +comment "Digital terrestrial only tuners/PLL" + depends on DVB_CORE + +config DVB_PLL + tristate "Generic I2C PLL based tuners" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + This module drives a number of tuners based on PLL chips with a + common I2C interface. Say Y when you want to support these tuners. + +config DVB_TUNER_DIB0070 + tristate "DiBcom DiB0070 silicon base-band tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for the silicon baseband tuner DiB0070 from DiBcom. + This device is only used inside a SiP called together with a + demodulator for now. + +config DVB_TUNER_DIB0090 + tristate "DiBcom DiB0090 silicon base-band tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for the silicon baseband tuner DiB0090 from DiBcom. + This device is only used inside a SiP called together with a + demodulator for now. + +comment "SEC control devices for DVB-S" + depends on DVB_CORE + +config DVB_A8293 + tristate "Allegro A8293" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + +config DVB_AF9033 + tristate "Afatech AF9033 DVB-T demodulator" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + +config DVB_ASCOT2E + tristate "Sony Ascot2E tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_ATBM8830 + tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + +config DVB_HELENE + tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_HORUS3A + tristate "Sony Horus3A tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_ISL6405 + tristate "ISL6405 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chip. + +config DVB_ISL6421 + tristate "ISL6421 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chip. + +config DVB_ISL6423 + tristate "ISL6423 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A SEC controller chip from Intersil + +config DVB_IX2505V + tristate "Sharp IX2505V silicon tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_LGS8GL5 + tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + +config DVB_LGS8GXX + tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" + depends on DVB_CORE && I2C + select FW_LOADER + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + +config DVB_LNBH25 + tristate "LNBH25 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chip. + Say Y when you want to support this chip. + +config DVB_LNBH29 + tristate "LNBH29 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + LNB power supply and control voltage + regulator chip with step-up converter + and I2C interface for STMicroelectronics LNBH29. + Say Y when you want to support this chip. + +config DVB_LNBP21 + tristate "LNBP21/LNBH24 SEC controllers" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chips. + +config DVB_LNBP22 + tristate "LNBP22 SEC controllers" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + LNB power supply and control voltage + regulator chip with step-up converter + and I2C interface. + Say Y when you want to support this chip. + +config DVB_M88RS2000 + tristate "M88RS2000 DVB-S demodulator and tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. + Say Y when you want to support this frontend. + +config DVB_TDA665x + tristate "TDA665x tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Support for tuner modules based on Philips TDA6650/TDA6651 chips. + Say Y when you want to support this chip. + + Currently supported tuners: + * Panasonic ENV57H12D5 (ET-50DT) + +source "drivers/media/dvb-frontends/drx39xyj/Kconfig" + +comment "Common Interface (EN50221) controller drivers" + depends on DVB_CORE + +config DVB_CXD2099 + tristate "Sony CXD2099AR Common Interface driver" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for the CI controller currently found mostly on + Digital Devices DuoFlex CI (single) addon modules. + + Say Y when you want to support these devices. + +config DVB_SP2 + tristate "CIMaX SP2" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + CIMaX SP2/SP2HF Common Interface module. + +endmenu # Customise DVB Frontends + +endif # MEDIA_DIGITAL_TV_SUPPORT + +comment "Tools to develop new frontends" + depends on MEDIA_TEST_SUPPORT + +config DVB_DUMMY_FE + tristate "Dummy frontend driver" + depends on MEDIA_TEST_SUPPORT && DVB_CORE + help + Dummy skeleton frontend driver. diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile new file mode 100644 index 0000000000..a93146cb42 --- /dev/null +++ b/drivers/media/dvb-frontends/Makefile @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the kernel DVB frontend device drivers. +# + +ccflags-y += -I$(srctree)/drivers/media/tuners/ + +# FIXME: RTL2832 SDR driver uses power management directly from USB IF driver +ifdef CONFIG_DVB_RTL2832_SDR + ccflags-y += -I$(srctree)/drivers/media/usb/dvb-usb-v2 +endif + +cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o +drxd-objs := drxd_firm.o drxd_hard.o +drxk-objs := drxk_hard.o +stb0899-objs := stb0899_drv.o stb0899_algo.o +stv0900-objs := stv0900_core.o stv0900_sw.o + +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) + +obj-$(CONFIG_DVB_A8293) += a8293.o +obj-$(CONFIG_DVB_AF9013) += af9013.o +obj-$(CONFIG_DVB_AF9033) += af9033.o +obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o +obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o +obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o +obj-$(CONFIG_DVB_AU8522) += au8522_common.o +obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o +obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o +obj-$(CONFIG_DVB_BCM3510) += bcm3510.o +obj-$(CONFIG_DVB_CX22700) += cx22700.o +obj-$(CONFIG_DVB_CX22702) += cx22702.o +obj-$(CONFIG_DVB_CX24110) += cx24110.o +obj-$(CONFIG_DVB_CX24116) += cx24116.o +obj-$(CONFIG_DVB_CX24117) += cx24117.o +obj-$(CONFIG_DVB_CX24120) += cx24120.o +obj-$(CONFIG_DVB_CX24123) += cx24123.o +obj-$(CONFIG_DVB_CXD2099) += cxd2099.o +obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o +obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o +obj-$(CONFIG_DVB_CXD2880) += cxd2880/ +obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o +obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o +obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o +obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o +obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o +obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o +obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj/ +obj-$(CONFIG_DVB_DRXD) += drxd.o +obj-$(CONFIG_DVB_DRXK) += drxk.o +obj-$(CONFIG_DVB_DS3000) += ds3000.o +obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o +obj-$(CONFIG_DVB_EC100) += ec100.o +obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o +obj-$(CONFIG_DVB_HELENE) += helene.o +obj-$(CONFIG_DVB_HORUS3A) += horus3a.o +obj-$(CONFIG_DVB_ISL6405) += isl6405.o +obj-$(CONFIG_DVB_ISL6421) += isl6421.o +obj-$(CONFIG_DVB_ISL6423) += isl6423.o +obj-$(CONFIG_DVB_IX2505V) += ix2505v.o +obj-$(CONFIG_DVB_L64781) += l64781.o +obj-$(CONFIG_DVB_LG2160) += lg2160.o +obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o +obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o +obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o +obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o +obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o +obj-$(CONFIG_DVB_LNBH25) += lnbh25.o +obj-$(CONFIG_DVB_LNBH29) += lnbh29.o +obj-$(CONFIG_DVB_LNBP21) += lnbp21.o +obj-$(CONFIG_DVB_LNBP22) += lnbp22.o +obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o +obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o +obj-$(CONFIG_DVB_MB86A16) += mb86a16.o +obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o +obj-$(CONFIG_DVB_MN88443X) += mn88443x.o +obj-$(CONFIG_DVB_MN88472) += mn88472.o +obj-$(CONFIG_DVB_MN88473) += mn88473.o +obj-$(CONFIG_DVB_MT312) += mt312.o +obj-$(CONFIG_DVB_MT352) += mt352.o +obj-$(CONFIG_DVB_MXL5XX) += mxl5xx.o +obj-$(CONFIG_DVB_MXL692) += mxl692.o +obj-$(CONFIG_DVB_NXT200X) += nxt200x.o +obj-$(CONFIG_DVB_NXT6000) += nxt6000.o +obj-$(CONFIG_DVB_OR51132) += or51132.o +obj-$(CONFIG_DVB_OR51211) += or51211.o +obj-$(CONFIG_DVB_PLL) += dvb-pll.o +obj-$(CONFIG_DVB_RTL2830) += rtl2830.o +obj-$(CONFIG_DVB_RTL2832) += rtl2832.o +obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o +obj-$(CONFIG_DVB_S5H1409) += s5h1409.o +obj-$(CONFIG_DVB_S5H1411) += s5h1411.o +obj-$(CONFIG_DVB_S5H1420) += s5h1420.o +obj-$(CONFIG_DVB_S5H1432) += s5h1432.o +obj-$(CONFIG_DVB_S921) += s921.o +obj-$(CONFIG_DVB_SI2165) += si2165.o +obj-$(CONFIG_DVB_SI2168) += si2168.o +obj-$(CONFIG_DVB_SI21XX) += si21xx.o +obj-$(CONFIG_DVB_SP2) += sp2.o +obj-$(CONFIG_DVB_SP887X) += sp887x.o +obj-$(CONFIG_DVB_STB0899) += stb0899.o +obj-$(CONFIG_DVB_STB6000) += stb6000.o +obj-$(CONFIG_DVB_STB6100) += stb6100.o +obj-$(CONFIG_DVB_STV0288) += stv0288.o +obj-$(CONFIG_DVB_STV0297) += stv0297.o +obj-$(CONFIG_DVB_STV0299) += stv0299.o +obj-$(CONFIG_DVB_STV0367) += stv0367.o +obj-$(CONFIG_DVB_STV0900) += stv0900.o +obj-$(CONFIG_DVB_STV090x) += stv090x.o +obj-$(CONFIG_DVB_STV0910) += stv0910.o +obj-$(CONFIG_DVB_STV6110) += stv6110.o +obj-$(CONFIG_DVB_STV6110x) += stv6110x.o +obj-$(CONFIG_DVB_STV6111) += stv6111.o +obj-$(CONFIG_DVB_TC90522) += tc90522.o +obj-$(CONFIG_DVB_TDA10021) += tda10021.o +obj-$(CONFIG_DVB_TDA10023) += tda10023.o +obj-$(CONFIG_DVB_TDA10048) += tda10048.o +obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o +obj-$(CONFIG_DVB_TDA10071) += tda10071.o +obj-$(CONFIG_DVB_TDA10086) += tda10086.o +obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o +obj-$(CONFIG_DVB_TDA665x) += tda665x.o +obj-$(CONFIG_DVB_TDA8083) += tda8083.o +obj-$(CONFIG_DVB_TDA8261) += tda8261.o +obj-$(CONFIG_DVB_TDA826X) += tda826x.o +obj-$(CONFIG_DVB_TS2020) += ts2020.o +obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o +obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o +obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o +obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o +obj-$(CONFIG_DVB_VES1820) += ves1820.o +obj-$(CONFIG_DVB_VES1X93) += ves1x93.o +obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o +obj-$(CONFIG_DVB_ZL10036) += zl10036.o +obj-$(CONFIG_DVB_ZL10039) += zl10039.o +obj-$(CONFIG_DVB_ZL10353) += zl10353.o diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c new file mode 100644 index 0000000000..f39887c049 --- /dev/null +++ b/drivers/media/dvb-frontends/a8293.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Allegro A8293 SEC driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#include "a8293.h" + +#define A8293_FLAG_ODT 0x10 + +struct a8293_dev { + struct i2c_client *client; + u8 reg[2]; + int volt_slew_nanos_per_mv; +}; + +/* + * When increasing voltage, do so in minimal steps over time, minimizing + * risk of vIN undervoltage. + */ + +static int a8293_set_voltage_slew(struct a8293_dev *dev, + struct i2c_client *client, + enum fe_sec_voltage fe_sec_voltage, + int min_nanos_per_mv) +{ + int ret; + u8 reg0, reg1; + int new_volt_idx; + const int idx_to_mv[] = { + 0, 12709, 13042, 13375, 14042, 15042, 18042, 18709, 19042 + }; + const u8 idx_to_reg[] = { + 0x00, 0x20, 0x21, 0x22, 0x24, 0x27, 0x28, 0x2A, 0x2B + }; + int this_volt_idx; + u8 status; + int prev_volt_idx; + + dev_dbg(&client->dev, "set_voltage_slew fe_sec_voltage=%d\n", + fe_sec_voltage); + + /* Read status register to clear any stale faults. */ + ret = i2c_master_recv(client, &status, 1); + if (ret < 0) + goto err; + + /* Determine previous voltage */ + switch (dev->reg[0] & 0x2F) { + case 0x00: + prev_volt_idx = 0; + break; + case 0x20: + prev_volt_idx = 1; + break; + case 0x21: + prev_volt_idx = 2; + break; + case 0x22: + prev_volt_idx = 3; + break; + case 0x24: + prev_volt_idx = 4; + break; + case 0x27: + prev_volt_idx = 5; + break; + case 0x28: + prev_volt_idx = 6; + break; + case 0x2A: + prev_volt_idx = 7; + break; + case 0x2B: + prev_volt_idx = 8; + break; + default: + prev_volt_idx = 0; + } + + /* Determine new voltage */ + switch (fe_sec_voltage) { + case SEC_VOLTAGE_OFF: + new_volt_idx = 0; + break; + case SEC_VOLTAGE_13: + new_volt_idx = 2; + break; + case SEC_VOLTAGE_18: + new_volt_idx = 6; + break; + default: + ret = -EINVAL; + goto err; + } + + /* Slew to new voltage if new voltage is greater than current voltage */ + this_volt_idx = prev_volt_idx; + if (this_volt_idx < new_volt_idx) { + while (this_volt_idx < new_volt_idx) { + int delta_mv = idx_to_mv[this_volt_idx+1] - idx_to_mv[this_volt_idx]; + int min_wait_time = delta_mv * min_nanos_per_mv; + + reg0 = idx_to_reg[this_volt_idx+1]; + reg0 |= A8293_FLAG_ODT; + + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + this_volt_idx++; + usleep_range(min_wait_time, min_wait_time * 2); + } + } else { /* Else just set the voltage */ + reg0 = idx_to_reg[new_volt_idx]; + reg0 |= A8293_FLAG_ODT; + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + } + + /* TMODE=0, TGATE=1 */ + reg1 = 0x82; + if (reg1 != dev->reg[1]) { + ret = i2c_master_send(client, ®1, 1); + if (ret < 0) + goto err; + dev->reg[1] = reg1; + } + + usleep_range(1500, 5000); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + + +static int a8293_set_voltage_noslew(struct dvb_frontend *fe, + enum fe_sec_voltage fe_sec_voltage) +{ + struct a8293_dev *dev = fe->sec_priv; + struct i2c_client *client = dev->client; + int ret; + u8 reg0, reg1; + + dev_dbg(&client->dev, "set_voltage_noslew fe_sec_voltage=%d\n", + fe_sec_voltage); + + switch (fe_sec_voltage) { + case SEC_VOLTAGE_OFF: + /* ENB=0 */ + reg0 = 0x10; + break; + case SEC_VOLTAGE_13: + /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ + reg0 = 0x31; + break; + case SEC_VOLTAGE_18: + /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ + reg0 = 0x38; + break; + default: + ret = -EINVAL; + goto err; + } + if (reg0 != dev->reg[0]) { + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + } + + /* TMODE=0, TGATE=1 */ + reg1 = 0x82; + if (reg1 != dev->reg[1]) { + ret = i2c_master_send(client, ®1, 1); + if (ret < 0) + goto err; + dev->reg[1] = reg1; + } + + usleep_range(1500, 50000); + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int a8293_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage fe_sec_voltage) +{ + struct a8293_dev *dev = fe->sec_priv; + struct i2c_client *client = dev->client; + int volt_slew_nanos_per_mv = dev->volt_slew_nanos_per_mv; + + dev_dbg(&client->dev, "set_voltage volt_slew_nanos_per_mv=%d\n", + volt_slew_nanos_per_mv); + + /* Use slew version if slew rate is set to a sane value */ + if (volt_slew_nanos_per_mv > 0 && volt_slew_nanos_per_mv < 1600) + a8293_set_voltage_slew(dev, client, fe_sec_voltage, + volt_slew_nanos_per_mv); + else + a8293_set_voltage_noslew(fe, fe_sec_voltage); + + return 0; +} + +static int a8293_probe(struct i2c_client *client) +{ + struct a8293_dev *dev; + struct a8293_platform_data *pdata = client->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + int ret; + u8 buf[2]; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->client = client; + dev->volt_slew_nanos_per_mv = pdata->volt_slew_nanos_per_mv; + + /* check if the SEC is there */ + ret = i2c_master_recv(client, buf, 2); + if (ret < 0) + goto err_kfree; + + /* override frontend ops */ + fe->ops.set_voltage = a8293_set_voltage; + fe->sec_priv = dev; + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n"); + return 0; +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void a8293_remove(struct i2c_client *client) +{ + struct a8293_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(dev); +} + +static const struct i2c_device_id a8293_id_table[] = { + {"a8293", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, a8293_id_table); + +static struct i2c_driver a8293_driver = { + .driver = { + .name = "a8293", + .suppress_bind_attrs = true, + }, + .probe = a8293_probe, + .remove = a8293_remove, + .id_table = a8293_id_table, +}; + +module_i2c_driver(a8293_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Allegro A8293 SEC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h new file mode 100644 index 0000000000..7fbac9735f --- /dev/null +++ b/drivers/media/dvb-frontends/a8293.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Allegro A8293 SEC driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#ifndef A8293_H +#define A8293_H + +#include <media/dvb_frontend.h> + +/* + * I2C address + * 0x08, 0x09, 0x0a, 0x0b + */ + +/** + * struct a8293_platform_data - Platform data for the a8293 driver + * @dvb_frontend: DVB frontend. + * @volt_slew_nanos_per_mv: Slew rate when increasing LNB voltage, + * in nanoseconds per millivolt. + */ +struct a8293_platform_data { + struct dvb_frontend *dvb_frontend; + int volt_slew_nanos_per_mv; +}; + +#endif /* A8293_H */ diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c new file mode 100644 index 0000000000..a829c89792 --- /dev/null +++ b/drivers/media/dvb-frontends/af9013.c @@ -0,0 +1,1576 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Afatech AF9013 demodulator driver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + */ + +#include "af9013_priv.h" + +struct af9013_state { + struct i2c_client *client; + struct regmap *regmap; + struct i2c_mux_core *muxc; + struct dvb_frontend fe; + u32 clk; + u8 tuner; + u32 if_frequency; + u8 ts_mode; + u8 ts_output_pin; + bool spec_inv; + u8 api_version[4]; + u8 gpio[4]; + + u32 bandwidth_hz; + enum fe_status fe_status; + /* RF and IF AGC limits used for signal strength calc */ + u8 strength_en, rf_agc_50, rf_agc_80, if_agc_50, if_agc_80; + unsigned long set_frontend_jiffies; + unsigned long read_status_jiffies; + unsigned long strength_jiffies; + unsigned long cnr_jiffies; + unsigned long ber_ucb_jiffies; + u16 dvbv3_snr; + u16 dvbv3_strength; + u32 dvbv3_ber; + u32 dvbv3_ucblocks; + bool first_tune; +}; + +static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) +{ + struct i2c_client *client = state->client; + int ret; + u8 pos; + u16 addr; + + dev_dbg(&client->dev, "gpio %u, gpioval %02x\n", gpio, gpioval); + + /* + * GPIO0 & GPIO1 0xd735 + * GPIO2 & GPIO3 0xd736 + */ + + switch (gpio) { + case 0: + case 1: + addr = 0xd735; + break; + case 2: + case 3: + addr = 0xd736; + break; + + default: + ret = -EINVAL; + goto err; + } + + switch (gpio) { + case 0: + case 2: + pos = 0; + break; + case 1: + case 3: + default: + pos = 4; + break; + } + + ret = regmap_update_bits(state->regmap, addr, 0x0f << pos, + gpioval << pos); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9013_set_frontend(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, sampling_freq; + bool auto_mode, spec_inv; + u8 buf[6]; + u32 if_frequency, freq_cw; + + dev_dbg(&client->dev, "frequency %u, bandwidth_hz %u\n", + c->frequency, c->bandwidth_hz); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + /* program CFOE coefficients */ + if (c->bandwidth_hz != state->bandwidth_hz) { + for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { + if (coeff_lut[i].clock == state->clk && + coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { + break; + } + } + + /* Return an error if can't find bandwidth or the right clock */ + if (i == ARRAY_SIZE(coeff_lut)) { + ret = -EINVAL; + goto err; + } + + ret = regmap_bulk_write(state->regmap, 0xae00, coeff_lut[i].val, + sizeof(coeff_lut[i].val)); + if (ret) + goto err; + } + + /* program frequency control */ + if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) { + /* get used IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, + &if_frequency); + if (ret) + goto err; + } else { + if_frequency = state->if_frequency; + } + + dev_dbg(&client->dev, "if_frequency %u\n", if_frequency); + + sampling_freq = if_frequency; + + while (sampling_freq > (state->clk / 2)) + sampling_freq -= state->clk; + + if (sampling_freq < 0) { + sampling_freq *= -1; + spec_inv = state->spec_inv; + } else { + spec_inv = !state->spec_inv; + } + + freq_cw = DIV_ROUND_CLOSEST_ULL((u64)sampling_freq * 0x800000, + state->clk); + + if (spec_inv) + freq_cw = 0x800000 - freq_cw; + + buf[0] = (freq_cw >> 0) & 0xff; + buf[1] = (freq_cw >> 8) & 0xff; + buf[2] = (freq_cw >> 16) & 0x7f; + + freq_cw = 0x800000 - freq_cw; + + buf[3] = (freq_cw >> 0) & 0xff; + buf[4] = (freq_cw >> 8) & 0xff; + buf[5] = (freq_cw >> 16) & 0x7f; + + ret = regmap_bulk_write(state->regmap, 0xd140, buf, 3); + if (ret) + goto err; + + ret = regmap_bulk_write(state->regmap, 0x9be7, buf, 6); + if (ret) + goto err; + } + + /* clear TPS lock flag */ + ret = regmap_update_bits(state->regmap, 0xd330, 0x08, 0x08); + if (ret) + goto err; + + /* clear MPEG2 lock flag */ + ret = regmap_update_bits(state->regmap, 0xd507, 0x40, 0x00); + if (ret) + goto err; + + /* empty channel function */ + ret = regmap_update_bits(state->regmap, 0x9bfe, 0x01, 0x00); + if (ret) + goto err; + + /* empty DVB-T channel function */ + ret = regmap_update_bits(state->regmap, 0x9bc2, 0x01, 0x00); + if (ret) + goto err; + + /* transmission parameters */ + auto_mode = false; + memset(buf, 0, 3); + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_AUTO: + auto_mode = true; + break; + case TRANSMISSION_MODE_2K: + break; + case TRANSMISSION_MODE_8K: + buf[0] |= (1 << 0); + break; + default: + dev_dbg(&client->dev, "invalid transmission_mode\n"); + auto_mode = true; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_AUTO: + auto_mode = true; + break; + case GUARD_INTERVAL_1_32: + break; + case GUARD_INTERVAL_1_16: + buf[0] |= (1 << 2); + break; + case GUARD_INTERVAL_1_8: + buf[0] |= (2 << 2); + break; + case GUARD_INTERVAL_1_4: + buf[0] |= (3 << 2); + break; + default: + dev_dbg(&client->dev, "invalid guard_interval\n"); + auto_mode = true; + } + + switch (c->hierarchy) { + case HIERARCHY_AUTO: + auto_mode = true; + break; + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + buf[0] |= (1 << 4); + break; + case HIERARCHY_2: + buf[0] |= (2 << 4); + break; + case HIERARCHY_4: + buf[0] |= (3 << 4); + break; + default: + dev_dbg(&client->dev, "invalid hierarchy\n"); + auto_mode = true; + } + + switch (c->modulation) { + case QAM_AUTO: + auto_mode = true; + break; + case QPSK: + break; + case QAM_16: + buf[1] |= (1 << 6); + break; + case QAM_64: + buf[1] |= (2 << 6); + break; + default: + dev_dbg(&client->dev, "invalid modulation\n"); + auto_mode = true; + } + + /* Use HP. How and which case we can switch to LP? */ + buf[1] |= (1 << 4); + + switch (c->code_rate_HP) { + case FEC_AUTO: + auto_mode = true; + break; + case FEC_1_2: + break; + case FEC_2_3: + buf[2] |= (1 << 0); + break; + case FEC_3_4: + buf[2] |= (2 << 0); + break; + case FEC_5_6: + buf[2] |= (3 << 0); + break; + case FEC_7_8: + buf[2] |= (4 << 0); + break; + default: + dev_dbg(&client->dev, "invalid code_rate_HP\n"); + auto_mode = true; + } + + switch (c->code_rate_LP) { + case FEC_AUTO: + auto_mode = true; + break; + case FEC_1_2: + break; + case FEC_2_3: + buf[2] |= (1 << 3); + break; + case FEC_3_4: + buf[2] |= (2 << 3); + break; + case FEC_5_6: + buf[2] |= (3 << 3); + break; + case FEC_7_8: + buf[2] |= (4 << 3); + break; + case FEC_NONE: + break; + default: + dev_dbg(&client->dev, "invalid code_rate_LP\n"); + auto_mode = true; + } + + switch (c->bandwidth_hz) { + case 6000000: + break; + case 7000000: + buf[1] |= (1 << 2); + break; + case 8000000: + buf[1] |= (2 << 2); + break; + default: + dev_dbg(&client->dev, "invalid bandwidth_hz\n"); + ret = -EINVAL; + goto err; + } + + ret = regmap_bulk_write(state->regmap, 0xd3c0, buf, 3); + if (ret) + goto err; + + if (auto_mode) { + /* clear easy mode flag */ + ret = regmap_write(state->regmap, 0xaefd, 0x00); + if (ret) + goto err; + + dev_dbg(&client->dev, "auto params\n"); + } else { + /* set easy mode flag */ + ret = regmap_write(state->regmap, 0xaefd, 0x01); + if (ret) + goto err; + + ret = regmap_write(state->regmap, 0xaefe, 0x00); + if (ret) + goto err; + + dev_dbg(&client->dev, "manual params\n"); + } + + /* Reset FSM */ + ret = regmap_write(state->regmap, 0xffff, 0x00); + if (ret) + goto err; + + state->bandwidth_hz = c->bandwidth_hz; + state->set_frontend_jiffies = jiffies; + state->first_tune = false; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret; + u8 buf[3]; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(state->regmap, 0xd3c0, buf, 3); + if (ret) + goto err; + + switch ((buf[1] >> 6) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[0] >> 0) & 3) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[0] >> 2) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[2] >> 3) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + } + + switch ((buf[1] >> 2) & 3) { + case 0: + c->bandwidth_hz = 6000000; + break; + case 1: + c->bandwidth_hz = 7000000; + break; + case 2: + c->bandwidth_hz = 8000000; + break; + } + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, stmp1; + unsigned int utmp, utmp1, utmp2, utmp3, utmp4; + u8 buf[7]; + + dev_dbg(&client->dev, "\n"); + + /* + * Return status from the cache if it is younger than 2000ms with the + * exception of last tune is done during 4000ms. + */ + if (time_is_after_jiffies(state->read_status_jiffies + msecs_to_jiffies(2000)) && + time_is_before_jiffies(state->set_frontend_jiffies + msecs_to_jiffies(4000))) { + *status = state->fe_status; + } else { + /* MPEG2 lock */ + ret = regmap_read(state->regmap, 0xd507, &utmp); + if (ret) + goto err; + + if ((utmp >> 6) & 0x01) { + utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else { + /* TPS lock */ + ret = regmap_read(state->regmap, 0xd330, &utmp); + if (ret) + goto err; + + if ((utmp >> 3) & 0x01) + utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else + utmp1 = 0; + } + + dev_dbg(&client->dev, "fe_status %02x\n", utmp1); + + state->read_status_jiffies = jiffies; + + state->fe_status = utmp1; + *status = utmp1; + } + + /* Signal strength */ + switch (state->strength_en) { + case 0: + /* Check if we support signal strength */ + ret = regmap_read(state->regmap, 0x9bee, &utmp); + if (ret) + goto err; + + if ((utmp >> 0) & 0x01) { + /* Read agc values for signal strength estimation */ + ret = regmap_read(state->regmap, 0x9bbd, &utmp1); + if (ret) + goto err; + ret = regmap_read(state->regmap, 0x9bd0, &utmp2); + if (ret) + goto err; + ret = regmap_read(state->regmap, 0x9be2, &utmp3); + if (ret) + goto err; + ret = regmap_read(state->regmap, 0x9be4, &utmp4); + if (ret) + goto err; + + state->rf_agc_50 = utmp1; + state->rf_agc_80 = utmp2; + state->if_agc_50 = utmp3; + state->if_agc_80 = utmp4; + dev_dbg(&client->dev, + "rf_agc_50 %u, rf_agc_80 %u, if_agc_50 %u, if_agc_80 %u\n", + utmp1, utmp2, utmp3, utmp4); + + state->strength_en = 1; + } else { + /* Signal strength is not supported */ + state->strength_en = 2; + break; + } + fallthrough; + case 1: + if (time_is_after_jiffies(state->strength_jiffies + msecs_to_jiffies(2000))) + break; + + /* Read value */ + ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2); + if (ret) + goto err; + + /* + * Construct line equation from tuner dependent -80/-50 dBm agc + * limits and use it to map current agc value to dBm estimate + */ + #define agc_gain (buf[0] + buf[1]) + #define agc_gain_50dbm (state->rf_agc_50 + state->if_agc_50) + #define agc_gain_80dbm (state->rf_agc_80 + state->if_agc_80) + stmp1 = 30000 * (agc_gain - agc_gain_80dbm) / + (agc_gain_50dbm - agc_gain_80dbm) - 80000; + + dev_dbg(&client->dev, + "strength %d, agc_gain %d, agc_gain_50dbm %d, agc_gain_80dbm %d\n", + stmp1, agc_gain, agc_gain_50dbm, agc_gain_80dbm); + + state->strength_jiffies = jiffies; + /* Convert [-90, -30] dBm to [0x0000, 0xffff] for dvbv3 */ + utmp1 = clamp(stmp1 + 90000, 0, 60000); + state->dvbv3_strength = div_u64((u64)utmp1 * 0xffff, 60000); + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = stmp1; + break; + default: + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; + } + + /* CNR */ + switch (state->fe_status & FE_HAS_VITERBI) { + case FE_HAS_VITERBI: + if (time_is_after_jiffies(state->cnr_jiffies + msecs_to_jiffies(2000))) + break; + + /* Check if cnr ready */ + ret = regmap_read(state->regmap, 0xd2e1, &utmp); + if (ret) + goto err; + + if (!((utmp >> 3) & 0x01)) { + dev_dbg(&client->dev, "cnr not ready\n"); + break; + } + + /* Read value */ + ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3); + if (ret) + goto err; + + utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0; + + /* Read current modulation */ + ret = regmap_read(state->regmap, 0xd3c1, &utmp); + if (ret) + goto err; + + switch ((utmp >> 6) & 3) { + case 0: + /* + * QPSK + * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6 + * value [653799, 1689999], 2.6 / 13 = 3355443 + */ + utmp1 = clamp(utmp1, 653799U, 1689999U); + utmp1 = ((u64)(intlog10(utmp1) + - intlog10(1690000 - utmp1) + + 3355443) * 13 * 1000) >> 24; + break; + case 1: + /* + * QAM-16 + * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7 + * value [371105, 827999], 15.7 / 6 = 43900382 + */ + utmp1 = clamp(utmp1, 371105U, 827999U); + utmp1 = ((u64)(intlog10(utmp1 - 370000) + - intlog10(828000 - utmp1) + + 43900382) * 6 * 1000) >> 24; + break; + case 2: + /* + * QAM-64 + * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8 + * value [193246, 424999], 23.8 / 8 = 49912218 + */ + utmp1 = clamp(utmp1, 193246U, 424999U); + utmp1 = ((u64)(intlog10(utmp1 - 193000) + - intlog10(425000 - utmp1) + + 49912218) * 8 * 1000) >> 24; + break; + default: + dev_dbg(&client->dev, "invalid modulation %u\n", + (utmp >> 6) & 3); + utmp1 = 0; + break; + } + + dev_dbg(&client->dev, "cnr %u\n", utmp1); + + state->cnr_jiffies = jiffies; + state->dvbv3_snr = utmp1 / 100; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = utmp1; + break; + default: + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; + } + + /* BER / PER */ + switch (state->fe_status & FE_HAS_SYNC) { + case FE_HAS_SYNC: + if (time_is_after_jiffies(state->ber_ucb_jiffies + msecs_to_jiffies(2000))) + break; + + /* Check if ber / ucb is ready */ + ret = regmap_read(state->regmap, 0xd391, &utmp); + if (ret) + goto err; + + if (!((utmp >> 4) & 0x01)) { + dev_dbg(&client->dev, "ber not ready\n"); + break; + } + + /* Read value */ + ret = regmap_bulk_read(state->regmap, 0xd385, buf, 7); + if (ret) + goto err; + + utmp1 = buf[4] << 16 | buf[3] << 8 | buf[2] << 0; + utmp2 = (buf[1] << 8 | buf[0] << 0) * 204 * 8; + utmp3 = buf[6] << 8 | buf[5] << 0; + utmp4 = buf[1] << 8 | buf[0] << 0; + + /* Use 10000 TS packets for measure */ + if (utmp4 != 10000) { + buf[0] = (10000 >> 0) & 0xff; + buf[1] = (10000 >> 8) & 0xff; + ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2); + if (ret) + goto err; + } + + /* Reset ber / ucb counter */ + ret = regmap_update_bits(state->regmap, 0xd391, 0x20, 0x20); + if (ret) + goto err; + + dev_dbg(&client->dev, "post_bit_error %u, post_bit_count %u\n", + utmp1, utmp2); + dev_dbg(&client->dev, "block_error %u, block_count %u\n", + utmp3, utmp4); + + state->ber_ucb_jiffies = jiffies; + state->dvbv3_ber = utmp1; + state->dvbv3_ucblocks += utmp3; + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += utmp1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp2; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += utmp3; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += utmp4; + break; + default: + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; + } + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct af9013_state *state = fe->demodulator_priv; + + *snr = state->dvbv3_snr; + + return 0; +} + +static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9013_state *state = fe->demodulator_priv; + + *strength = state->dvbv3_strength; + + return 0; +} + +static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct af9013_state *state = fe->demodulator_priv; + + *ber = state->dvbv3_ber; + + return 0; +} + +static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct af9013_state *state = fe->demodulator_priv; + + *ucblocks = state->dvbv3_ucblocks; + + return 0; +} + +static int af9013_init(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret, i, len; + unsigned int utmp; + u8 buf[3]; + const struct af9013_reg_mask_val *tab; + + dev_dbg(&client->dev, "\n"); + + /* ADC on */ + ret = regmap_update_bits(state->regmap, 0xd73a, 0x08, 0x00); + if (ret) + goto err; + + /* Clear reset */ + ret = regmap_update_bits(state->regmap, 0xd417, 0x02, 0x00); + if (ret) + goto err; + + /* Disable reset */ + ret = regmap_update_bits(state->regmap, 0xd417, 0x10, 0x00); + if (ret) + goto err; + + /* write API version to firmware */ + ret = regmap_bulk_write(state->regmap, 0x9bf2, state->api_version, 4); + if (ret) + goto err; + + /* program ADC control */ + switch (state->clk) { + case 28800000: /* 28.800 MHz */ + utmp = 0; + break; + case 20480000: /* 20.480 MHz */ + utmp = 1; + break; + case 28000000: /* 28.000 MHz */ + utmp = 2; + break; + case 25000000: /* 25.000 MHz */ + utmp = 3; + break; + default: + ret = -EINVAL; + goto err; + } + + ret = regmap_update_bits(state->regmap, 0x9bd2, 0x0f, utmp); + if (ret) + goto err; + + utmp = div_u64((u64)state->clk * 0x80000, 1000000); + buf[0] = (utmp >> 0) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 16) & 0xff; + ret = regmap_bulk_write(state->regmap, 0xd180, buf, 3); + if (ret) + goto err; + + /* Demod core settings */ + dev_dbg(&client->dev, "load demod core settings\n"); + len = ARRAY_SIZE(demod_init_tab); + tab = demod_init_tab; + for (i = 0; i < len; i++) { + ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask, + tab[i].val); + if (ret) + goto err; + } + + /* Demod tuner specific settings */ + dev_dbg(&client->dev, "load tuner specific settings\n"); + switch (state->tuner) { + case AF9013_TUNER_MXL5003D: + len = ARRAY_SIZE(tuner_init_tab_mxl5003d); + tab = tuner_init_tab_mxl5003d; + break; + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + case AF9013_TUNER_MXL5007T: + len = ARRAY_SIZE(tuner_init_tab_mxl5005); + tab = tuner_init_tab_mxl5005; + break; + case AF9013_TUNER_ENV77H11D5: + len = ARRAY_SIZE(tuner_init_tab_env77h11d5); + tab = tuner_init_tab_env77h11d5; + break; + case AF9013_TUNER_MT2060: + len = ARRAY_SIZE(tuner_init_tab_mt2060); + tab = tuner_init_tab_mt2060; + break; + case AF9013_TUNER_MC44S803: + len = ARRAY_SIZE(tuner_init_tab_mc44s803); + tab = tuner_init_tab_mc44s803; + break; + case AF9013_TUNER_QT1010: + case AF9013_TUNER_QT1010A: + len = ARRAY_SIZE(tuner_init_tab_qt1010); + tab = tuner_init_tab_qt1010; + break; + case AF9013_TUNER_MT2060_2: + len = ARRAY_SIZE(tuner_init_tab_mt2060_2); + tab = tuner_init_tab_mt2060_2; + break; + case AF9013_TUNER_TDA18271: + case AF9013_TUNER_TDA18218: + len = ARRAY_SIZE(tuner_init_tab_tda18271); + tab = tuner_init_tab_tda18271; + break; + case AF9013_TUNER_UNKNOWN: + default: + len = ARRAY_SIZE(tuner_init_tab_unknown); + tab = tuner_init_tab_unknown; + break; + } + + for (i = 0; i < len; i++) { + ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask, + tab[i].val); + if (ret) + goto err; + } + + /* TS interface */ + if (state->ts_output_pin == 7) + utmp = 1 << 3 | state->ts_mode << 1; + else + utmp = 0 << 3 | state->ts_mode << 1; + ret = regmap_update_bits(state->regmap, 0xd500, 0x0e, utmp); + if (ret) + goto err; + + /* enable lock led */ + ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x01); + if (ret) + goto err; + + state->first_tune = true; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_sleep(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret; + unsigned int utmp; + + dev_dbg(&client->dev, "\n"); + + /* disable lock led */ + ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x00); + if (ret) + goto err; + + /* Enable reset */ + ret = regmap_update_bits(state->regmap, 0xd417, 0x10, 0x10); + if (ret) + goto err; + + /* Start reset execution */ + ret = regmap_write(state->regmap, 0xaeff, 0x01); + if (ret) + goto err; + + /* Wait reset performs */ + ret = regmap_read_poll_timeout(state->regmap, 0xd417, utmp, + (utmp >> 1) & 0x01, 5000, 1000000); + if (ret) + goto err; + + if (!((utmp >> 1) & 0x01)) { + ret = -ETIMEDOUT; + goto err; + } + + /* ADC off */ + ret = regmap_update_bits(state->regmap, 0xd73a, 0x08, 0x08); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops af9013_ops; + +static int af9013_download_firmware(struct af9013_state *state) +{ + struct i2c_client *client = state->client; + int ret, i, len, rem; + unsigned int utmp; + u8 buf[4]; + u16 checksum = 0; + const struct firmware *firmware; + const char *name = AF9013_FIRMWARE; + + dev_dbg(&client->dev, "\n"); + + /* Check whether firmware is already running */ + ret = regmap_read(state->regmap, 0x98be, &utmp); + if (ret) + goto err; + + dev_dbg(&client->dev, "firmware status %02x\n", utmp); + + if (utmp == 0x0c) + return 0; + + dev_info(&client->dev, "found a '%s' in cold state, will try to load a firmware\n", + af9013_ops.info.name); + + /* Request the firmware, will block and timeout */ + ret = request_firmware(&firmware, name, &client->dev); + if (ret) { + dev_info(&client->dev, "firmware file '%s' not found %d\n", + name, ret); + goto err; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", + name); + + /* Write firmware checksum & size */ + for (i = 0; i < firmware->size; i++) + checksum += firmware->data[i]; + + buf[0] = (checksum >> 8) & 0xff; + buf[1] = (checksum >> 0) & 0xff; + buf[2] = (firmware->size >> 8) & 0xff; + buf[3] = (firmware->size >> 0) & 0xff; + ret = regmap_bulk_write(state->regmap, 0x50fc, buf, 4); + if (ret) + goto err_release_firmware; + + /* Download firmware */ + #define LEN_MAX 16 + for (rem = firmware->size; rem > 0; rem -= LEN_MAX) { + len = min(LEN_MAX, rem); + ret = regmap_bulk_write(state->regmap, + 0x5100 + firmware->size - rem, + &firmware->data[firmware->size - rem], + len); + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; + } + } + + release_firmware(firmware); + + /* Boot firmware */ + ret = regmap_write(state->regmap, 0xe205, 0x01); + if (ret) + goto err; + + /* Check firmware status. 0c=OK, 04=fail */ + ret = regmap_read_poll_timeout(state->regmap, 0x98be, utmp, + (utmp == 0x0c || utmp == 0x04), + 5000, 1000000); + if (ret) + goto err; + + dev_dbg(&client->dev, "firmware status %02x\n", utmp); + + if (utmp == 0x04) { + ret = -ENODEV; + dev_err(&client->dev, "firmware did not run\n"); + goto err; + } else if (utmp != 0x0c) { + ret = -ENODEV; + dev_err(&client->dev, "firmware boot timeout\n"); + goto err; + } + + dev_info(&client->dev, "found a '%s' in warm state\n", + af9013_ops.info.name); + + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops af9013_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Afatech AF9013", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 250 * kHz, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .init = af9013_init, + .sleep = af9013_sleep, + + .get_tune_settings = af9013_get_tune_settings, + .set_frontend = af9013_set_frontend, + .get_frontend = af9013_get_frontend, + + .read_status = af9013_read_status, + .read_snr = af9013_read_snr, + .read_signal_strength = af9013_read_signal_strength, + .read_ber = af9013_read_ber, + .read_ucblocks = af9013_read_ucblocks, +}; + +static int af9013_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret; + + dev_dbg(&client->dev, "onoff %d\n", onoff); + + ret = regmap_update_bits(state->regmap, 0xd503, 0x01, onoff); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, + int onoff) +{ + struct af9013_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + int ret; + u8 buf[2]; + + dev_dbg(&client->dev, "index %d, pid %04x, onoff %d\n", + index, pid, onoff); + + if (pid > 0x1fff) { + /* 0x2000 is kernel virtual pid for whole ts (all pids) */ + ret = 0; + goto err; + } + + buf[0] = (pid >> 0) & 0xff; + buf[1] = (pid >> 8) & 0xff; + ret = regmap_bulk_write(state->regmap, 0xd505, buf, 2); + if (ret) + goto err; + ret = regmap_write(state->regmap, 0xd504, onoff << 5 | index << 0); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client) +{ + struct af9013_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &state->fe; +} + +static struct i2c_adapter *af9013_get_i2c_adapter(struct i2c_client *client) +{ + struct af9013_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return state->muxc->adapter[0]; +} + +/* + * XXX: Hackish solution. We use virtual register, reg bit 16, to carry info + * about i2c adapter locking. Own locking is needed because i2c mux call has + * already locked i2c adapter. + */ +static int af9013_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct af9013_state *state = i2c_mux_priv(muxc); + struct i2c_client *client = state->client; + int ret; + + dev_dbg(&client->dev, "\n"); + + if (state->ts_mode == AF9013_TS_MODE_USB) + ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x08); + else + ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x04); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct af9013_state *state = i2c_mux_priv(muxc); + struct i2c_client *client = state->client; + int ret; + + dev_dbg(&client->dev, "\n"); + + if (state->ts_mode == AF9013_TS_MODE_USB) + ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x00); + else + ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x00); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +/* Own I2C access routines needed for regmap as chip uses extra command byte */ +static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg, + const u8 *val, int len, u8 lock) +{ + int ret; + u8 buf[21]; + struct i2c_msg msg[1] = { + { + .addr = client->addr, + .flags = 0, + .len = 3 + len, + .buf = buf, + } + }; + + if (3 + len > sizeof(buf)) { + ret = -EINVAL; + goto err; + } + + buf[0] = (reg >> 8) & 0xff; + buf[1] = (reg >> 0) & 0xff; + buf[2] = cmd; + memcpy(&buf[3], val, len); + + if (lock) + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = __i2c_transfer(client->adapter, msg, 1); + if (lock) + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret < 0) { + goto err; + } else if (ret != 1) { + ret = -EREMOTEIO; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg, + u8 *val, int len, u8 lock) +{ + int ret; + u8 buf[3]; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 3, + .buf = buf, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + buf[0] = (reg >> 8) & 0xff; + buf[1] = (reg >> 0) & 0xff; + buf[2] = cmd; + + if (lock) + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = __i2c_transfer(client->adapter, msg, 2); + if (lock) + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret < 0) { + goto err; + } else if (ret != 2) { + ret = -EREMOTEIO; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_regmap_write(void *context, const void *data, size_t count) +{ + struct i2c_client *client = context; + struct af9013_state *state = i2c_get_clientdata(client); + int ret, i; + u8 cmd; + u8 lock = !((u8 *)data)[0]; + u16 reg = ((u8 *)data)[1] << 8 | ((u8 *)data)[2] << 0; + u8 *val = &((u8 *)data)[3]; + const unsigned int len = count - 3; + + if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) { + cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|1 << 0; + ret = af9013_wregs(client, cmd, reg, val, len, lock); + if (ret) + goto err; + } else if (reg >= 0x5100 && reg < 0x8fff) { + /* Firmware download */ + cmd = 1 << 7|1 << 6|(len - 1) << 2|1 << 1|1 << 0; + ret = af9013_wregs(client, cmd, reg, val, len, lock); + if (ret) + goto err; + } else { + cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|1 << 0; + for (i = 0; i < len; i++) { + ret = af9013_wregs(client, cmd, reg + i, val + i, 1, + lock); + if (ret) + goto err; + } + } + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct i2c_client *client = context; + struct af9013_state *state = i2c_get_clientdata(client); + int ret, i; + u8 cmd; + u8 lock = !((u8 *)reg_buf)[0]; + u16 reg = ((u8 *)reg_buf)[1] << 8 | ((u8 *)reg_buf)[2] << 0; + u8 *val = &((u8 *)val_buf)[0]; + const unsigned int len = val_size; + + if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) { + cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|0 << 0; + ret = af9013_rregs(client, cmd, reg, val_buf, len, lock); + if (ret) + goto err; + } else { + cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|0 << 0; + for (i = 0; i < len; i++) { + ret = af9013_rregs(client, cmd, reg + i, val + i, 1, + lock); + if (ret) + goto err; + } + } + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static int af9013_probe(struct i2c_client *client) +{ + struct af9013_state *state; + struct af9013_platform_data *pdata = client->dev.platform_data; + struct dtv_frontend_properties *c; + int ret, i; + u8 firmware_version[4]; + static const struct regmap_bus regmap_bus = { + .read = af9013_regmap_read, + .write = af9013_regmap_write, + }; + static const struct regmap_config regmap_config = { + /* Actual reg is 16 bits, see i2c adapter lock */ + .reg_bits = 24, + .val_bits = 8, + }; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto err; + } + + dev_dbg(&client->dev, "\n"); + + /* Setup the state */ + state->client = client; + i2c_set_clientdata(client, state); + state->clk = pdata->clk; + state->tuner = pdata->tuner; + state->if_frequency = pdata->if_frequency; + state->ts_mode = pdata->ts_mode; + state->ts_output_pin = pdata->ts_output_pin; + state->spec_inv = pdata->spec_inv; + memcpy(&state->api_version, pdata->api_version, sizeof(state->api_version)); + memcpy(&state->gpio, pdata->gpio, sizeof(state->gpio)); + state->regmap = regmap_init(&client->dev, ®map_bus, client, + ®map_config); + if (IS_ERR(state->regmap)) { + ret = PTR_ERR(state->regmap); + goto err_kfree; + } + /* Create mux i2c adapter */ + state->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0, + af9013_select, af9013_deselect); + if (!state->muxc) { + ret = -ENOMEM; + goto err_regmap_exit; + } + state->muxc->priv = state; + ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0); + if (ret) + goto err_regmap_exit; + + /* Download firmware */ + if (state->ts_mode != AF9013_TS_MODE_USB) { + ret = af9013_download_firmware(state); + if (ret) + goto err_i2c_mux_del_adapters; + } + + /* Firmware version */ + ret = regmap_bulk_read(state->regmap, 0x5103, firmware_version, + sizeof(firmware_version)); + if (ret) + goto err_i2c_mux_del_adapters; + + /* Set GPIOs */ + for (i = 0; i < sizeof(state->gpio); i++) { + ret = af9013_set_gpio(state, i, state->gpio[i]); + if (ret) + goto err_i2c_mux_del_adapters; + } + + /* Create dvb frontend */ + memcpy(&state->fe.ops, &af9013_ops, sizeof(state->fe.ops)); + state->fe.demodulator_priv = state; + + /* Setup callbacks */ + pdata->get_dvb_frontend = af9013_get_dvb_frontend; + pdata->get_i2c_adapter = af9013_get_i2c_adapter; + pdata->pid_filter = af9013_pid_filter; + pdata->pid_filter_ctrl = af9013_pid_filter_ctrl; + + /* Init stats to indicate which stats are supported */ + c = &state->fe.dtv_property_cache; + c->strength.len = 1; + c->cnr.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; + + dev_info(&client->dev, "Afatech AF9013 successfully attached\n"); + dev_info(&client->dev, "firmware version: %d.%d.%d.%d\n", + firmware_version[0], firmware_version[1], + firmware_version[2], firmware_version[3]); + return 0; +err_i2c_mux_del_adapters: + i2c_mux_del_adapters(state->muxc); +err_regmap_exit: + regmap_exit(state->regmap); +err_kfree: + kfree(state); +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return ret; +} + +static void af9013_remove(struct i2c_client *client) +{ + struct af9013_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + i2c_mux_del_adapters(state->muxc); + + regmap_exit(state->regmap); + + kfree(state); +} + +static const struct i2c_device_id af9013_id_table[] = { + {"af9013", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, af9013_id_table); + +static struct i2c_driver af9013_driver = { + .driver = { + .name = "af9013", + .suppress_bind_attrs = true, + }, + .probe = af9013_probe, + .remove = af9013_remove, + .id_table = af9013_id_table, +}; + +module_i2c_driver(af9013_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(AF9013_FIRMWARE); diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h new file mode 100644 index 0000000000..bbf1ccee72 --- /dev/null +++ b/drivers/media/dvb-frontends/af9013.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Afatech AF9013 demodulator driver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + */ + +#ifndef AF9013_H +#define AF9013_H + +#include <linux/dvb/frontend.h> + +/* + * I2C address: 0x1c, 0x1d + */ + +/** + * struct af9013_platform_data - Platform data for the af9013 driver + * @clk: Clock frequency. + * @tuner: Used tuner model. + * @if_frequency: IF frequency. + * @ts_mode: TS mode. + * @ts_output_pin: TS output pin. + * @spec_inv: Input spectrum inverted. + * @api_version: Firmware API version. + * @gpio: GPIOs. + * @get_dvb_frontend: Get DVB frontend callback. + * @get_i2c_adapter: Get I2C adapter. + * @pid_filter_ctrl: Control PID filter. + * @pid_filter: Set PID to PID filter. + */ +struct af9013_platform_data { + /* + * 20480000, 25000000, 28000000, 28800000 + */ + u32 clk; +#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */ +#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */ +#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */ +#define AF9013_TUNER_ENV77H11D5 129 /* Panasonic */ +#define AF9013_TUNER_MT2060 130 /* Microtune */ +#define AF9013_TUNER_MC44S803 133 /* Freescale */ +#define AF9013_TUNER_QT1010 134 /* Quantek */ +#define AF9013_TUNER_UNKNOWN 140 /* for can tuners ? */ +#define AF9013_TUNER_MT2060_2 147 /* Microtune */ +#define AF9013_TUNER_TDA18271 156 /* NXP */ +#define AF9013_TUNER_QT1010A 162 /* Quantek */ +#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */ +#define AF9013_TUNER_TDA18218 179 /* NXP */ + u8 tuner; + u32 if_frequency; +#define AF9013_TS_MODE_USB 0 +#define AF9013_TS_MODE_PARALLEL 1 +#define AF9013_TS_MODE_SERIAL 2 + u8 ts_mode; + u8 ts_output_pin; + bool spec_inv; + u8 api_version[4]; +#define AF9013_GPIO_ON (1 << 0) +#define AF9013_GPIO_EN (1 << 1) +#define AF9013_GPIO_O (1 << 2) +#define AF9013_GPIO_I (1 << 3) +#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN) +#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) +#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN) +#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) + u8 gpio[4]; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + int (*pid_filter_ctrl)(struct dvb_frontend *, int); + int (*pid_filter)(struct dvb_frontend *, u8, u16, int); +}; + +/* + * AF9013/5 GPIOs (mostly guessed) + * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices + * demod#1-gpio#1 - xtal setting (?) + * demod#1-gpio#3 - tuner#1 + * demod#2-gpio#0 - tuner#2 + * demod#2-gpio#1 - xtal setting (?) + */ + +#endif /* AF9013_H */ diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h new file mode 100644 index 0000000000..bba7a9693a --- /dev/null +++ b/drivers/media/dvb-frontends/af9013_priv.h @@ -0,0 +1,851 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Afatech AF9013 demodulator driver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * Thanks to Afatech who kindly provided information. + */ + +#ifndef AF9013_PRIV_H +#define AF9013_PRIV_H + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "af9013.h" +#include <linux/firmware.h> +#include <linux/i2c-mux.h> +#include <linux/math64.h> +#include <linux/regmap.h> + +#define AF9013_FIRMWARE "dvb-fe-af9013.fw" + +struct af9013_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +struct af9013_coeff { + u32 clock; + u32 bandwidth_hz; + u8 val[24]; +}; + +/* pre-calculated coeff lookup table */ +static const struct af9013_coeff coeff_lut[] = { + /* 28.800 MHz */ + { 28800000, 8000000, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14, + 0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a, + 0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } }, + { 28800000, 7000000, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71, + 0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38, + 0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } }, + { 28800000, 6000000, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf, + 0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7, + 0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } }, + /* 20.480 MHz */ + { 20480000, 8000000, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24, + 0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92, + 0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } }, + { 20480000, 7000000, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40, + 0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00, + 0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } }, + { 20480000, 6000000, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b, + 0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d, + 0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } }, + /* 28.000 MHz */ + { 28000000, 8000000, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39, + 0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f, + 0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } }, + { 28000000, 7000000, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92, + 0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49, + 0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } }, + { 28000000, 6000000, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb, + 0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63, + 0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } }, + /* 25.000 MHz */ + { 25000000, 8000000, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9, + 0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e, + 0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } }, + { 25000000, 7000000, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e, + 0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7, + 0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } }, + { 25000000, 6000000, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63, + 0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f, + 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } }, +}; + +/* + * Afatech AF9013 demod init + */ +static const struct af9013_reg_mask_val demod_init_tab[] = { + {0xd73a, 0xff, 0xa1}, + {0xd73b, 0xff, 0x1f}, + {0xd73c, 0xf0, 0xa0}, + {0xd732, 0x08, 0x00}, + {0xd731, 0x30, 0x30}, + {0xd73d, 0x80, 0x80}, + {0xd740, 0x01, 0x00}, + {0xd740, 0x02, 0x00}, + {0xd740, 0x04, 0x00}, + {0xd740, 0x08, 0x08}, + {0xd3c1, 0x10, 0x10}, + {0x9124, 0xff, 0x58}, + {0x9125, 0x03, 0x02}, + {0xd3a2, 0xff, 0x00}, + {0xd3a3, 0xff, 0x04}, + {0xd305, 0xff, 0x32}, + {0xd306, 0xff, 0x10}, + {0xd304, 0xff, 0x04}, + {0x9112, 0x01, 0x01}, + {0x911d, 0x01, 0x01}, + {0x911a, 0x01, 0x01}, + {0x911b, 0x01, 0x01}, + {0x9bce, 0x0f, 0x02}, + {0x9116, 0x01, 0x01}, + {0x9122, 0xff, 0xd0}, + {0xd2e0, 0xff, 0xd0}, + {0xd2e9, 0x0f, 0x0d}, + {0xd38c, 0xff, 0xfc}, + {0xd38d, 0xff, 0x00}, + {0xd38e, 0xff, 0x7e}, + {0xd38f, 0xff, 0x00}, + {0xd390, 0xff, 0x2f}, + {0xd145, 0x10, 0x10}, + {0xd1a9, 0x10, 0x10}, + {0xd158, 0xe0, 0x20}, + {0xd159, 0x3f, 0x06}, + {0xd167, 0xff, 0x00}, + {0xd168, 0x0f, 0x07}, + {0xd1c3, 0xe0, 0x00}, + {0xd1c4, 0x3f, 0x00}, + {0xd1c5, 0x7f, 0x10}, + {0xd1c6, 0x07, 0x02}, + {0xd080, 0x7c, 0x0c}, + {0xd081, 0xf0, 0x90}, + {0xd098, 0xf0, 0xf0}, + {0xd098, 0x0f, 0x03}, + {0xdbc0, 0x10, 0x10}, + {0xdbc7, 0xff, 0x08}, + {0xdbc8, 0xf0, 0x00}, + {0xdbc9, 0x1f, 0x01}, + {0xd280, 0xff, 0xe0}, + {0xd281, 0xff, 0xff}, + {0xd282, 0xff, 0xff}, + {0xd283, 0xff, 0xc3}, + {0xd284, 0xff, 0xff}, + {0xd285, 0x0f, 0x01}, + {0xd0f0, 0x7f, 0x1a}, + {0xd0f1, 0x10, 0x10}, + {0xd0f2, 0xff, 0x0c}, + {0xd101, 0xe0, 0xc0}, + {0xd103, 0x0f, 0x08}, + {0xd0f8, 0x7f, 0x20}, + {0xd111, 0x20, 0x00}, + {0xd111, 0x40, 0x00}, + {0x910b, 0xff, 0x0a}, + {0x9115, 0xff, 0x02}, + {0x910c, 0xff, 0x02}, + {0x910d, 0xff, 0x08}, + {0x910e, 0xff, 0x0a}, + {0x9bf6, 0xff, 0x06}, + {0x9bf8, 0xff, 0x02}, + {0x9bf7, 0xff, 0x05}, + {0x9bf9, 0xff, 0x0f}, + {0x9bfc, 0xff, 0x13}, + {0x9bd3, 0xff, 0xff}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, +}; + +/* + * Panasonic ENV77H11D5 tuner init + * AF9013_TUNER_ENV77H11D5 0x81 + */ +static const struct af9013_reg_mask_val tuner_init_tab_env77h11d5[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x03}, + {0x9bbe, 0xff, 0x01}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0xd015, 0xff, 0x50}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0xdf}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x44}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0xeb}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xf4}, + {0xd00b, 0x03, 0x01}, + {0x9bba, 0xff, 0xf9}, + {0x9bc3, 0xff, 0xdf}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0xeb}, + {0x9bc6, 0xff, 0x02}, + {0x9bc9, 0xff, 0x52}, + {0xd011, 0xff, 0x3c}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xf7}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x0b}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x4d}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, +}; + +/* + * Microtune MT2060 tuner init + * AF9013_TUNER_MT2060 0x82 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mt2060[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x07}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0x9bbe, 0x01, 0x00}, + {0x9bcc, 0x01, 0x00}, + {0x9bb9, 0xff, 0x75}, + {0x9bcd, 0xff, 0x24}, + {0x9bff, 0xff, 0x30}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x32}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x36}, + {0xd00d, 0x03, 0x03}, + {0xd00a, 0xff, 0x35}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x07}, + {0x9bc8, 0xff, 0x90}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x36}, + {0x9bc6, 0xff, 0x03}, + {0x9bba, 0xff, 0xc9}, + {0x9bc9, 0xff, 0x79}, + {0xd011, 0xff, 0x10}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0x45}, + {0xd014, 0x03, 0x03}, + {0xd040, 0xff, 0x98}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0xcf}, + {0xd043, 0x03, 0x03}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0xcc}, + {0x9be4, 0xff, 0xa0}, + {0x9bbd, 0xff, 0x8e}, + {0x9be2, 0xff, 0x4d}, + {0x9bee, 0x01, 0x01}, +}; + +/* + * Microtune MT2060 tuner init + * AF9013_TUNER_MT2060_2 0x93 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mt2060_2[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x06}, + {0x9bbe, 0xff, 0x01}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x32}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x36}, + {0xd00d, 0x03, 0x03}, + {0xd00a, 0xff, 0x35}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x07}, + {0x9bc8, 0xff, 0x90}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x36}, + {0x9bc6, 0xff, 0x03}, + {0x9bba, 0xff, 0xc9}, + {0x9bc9, 0xff, 0x79}, + {0xd011, 0xff, 0x10}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0x45}, + {0xd014, 0x03, 0x03}, + {0xd040, 0xff, 0x98}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0xcf}, + {0xd043, 0x03, 0x03}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0xff, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x96}, + {0xd054, 0xff, 0x46}, + {0xd045, 0x80, 0x00}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, +}; + +/* + * MaxLinear MXL5003 tuner init + * AF9013_TUNER_MXL5003D 0x03 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mxl5003d[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x09}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0x9bfc, 0xff, 0x0f}, + {0x9bf6, 0xff, 0x01}, + {0x9bbe, 0x01, 0x01}, + {0xd015, 0xff, 0x33}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x40}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x6c}, + {0xd007, 0x03, 0x00}, + {0xd00c, 0xff, 0x3d}, + {0xd00d, 0x03, 0x00}, + {0xd00a, 0xff, 0x45}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x07}, + {0x9bc8, 0xff, 0x52}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x3d}, + {0x9bc6, 0xff, 0x00}, + {0x9bba, 0xff, 0xa2}, + {0x9bc9, 0xff, 0xa0}, + {0xd011, 0xff, 0x56}, + {0xd012, 0x03, 0x00}, + {0xd013, 0xff, 0x50}, + {0xd014, 0x03, 0x00}, + {0xd040, 0xff, 0x56}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0x50}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0xff, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, +}; + +/* + * MaxLinear MXL5005S & MXL5007T tuner init + * AF9013_TUNER_MXL5005D 0x0d + * AF9013_TUNER_MXL5005R 0x1e + * AF9013_TUNER_MXL5007T 0xb1 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mxl5005[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x07}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x28}, + {0x9bff, 0xff, 0x24}, + {0xd015, 0xff, 0x40}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x40}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x73}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0xfa}, + {0xd00d, 0x03, 0x01}, + {0xd00a, 0xff, 0xff}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x23}, + {0x9bc8, 0xff, 0x55}, + {0x9bc3, 0xff, 0x01}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0xfa}, + {0x9bc6, 0xff, 0x01}, + {0x9bba, 0xff, 0xff}, + {0x9bc9, 0xff, 0xff}, + {0x9bd3, 0xff, 0x95}, + {0xd011, 0xff, 0x70}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xfb}, + {0xd014, 0x03, 0x01}, + {0xd040, 0xff, 0x70}, + {0xd041, 0x03, 0x01}, + {0xd042, 0xff, 0xfb}, + {0xd043, 0x03, 0x01}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0x93}, + {0x9be4, 0xff, 0xfe}, + {0x9bbd, 0xff, 0x63}, + {0x9be2, 0xff, 0xfe}, + {0x9bee, 0x01, 0x01}, +}; + +/* + * Quantek QT1010 tuner init + * AF9013_TUNER_QT1010 0x86 + * AF9013_TUNER_QT1010A 0xa2 + */ +static const struct af9013_reg_mask_val tuner_init_tab_qt1010[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x09}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x28}, + {0x9bff, 0xff, 0x20}, + {0xd008, 0xff, 0x0f}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x99}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x0f}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0x50}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x00}, + {0x9bc8, 0xff, 0x00}, + {0x9bc3, 0xff, 0x0f}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x0f}, + {0x9bc6, 0xff, 0x02}, + {0x9bba, 0xff, 0xc5}, + {0x9bc9, 0xff, 0xff}, + {0xd011, 0xff, 0x58}, + {0xd012, 0x03, 0x02}, + {0xd013, 0xff, 0x89}, + {0xd014, 0x03, 0x01}, + {0xd040, 0xff, 0x58}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x89}, + {0xd043, 0x03, 0x01}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0xcd}, + {0x9be4, 0xff, 0xbb}, + {0x9bbd, 0xff, 0x93}, + {0x9be2, 0xff, 0x80}, + {0x9bee, 0x01, 0x01}, +}; + +/* + * Freescale MC44S803 tuner init + * AF9013_TUNER_MC44S803 0x85 + */ +static const struct af9013_reg_mask_val tuner_init_tab_mc44s803[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x06}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x00}, + {0x9be3, 0xff, 0x00}, + {0x9bf6, 0xff, 0x01}, + {0x9bf8, 0xff, 0x02}, + {0x9bf9, 0xff, 0x02}, + {0x9bfc, 0xff, 0x1f}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x24}, + {0x9bff, 0xff, 0x24}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0x01}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x7b}, + {0xd007, 0x03, 0x00}, + {0xd00c, 0xff, 0x7c}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xfe}, + {0xd00b, 0x03, 0x01}, + {0x9bc7, 0xff, 0x08}, + {0x9bc8, 0xff, 0x9a}, + {0x9bc3, 0xff, 0x01}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x7c}, + {0x9bc6, 0xff, 0x02}, + {0x9bba, 0xff, 0xfc}, + {0x9bc9, 0xff, 0xaa}, + {0xd011, 0xff, 0x6b}, + {0xd012, 0x03, 0x00}, + {0xd013, 0xff, 0x88}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x6b}, + {0xd041, 0x03, 0x00}, + {0xd042, 0xff, 0x7c}, + {0xd043, 0x03, 0x02}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0x9e}, + {0x9be4, 0xff, 0xff}, + {0x9bbd, 0xff, 0x9e}, + {0x9be2, 0xff, 0x25}, + {0x9bee, 0x01, 0x01}, + {0xd73b, 0x08, 0x00}, +}; + +/* + * Unknown, probably for tin can tuner, tuner init + * AF9013_TUNER_UNKNOWN 0x8c + */ +static const struct af9013_reg_mask_val tuner_init_tab_unknown[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x02}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0xd1a0, 0x02, 0x00}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x18}, + {0x9bff, 0xff, 0x2c}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0xdf}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x44}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x00}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xf6}, + {0xd00b, 0x03, 0x01}, + {0x9bba, 0xff, 0xf9}, + {0x9bc8, 0xff, 0xaa}, + {0x9bc3, 0xff, 0xdf}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x00}, + {0x9bc6, 0xff, 0x02}, + {0x9bc9, 0xff, 0xf0}, + {0xd011, 0xff, 0x3c}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xf7}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x0b}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x4d}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, +}; + +/* + * NXP TDA18271 & TDA18218 tuner init + * AF9013_TUNER_TDA18271 0x9c + * AF9013_TUNER_TDA18218 0xb3 + */ +static const struct af9013_reg_mask_val tuner_init_tab_tda18271[] = { + {0x9bd5, 0xff, 0x01}, + {0x9bd6, 0xff, 0x04}, + {0xd1a0, 0x02, 0x02}, + {0xd000, 0x01, 0x01}, + {0xd000, 0x02, 0x00}, + {0xd001, 0x02, 0x02}, + {0xd001, 0x01, 0x00}, + {0xd001, 0x20, 0x00}, + {0xd002, 0x1f, 0x19}, + {0xd003, 0x1f, 0x1a}, + {0xd004, 0x1f, 0x19}, + {0xd005, 0x1f, 0x1a}, + {0xd00e, 0x1f, 0x10}, + {0xd00f, 0x07, 0x04}, + {0xd00f, 0x38, 0x28}, + {0xd010, 0x07, 0x04}, + {0xd010, 0x38, 0x28}, + {0xd016, 0xf0, 0x30}, + {0xd01f, 0x3f, 0x0a}, + {0xd020, 0x3f, 0x0a}, + {0x9bda, 0xff, 0x01}, + {0x9be3, 0xff, 0x01}, + {0xd1a0, 0x02, 0x00}, + {0x9bbe, 0x01, 0x01}, + {0x9bcc, 0x01, 0x01}, + {0x9bb9, 0xff, 0x00}, + {0x9bcd, 0xff, 0x18}, + {0x9bff, 0xff, 0x2c}, + {0xd015, 0xff, 0x46}, + {0xd016, 0x01, 0x00}, + {0xd044, 0xff, 0x46}, + {0xd045, 0x01, 0x00}, + {0xd008, 0xff, 0xdf}, + {0xd009, 0x03, 0x02}, + {0xd006, 0xff, 0x44}, + {0xd007, 0x03, 0x01}, + {0xd00c, 0xff, 0x00}, + {0xd00d, 0x03, 0x02}, + {0xd00a, 0xff, 0xf6}, + {0xd00b, 0x03, 0x01}, + {0x9bba, 0xff, 0xf9}, + {0x9bc8, 0xff, 0xaa}, + {0x9bc3, 0xff, 0xdf}, + {0x9bc4, 0xff, 0x02}, + {0x9bc5, 0xff, 0x00}, + {0x9bc6, 0xff, 0x02}, + {0x9bc9, 0xff, 0xf0}, + {0xd011, 0xff, 0x3c}, + {0xd012, 0x03, 0x01}, + {0xd013, 0xff, 0xf7}, + {0xd014, 0x03, 0x02}, + {0xd040, 0xff, 0x0b}, + {0xd041, 0x03, 0x02}, + {0xd042, 0xff, 0x4d}, + {0xd043, 0x03, 0x00}, + {0xd045, 0x02, 0x00}, + {0x9bcf, 0x01, 0x01}, + {0xd045, 0x04, 0x04}, + {0xd04f, 0xff, 0x9a}, + {0xd050, 0x01, 0x01}, + {0xd051, 0xff, 0x5a}, + {0xd052, 0x01, 0x01}, + {0xd053, 0xff, 0x50}, + {0xd054, 0xff, 0x46}, + {0x9bd7, 0xff, 0x0a}, + {0x9bd8, 0xff, 0x14}, + {0x9bd9, 0xff, 0x08}, + {0x9bd0, 0xff, 0xa8}, + {0x9be4, 0xff, 0x7f}, + {0x9bbd, 0xff, 0xa8}, + {0x9be2, 0xff, 0x20}, + {0x9bee, 0x01, 0x01}, +}; + +#endif /* AF9013_PRIV_H */ diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c new file mode 100644 index 0000000000..49b7b04a78 --- /dev/null +++ b/drivers/media/dvb-frontends/af9033.c @@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + */ + +#include "af9033_priv.h" + +struct af9033_dev { + struct i2c_client *client; + struct regmap *regmap; + struct dvb_frontend fe; + struct af9033_config cfg; + bool is_af9035; + bool is_it9135; + + u32 bandwidth_hz; + bool ts_mode_parallel; + bool ts_mode_serial; + + enum fe_status fe_status; + u64 post_bit_error_prev; /* for old read_ber we return (curr - prev) */ + u64 post_bit_error; + u64 post_bit_count; + u64 error_block_count; + u64 total_block_count; +}; + +/* Write reg val table using reg addr auto increment */ +static int af9033_wr_reg_val_tab(struct af9033_dev *dev, + const struct reg_val *tab, int tab_len) +{ + struct i2c_client *client = dev->client; +#define MAX_TAB_LEN 212 + int ret, i, j; + u8 buf[1 + MAX_TAB_LEN]; + + dev_dbg(&client->dev, "tab_len=%d\n", tab_len); + + if (tab_len > sizeof(buf)) { + dev_warn(&client->dev, "tab len %d is too big\n", tab_len); + return -EINVAL; + } + + for (i = 0, j = 0; i < tab_len; i++) { + buf[j] = tab[i].val; + + if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1) { + ret = regmap_bulk_write(dev->regmap, tab[i].reg - j, + buf, j + 1); + if (ret) + goto err; + + j = 0; + } else { + j++; + } + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_init(struct dvb_frontend *fe) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, len; + unsigned int utmp; + const struct reg_val *init; + u8 buf[4]; + struct reg_val_mask tab[] = { + { 0x80fb24, 0x00, 0x08 }, + { 0x80004c, 0x00, 0xff }, + { 0x00f641, dev->cfg.tuner, 0xff }, + { 0x80f5ca, 0x01, 0x01 }, + { 0x80f715, 0x01, 0x01 }, + { 0x00f41f, 0x04, 0x04 }, + { 0x00f41a, 0x01, 0x01 }, + { 0x80f731, 0x00, 0x01 }, + { 0x00d91e, 0x00, 0x01 }, + { 0x00d919, 0x00, 0x01 }, + { 0x80f732, 0x00, 0x01 }, + { 0x00d91f, 0x00, 0x01 }, + { 0x00d91a, 0x00, 0x01 }, + { 0x80f730, 0x00, 0x01 }, + { 0x80f778, 0x00, 0xff }, + { 0x80f73c, 0x01, 0x01 }, + { 0x80f776, 0x00, 0x01 }, + { 0x00d8fd, 0x01, 0xff }, + { 0x00d830, 0x01, 0xff }, + { 0x00d831, 0x00, 0xff }, + { 0x00d832, 0x00, 0xff }, + { 0x80f985, dev->ts_mode_serial, 0x01 }, + { 0x80f986, dev->ts_mode_parallel, 0x01 }, + { 0x00d827, 0x00, 0xff }, + { 0x00d829, 0x00, 0xff }, + { 0x800045, dev->cfg.adc_multiplier, 0xff }, + }; + + dev_dbg(&client->dev, "\n"); + + /* Main clk control */ + utmp = div_u64((u64)dev->cfg.clock * 0x80000, 1000000); + buf[0] = (utmp >> 0) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 16) & 0xff; + buf[3] = (utmp >> 24) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x800025, buf, 4); + if (ret) + goto err; + + dev_dbg(&client->dev, "clk=%u clk_cw=%08x\n", dev->cfg.clock, utmp); + + /* ADC clk control */ + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == dev->cfg.clock) + break; + } + if (i == ARRAY_SIZE(clock_adc_lut)) { + dev_err(&client->dev, "Couldn't find ADC config for clock %d\n", + dev->cfg.clock); + ret = -ENODEV; + goto err; + } + + utmp = div_u64((u64)clock_adc_lut[i].adc * 0x80000, 1000000); + buf[0] = (utmp >> 0) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 16) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x80f1cd, buf, 3); + if (ret) + goto err; + + dev_dbg(&client->dev, "adc=%u adc_cw=%06x\n", + clock_adc_lut[i].adc, utmp); + + /* Config register table */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = regmap_update_bits(dev->regmap, tab[i].reg, tab[i].mask, + tab[i].val); + if (ret) + goto err; + } + + /* Demod clk output */ + if (dev->cfg.dyn0_clk) { + ret = regmap_write(dev->regmap, 0x80fba8, 0x00); + if (ret) + goto err; + } + + /* TS interface */ + if (dev->cfg.ts_mode == AF9033_TS_MODE_USB) { + ret = regmap_update_bits(dev->regmap, 0x80f9a5, 0x01, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x80f9b5, 0x01, 0x01); + if (ret) + goto err; + } else { + ret = regmap_update_bits(dev->regmap, 0x80f990, 0x01, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x80f9b5, 0x01, 0x00); + if (ret) + goto err; + } + + /* Demod core settings */ + dev_dbg(&client->dev, "load ofsm settings\n"); + switch (dev->cfg.tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + len = ARRAY_SIZE(ofsm_init_it9135_v1); + init = ofsm_init_it9135_v1; + break; + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + len = ARRAY_SIZE(ofsm_init_it9135_v2); + init = ofsm_init_it9135_v2; + break; + default: + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + break; + } + + ret = af9033_wr_reg_val_tab(dev, init, len); + if (ret) + goto err; + + /* Demod tuner specific settings */ + dev_dbg(&client->dev, "load tuner specific settings\n"); + switch (dev->cfg.tuner) { + case AF9033_TUNER_TUA9001: + len = ARRAY_SIZE(tuner_init_tua9001); + init = tuner_init_tua9001; + break; + case AF9033_TUNER_FC0011: + len = ARRAY_SIZE(tuner_init_fc0011); + init = tuner_init_fc0011; + break; + case AF9033_TUNER_MXL5007T: + len = ARRAY_SIZE(tuner_init_mxl5007t); + init = tuner_init_mxl5007t; + break; + case AF9033_TUNER_TDA18218: + len = ARRAY_SIZE(tuner_init_tda18218); + init = tuner_init_tda18218; + break; + case AF9033_TUNER_FC2580: + len = ARRAY_SIZE(tuner_init_fc2580); + init = tuner_init_fc2580; + break; + case AF9033_TUNER_FC0012: + len = ARRAY_SIZE(tuner_init_fc0012); + init = tuner_init_fc0012; + break; + case AF9033_TUNER_IT9135_38: + len = ARRAY_SIZE(tuner_init_it9135_38); + init = tuner_init_it9135_38; + break; + case AF9033_TUNER_IT9135_51: + len = ARRAY_SIZE(tuner_init_it9135_51); + init = tuner_init_it9135_51; + break; + case AF9033_TUNER_IT9135_52: + len = ARRAY_SIZE(tuner_init_it9135_52); + init = tuner_init_it9135_52; + break; + case AF9033_TUNER_IT9135_60: + len = ARRAY_SIZE(tuner_init_it9135_60); + init = tuner_init_it9135_60; + break; + case AF9033_TUNER_IT9135_61: + len = ARRAY_SIZE(tuner_init_it9135_61); + init = tuner_init_it9135_61; + break; + case AF9033_TUNER_IT9135_62: + len = ARRAY_SIZE(tuner_init_it9135_62); + init = tuner_init_it9135_62; + break; + default: + dev_dbg(&client->dev, "unsupported tuner ID=%d\n", + dev->cfg.tuner); + ret = -ENODEV; + goto err; + } + + ret = af9033_wr_reg_val_tab(dev, init, len); + if (ret) + goto err; + + if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + ret = regmap_update_bits(dev->regmap, 0x00d91c, 0x01, 0x01); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x00d917, 0x01, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x00d916, 0x01, 0x00); + if (ret) + goto err; + } + + switch (dev->cfg.tuner) { + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + ret = regmap_write(dev->regmap, 0x800000, 0x01); + if (ret) + goto err; + } + + dev->bandwidth_hz = 0; /* Force to program all parameters */ + /* Init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_sleep(struct dvb_frontend *fe) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_write(dev->regmap, 0x80004c, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x800000, 0x00); + if (ret) + goto err; + ret = regmap_read_poll_timeout(dev->regmap, 0x80004c, utmp, utmp == 0, + 5000, 1000000); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x80fb24, 0x08, 0x08); + if (ret) + goto err; + + /* Prevent current leak by setting TS interface to parallel mode */ + if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + /* Enable parallel TS */ + ret = regmap_update_bits(dev->regmap, 0x00d917, 0x01, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x00d916, 0x01, 0x01); + if (ret) + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + /* 800 => 2000 because IT9135 v2 is slow to gain lock */ + fesettings->min_delay_ms = 2000; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9033_set_frontend(struct dvb_frontend *fe) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + unsigned int utmp, adc_freq; + u8 tmp, buf[3], bandwidth_reg_val; + u32 if_frequency; + + dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u\n", + c->frequency, c->bandwidth_hz); + + /* Check bandwidth */ + switch (c->bandwidth_hz) { + case 6000000: + bandwidth_reg_val = 0x00; + break; + case 7000000: + bandwidth_reg_val = 0x01; + break; + case 8000000: + bandwidth_reg_val = 0x02; + break; + default: + dev_dbg(&client->dev, "invalid bandwidth_hz\n"); + ret = -EINVAL; + goto err; + } + + /* Program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* Coefficients */ + if (c->bandwidth_hz != dev->bandwidth_hz) { + for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { + if (coeff_lut[i].clock == dev->cfg.clock && + coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { + break; + } + } + if (i == ARRAY_SIZE(coeff_lut)) { + dev_err(&client->dev, + "Couldn't find config for clock %u\n", + dev->cfg.clock); + ret = -EINVAL; + goto err; + } + + ret = regmap_bulk_write(dev->regmap, 0x800001, coeff_lut[i].val, + sizeof(coeff_lut[i].val)); + if (ret) + goto err; + } + + /* IF frequency control */ + if (c->bandwidth_hz != dev->bandwidth_hz) { + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == dev->cfg.clock) + break; + } + if (i == ARRAY_SIZE(clock_adc_lut)) { + dev_err(&client->dev, + "Couldn't find ADC clock for clock %u\n", + dev->cfg.clock); + ret = -EINVAL; + goto err; + } + adc_freq = clock_adc_lut[i].adc; + + if (dev->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X) + adc_freq = 2 * adc_freq; + + /* Get used IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + else + if_frequency = 0; + + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x800000, + adc_freq); + + if (!dev->cfg.spec_inv && if_frequency) + utmp = 0x800000 - utmp; + + buf[0] = (utmp >> 0) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 16) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x800029, buf, 3); + if (ret) + goto err; + + dev_dbg(&client->dev, "if_frequency_cw=%06x\n", utmp); + + dev->bandwidth_hz = c->bandwidth_hz; + } + + ret = regmap_update_bits(dev->regmap, 0x80f904, 0x03, + bandwidth_reg_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x800040, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x800047, 0x00); + if (ret) + goto err; + ret = regmap_update_bits(dev->regmap, 0x80f999, 0x01, 0x00); + if (ret) + goto err; + + if (c->frequency <= 230000000) + tmp = 0x00; /* VHF */ + else + tmp = 0x01; /* UHF */ + + ret = regmap_write(dev->regmap, 0x80004b, tmp); + if (ret) + goto err; + /* Reset FSM */ + ret = regmap_write(dev->regmap, 0x800000, 0x00); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 buf[8]; + + dev_dbg(&client->dev, "\n"); + + /* Read all needed TPS registers */ + ret = regmap_bulk_read(dev->regmap, 0x80f900, buf, 8); + if (ret) + goto err; + + switch ((buf[0] >> 0) & 3) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch ((buf[1] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[3] >> 0) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[4] >> 0) & 3) { + case 0: + c->bandwidth_hz = 6000000; + break; + case 1: + c->bandwidth_hz = 7000000; + break; + case 2: + c->bandwidth_hz = 8000000; + break; + } + + switch ((buf[6] >> 0) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + case 5: + c->code_rate_HP = FEC_NONE; + break; + } + + switch ((buf[7] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + case 5: + c->code_rate_LP = FEC_NONE; + break; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, tmp = 0; + u8 buf[7]; + unsigned int utmp, utmp1; + + dev_dbg(&client->dev, "\n"); + + *status = 0; + + /* Radio channel status: 0=no result, 1=has signal, 2=no signal */ + ret = regmap_read(dev->regmap, 0x800047, &utmp); + if (ret) + goto err; + + /* Has signal */ + if (utmp == 0x01) + *status |= FE_HAS_SIGNAL; + + if (utmp != 0x02) { + /* TPS lock */ + ret = regmap_read(dev->regmap, 0x80f5a9, &utmp); + if (ret) + goto err; + + if ((utmp >> 0) & 0x01) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + + /* Full lock */ + ret = regmap_read(dev->regmap, 0x80f999, &utmp); + if (ret) + goto err; + + if ((utmp >> 0) & 0x01) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } + + dev->fe_status = *status; + + /* Signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + if (dev->is_af9035) { + ret = regmap_read(dev->regmap, 0x80004a, &utmp); + if (ret) + goto err; + tmp = -utmp * 1000; + } else { + ret = regmap_read(dev->regmap, 0x8000f7, &utmp); + if (ret) + goto err; + tmp = (utmp - 100) * 1000; + } + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = tmp; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + /* Read raw SNR value */ + ret = regmap_bulk_read(dev->regmap, 0x80002c, buf, 3); + if (ret) + goto err; + + utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0; + + /* Read superframe number */ + ret = regmap_read(dev->regmap, 0x80f78b, &utmp); + if (ret) + goto err; + + if (utmp) + utmp1 /= utmp; + + /* Read current transmission mode */ + ret = regmap_read(dev->regmap, 0x80f900, &utmp); + if (ret) + goto err; + + switch ((utmp >> 0) & 3) { + case 0: + /* 2k */ + utmp1 *= 4; + break; + case 1: + /* 8k */ + utmp1 *= 1; + break; + case 2: + /* 4k */ + utmp1 *= 2; + break; + default: + utmp1 *= 0; + break; + } + + /* Read current modulation */ + ret = regmap_read(dev->regmap, 0x80f903, &utmp); + if (ret) + goto err; + + switch ((utmp >> 0) & 3) { + case 0: + /* + * QPSK + * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6 + * value [653799, 1689999], 2.6 / 13 = 3355443 + */ + utmp1 = clamp(utmp1, 653799U, 1689999U); + utmp1 = ((u64)(intlog10(utmp1) + - intlog10(1690000 - utmp1) + + 3355443) * 13 * 1000) >> 24; + break; + case 1: + /* + * QAM-16 + * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7 + * value [371105, 827999], 15.7 / 6 = 43900382 + */ + utmp1 = clamp(utmp1, 371105U, 827999U); + utmp1 = ((u64)(intlog10(utmp1 - 370000) + - intlog10(828000 - utmp1) + + 43900382) * 6 * 1000) >> 24; + break; + case 2: + /* + * QAM-64 + * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8 + * value [193246, 424999], 23.8 / 8 = 49912218 + */ + utmp1 = clamp(utmp1, 193246U, 424999U); + utmp1 = ((u64)(intlog10(utmp1 - 193000) + - intlog10(425000 - utmp1) + + 49912218) * 8 * 1000) >> 24; + break; + default: + utmp1 = 0; + break; + } + + dev_dbg(&client->dev, "cnr=%u\n", utmp1); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = utmp1; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* UCB/PER/BER */ + if (dev->fe_status & FE_HAS_LOCK) { + /* Outer FEC, 204 byte packets */ + u16 abort_packet_count, rsd_packet_count; + /* Inner FEC, bits */ + u32 rsd_bit_err_count; + + /* + * Packet count used for measurement is 10000 + * (rsd_packet_count). Maybe it should be increased? + */ + + ret = regmap_bulk_read(dev->regmap, 0x800032, buf, 7); + if (ret) + goto err; + + abort_packet_count = (buf[1] << 8) | (buf[0] << 0); + rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2]; + rsd_packet_count = (buf[6] << 8) | (buf[5] << 0); + + dev->error_block_count += abort_packet_count; + dev->total_block_count += rsd_packet_count; + dev->post_bit_error += rsd_bit_err_count; + dev->post_bit_count += rsd_packet_count * 204 * 8; + + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue = dev->total_block_count; + + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = dev->error_block_count; + + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret; + unsigned int utmp; + + dev_dbg(&client->dev, "\n"); + + /* Use DVBv5 CNR */ + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) { + /* Return 0.1 dB for AF9030 and 0-0xffff for IT9130. */ + if (dev->is_af9035) { + /* 1000x => 10x (0.1 dB) */ + *snr = div_s64(c->cnr.stat[0].svalue, 100); + } else { + /* 1000x => 1x (1 dB) */ + *snr = div_s64(c->cnr.stat[0].svalue, 1000); + + /* Read current modulation */ + ret = regmap_read(dev->regmap, 0x80f903, &utmp); + if (ret) + goto err; + + /* scale value to 0x0000-0xffff */ + switch ((utmp >> 0) & 3) { + case 0: + *snr = *snr * 0xffff / 23; + break; + case 1: + *snr = *snr * 0xffff / 26; + break; + case 2: + *snr = *snr * 0xffff / 32; + break; + default: + ret = -EINVAL; + goto err; + } + } + } else { + *snr = 0; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, tmp, power_real; + unsigned int utmp; + u8 gain_offset, buf[7]; + + dev_dbg(&client->dev, "\n"); + + if (dev->is_af9035) { + /* Read signal strength of 0-100 scale */ + ret = regmap_read(dev->regmap, 0x800048, &utmp); + if (ret) + goto err; + + /* Scale value to 0x0000-0xffff */ + *strength = utmp * 0xffff / 100; + } else { + ret = regmap_read(dev->regmap, 0x8000f7, &utmp); + if (ret) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0x80f900, buf, 7); + if (ret) + goto err; + + if (c->frequency <= 300000000) + gain_offset = 7; /* VHF */ + else + gain_offset = 4; /* UHF */ + + power_real = (utmp - 100 - gain_offset) - + power_reference[((buf[3] >> 0) & 3)][((buf[6] >> 0) & 7)]; + + if (power_real < -15) + tmp = 0; + else if ((power_real >= -15) && (power_real < 0)) + tmp = (2 * (power_real + 15)) / 3; + else if ((power_real >= 0) && (power_real < 20)) + tmp = 4 * power_real + 10; + else if ((power_real >= 20) && (power_real < 35)) + tmp = (2 * (power_real - 20)) / 3 + 90; + else + tmp = 100; + + /* Scale value to 0x0000-0xffff */ + *strength = tmp * 0xffff / 100; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct af9033_dev *dev = fe->demodulator_priv; + + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; + + return 0; +} + +static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct af9033_dev *dev = fe->demodulator_priv; + + *ucblocks = dev->error_block_count; + + return 0; +} + +static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + + dev_dbg(&client->dev, "enable=%d\n", enable); + + ret = regmap_update_bits(dev->regmap, 0x00fa04, 0x01, enable); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + + dev_dbg(&client->dev, "onoff=%d\n", onoff); + + ret = regmap_update_bits(dev->regmap, 0x80f993, 0x01, onoff); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, + int onoff) +{ + struct af9033_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff}; + + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", + index, pid, onoff); + + if (pid > 0x1fff) + return 0; + + ret = regmap_bulk_write(dev->regmap, 0x80f996, wbuf, 2); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x80f994, onoff); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x80f995, index); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops af9033_ops = { + .delsys = {SYS_DVBT}, + .info = { + .name = "Afatech AF9033 (DVB-T)", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 250 * kHz, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .init = af9033_init, + .sleep = af9033_sleep, + + .get_tune_settings = af9033_get_tune_settings, + .set_frontend = af9033_set_frontend, + .get_frontend = af9033_get_frontend, + + .read_status = af9033_read_status, + .read_snr = af9033_read_snr, + .read_signal_strength = af9033_read_signal_strength, + .read_ber = af9033_read_ber, + .read_ucblocks = af9033_read_ucblocks, + + .i2c_gate_ctrl = af9033_i2c_gate_ctrl, +}; + +static int af9033_probe(struct i2c_client *client) +{ + struct af9033_config *cfg = client->dev.platform_data; + struct af9033_dev *dev; + int ret; + u8 buf[8]; + u32 reg; + static const struct regmap_config regmap_config = { + .reg_bits = 24, + .val_bits = 8, + }; + + /* Allocate memory for the internal state */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + /* Setup the state */ + dev->client = client; + memcpy(&dev->cfg, cfg, sizeof(dev->cfg)); + switch (dev->cfg.ts_mode) { + case AF9033_TS_MODE_PARALLEL: + dev->ts_mode_parallel = true; + break; + case AF9033_TS_MODE_SERIAL: + dev->ts_mode_serial = true; + break; + case AF9033_TS_MODE_USB: + /* USB mode for AF9035 */ + default: + break; + } + + if (dev->cfg.clock != 12000000) { + ret = -ENODEV; + dev_err(&client->dev, + "Unsupported clock %u Hz. Only 12000000 Hz is supported currently\n", + dev->cfg.clock); + goto err_kfree; + } + + /* Create regmap */ + dev->regmap = regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + /* Firmware version */ + switch (dev->cfg.tuner) { + case AF9033_TUNER_IT9135_38: + case AF9033_TUNER_IT9135_51: + case AF9033_TUNER_IT9135_52: + case AF9033_TUNER_IT9135_60: + case AF9033_TUNER_IT9135_61: + case AF9033_TUNER_IT9135_62: + dev->is_it9135 = true; + reg = 0x004bfc; + break; + default: + dev->is_af9035 = true; + reg = 0x0083e9; + break; + } + + ret = regmap_bulk_read(dev->regmap, reg, &buf[0], 4); + if (ret) + goto err_regmap_exit; + ret = regmap_bulk_read(dev->regmap, 0x804191, &buf[4], 4); + if (ret) + goto err_regmap_exit; + + dev_info(&client->dev, + "firmware version: LINK %d.%d.%d.%d - OFDM %d.%d.%d.%d\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* Sleep as chip seems to be partly active by default */ + /* IT9135 did not like to sleep at that early */ + if (dev->is_af9035) { + ret = regmap_write(dev->regmap, 0x80004c, 0x01); + if (ret) + goto err_regmap_exit; + ret = regmap_write(dev->regmap, 0x800000, 0x00); + if (ret) + goto err_regmap_exit; + } + + /* Create dvb frontend */ + memcpy(&dev->fe.ops, &af9033_ops, sizeof(dev->fe.ops)); + dev->fe.demodulator_priv = dev; + *cfg->fe = &dev->fe; + if (cfg->ops) { + cfg->ops->pid_filter = af9033_pid_filter; + cfg->ops->pid_filter_ctrl = af9033_pid_filter_ctrl; + } + cfg->regmap = dev->regmap; + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "Afatech AF9033 successfully attached\n"); + + return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void af9033_remove(struct i2c_client *client) +{ + struct af9033_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + regmap_exit(dev->regmap); + kfree(dev); +} + +static const struct i2c_device_id af9033_id_table[] = { + {"af9033", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, af9033_id_table); + +static struct i2c_driver af9033_driver = { + .driver = { + .name = "af9033", + .suppress_bind_attrs = true, + }, + .probe = af9033_probe, + .remove = af9033_remove, + .id_table = af9033_id_table, +}; + +module_i2c_driver(af9033_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h new file mode 100644 index 0000000000..4f2277af1a --- /dev/null +++ b/drivers/media/dvb-frontends/af9033.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + */ + +#ifndef AF9033_H +#define AF9033_H + +/* + * I2C address: 0x1c, 0x1d, 0x1e, 0x1f + */ +struct af9033_config { + /* + * clock Hz + * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000, + * 30000000, 36000000, 20480000, 16384000 + */ + u32 clock; + + /* + * ADC multiplier + */ +#define AF9033_ADC_MULTIPLIER_1X 0 +#define AF9033_ADC_MULTIPLIER_2X 1 + u8 adc_multiplier; + + /* + * tuner + */ +#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ +#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_FC0012 0x2e /* Fitipower FC0012 */ +#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ +#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ +#define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ +/* 50-5f Omega */ +#define AF9033_TUNER_IT9135_38 0x38 /* Omega */ +#define AF9033_TUNER_IT9135_51 0x51 /* Omega LNA config 1 */ +#define AF9033_TUNER_IT9135_52 0x52 /* Omega LNA config 2 */ +/* 60-6f Omega v2 */ +#define AF9033_TUNER_IT9135_60 0x60 /* Omega v2 */ +#define AF9033_TUNER_IT9135_61 0x61 /* Omega v2 LNA config 1 */ +#define AF9033_TUNER_IT9135_62 0x62 /* Omega v2 LNA config 2 */ + u8 tuner; + + /* + * TS settings + */ +#define AF9033_TS_MODE_USB 0 +#define AF9033_TS_MODE_PARALLEL 1 +#define AF9033_TS_MODE_SERIAL 2 + u8 ts_mode:2; + + /* + * input spectrum inversion + */ + bool spec_inv; + + /* + * + */ + bool dyn0_clk; + + /* + * PID filter ops + */ + struct af9033_ops *ops; + + /* + * frontend + * returned by that driver + */ + struct dvb_frontend **fe; + + /* + * regmap for IT913x integrated tuner driver + * returned by that driver + */ + struct regmap *regmap; +}; + +struct af9033_ops { + int (*pid_filter_ctrl)(struct dvb_frontend *fe, int onoff); + int (*pid_filter)(struct dvb_frontend *fe, int index, u16 pid, + int onoff); +}; + +#endif /* AF9033_H */ diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h new file mode 100644 index 0000000000..7560da75ef --- /dev/null +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -0,0 +1,1991 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + */ + +#ifndef AF9033_PRIV_H +#define AF9033_PRIV_H + +#include <media/dvb_frontend.h> +#include "af9033.h" +#include <linux/math64.h> +#include <linux/regmap.h> +#include <linux/kernel.h> +#include <linux/int_log.h> + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct coeff { + u32 clock; + u32 bandwidth_hz; + u8 val[36]; +}; + +struct clock_adc { + u32 clock; + u32 adc; +}; + +struct val_snr { + u32 val; + u8 snr; +}; + +/* Xtal clock vs. ADC clock lookup table */ +static const struct clock_adc clock_adc_lut[] = { + { 16384000, 20480000 }, + { 20480000, 20480000 }, + { 36000000, 20250000 }, + { 30000000, 20156250 }, + { 26000000, 20583333 }, + { 28000000, 20416667 }, + { 32000000, 20500000 }, + { 34000000, 20187500 }, + { 24000000, 20500000 }, + { 22000000, 20625000 }, + { 12000000, 20250000 }, +}; + +/* Pre-calculated coeff lookup table */ +static const struct coeff coeff_lut[] = { + /* 12.000 MHz */ + { 12000000, 8000000, { + 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5, + 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 } + }, + { 12000000, 7000000, { + 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b, + 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 } + }, + { 12000000, 6000000, { + 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60, + 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 } + }, +}; + +/* + * Afatech AF9033 demod init + */ +static const struct reg_val ofsm_init[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c4, 0x05 }, + { 0x8000c8, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * Infineon TUA 9001 tuner init + * AF9033_TUNER_TUA9001 = 0x27 + */ +static const struct reg_val tuner_init_tua9001[] = { + { 0x800046, 0x27 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x00 }, + { 0x800078, 0x01 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x7e }, + { 0x80007b, 0x3e }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + +/* + * Fitipower FC0011 tuner init + * AF9033_TUNER_FC0011 = 0x28 + */ +static const struct reg_val tuner_init_fc0011[] = { + { 0x800046, 0x28 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0xa5 }, + { 0x80006e, 0x01 }, + { 0x800071, 0x0a }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x80009b, 0x2d }, + { 0x80009c, 0x60 }, + { 0x80009d, 0x23 }, + { 0x8000a4, 0x50 }, + { 0x8000ad, 0x50 }, + { 0x8000b3, 0x01 }, + { 0x8000b7, 0x88 }, + { 0x8000b8, 0xa6 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x01 }, + { 0x8000c9, 0x69 }, + { 0x80f007, 0x00 }, + { 0x80f00a, 0x1b }, + { 0x80f00b, 0x1b }, + { 0x80f00c, 0x1b }, + { 0x80f00d, 0x1b }, + { 0x80f00e, 0xff }, + { 0x80f00f, 0x01 }, + { 0x80f010, 0x00 }, + { 0x80f011, 0x02 }, + { 0x80f012, 0xff }, + { 0x80f013, 0x01 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01b, 0xef }, + { 0x80f01c, 0x01 }, + { 0x80f01d, 0x0f }, + { 0x80f01e, 0x02 }, + { 0x80f01f, 0x6e }, + { 0x80f020, 0x00 }, + { 0x80f025, 0xde }, + { 0x80f026, 0x00 }, + { 0x80f027, 0x0a }, + { 0x80f028, 0x03 }, + { 0x80f029, 0x6e }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + +/* + * Fitipower FC0012 tuner init + * AF9033_TUNER_FC0012 = 0x2e + */ +static const struct reg_val tuner_init_fc0012[] = { + { 0x800046, 0x2e }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x800059, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x01 }, + { 0x800078, 0x00 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x90 }, + { 0x80007b, 0x90 }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0xa0 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + +/* + * MaxLinear MxL5007T tuner init + * AF9033_TUNER_MXL5007T = 0xa0 + */ +static const struct reg_val tuner_init_mxl5007t[] = { + { 0x800046, 0x1b }, + { 0x800057, 0x01 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x96 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x8000b3, 0x01 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f00c, 0x19 }, + { 0x80f00d, 0x1a }, + { 0x80f012, 0xda }, + { 0x80f013, 0x00 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f077, 0x02 }, + { 0x80f1e6, 0x00 }, +}; + +/* + * NXP TDA18218HN tuner init + * AF9033_TUNER_TDA18218 = 0xa1 + */ +static const struct reg_val tuner_init_tda18218[] = { + {0x800046, 0xa1}, + {0x800057, 0x01}, + {0x800058, 0x01}, + {0x80005f, 0x00}, + {0x800060, 0x00}, + {0x800071, 0x05}, + {0x800072, 0x02}, + {0x800074, 0x01}, + {0x800079, 0x01}, + {0x800093, 0x00}, + {0x800094, 0x00}, + {0x800095, 0x00}, + {0x800096, 0x00}, + {0x8000b3, 0x01}, + {0x8000c3, 0x01}, + {0x8000c4, 0x00}, + {0x80f007, 0x00}, + {0x80f00c, 0x19}, + {0x80f00d, 0x1a}, + {0x80f012, 0xda}, + {0x80f013, 0x00}, + {0x80f014, 0x00}, + {0x80f015, 0x02}, + {0x80f01f, 0x82}, + {0x80f020, 0x00}, + {0x80f029, 0x82}, + {0x80f02a, 0x00}, + {0x80f077, 0x02}, + {0x80f1e6, 0x00}, +}; + +/* + * FCI FC2580 tuner init + * AF9033_TUNER_FC2580 = 0x32 + */ +static const struct reg_val tuner_init_fc2580[] = { + { 0x800046, 0x32 }, + { 0x800057, 0x01 }, + { 0x800058, 0x00 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x05 }, + { 0x8000b3, 0x01 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000d1, 0x01 }, + { 0x80f007, 0x00 }, + { 0x80f00c, 0x19 }, + { 0x80f00d, 0x1a }, + { 0x80f00e, 0x00 }, + { 0x80f00f, 0x02 }, + { 0x80f010, 0x00 }, + { 0x80f011, 0x02 }, + { 0x80f012, 0x00 }, + { 0x80f013, 0x02 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01f, 0x96 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x96 }, + { 0x80f02a, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x01 }, +}; + +/* + * IT9133 AX demod init + */ +static const struct reg_val ofsm_init_it9135_v1[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c2, 0x05 }, + { 0x8000c6, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 AX Omega tuner init + * AF9033_TUNER_IT9135_38 = 0x38 + */ +static const struct reg_val tuner_init_it9135_38[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x38 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0xc8 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x80007f, 0x00 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800083, 0x02 }, + { 0x800084, 0x0a }, + { 0x800085, 0x03 }, + { 0x800086, 0xc8 }, + { 0x800087, 0xb8 }, + { 0x800088, 0xd0 }, + { 0x800089, 0xc3 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x32 }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cc, 0x2e }, + { 0x8000cd, 0x51 }, + { 0x8000ce, 0x33 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x8c }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x02 }, + { 0x8000fd, 0x02 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x7b }, + { 0x800102, 0x77 }, + { 0x800103, 0x00 }, + { 0x800104, 0x02 }, + { 0x800105, 0xc8 }, + { 0x800106, 0x05 }, + { 0x800107, 0x7b }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x800117, 0x02 }, + { 0x800118, 0x80 }, + { 0x80011a, 0xc8 }, + { 0x80011b, 0x7b }, + { 0x80011c, 0x8a }, + { 0x80011d, 0xa0 }, + { 0x800122, 0x02 }, + { 0x800123, 0x18 }, + { 0x800124, 0xc3 }, + { 0x800127, 0x00 }, + { 0x800128, 0x07 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x01 }, + { 0x800138, 0x00 }, + { 0x800139, 0x07 }, + { 0x80013a, 0x00 }, + { 0x80013b, 0x06 }, + { 0x80013d, 0x00 }, + { 0x80013e, 0x01 }, + { 0x80013f, 0x5b }, + { 0x800140, 0xc8 }, + { 0x800141, 0x59 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f085, 0x00 }, + { 0x80f086, 0x02 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 AX Omega LNA config 1 tuner init + * AF9033_TUNER_IT9135_51 = 0x51 + */ +static const struct reg_val tuner_init_it9135_51[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x51 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x800070, 0x0a }, + { 0x800071, 0x06 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0xc8 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x80007f, 0x00 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800083, 0x02 }, + { 0x800084, 0x0a }, + { 0x800085, 0x03 }, + { 0x800086, 0xc0 }, + { 0x800087, 0x96 }, + { 0x800088, 0xcf }, + { 0x800089, 0xc3 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3c }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cc, 0x2e }, + { 0x8000cd, 0x51 }, + { 0x8000ce, 0x33 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x8c }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x02 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x7a }, + { 0x800102, 0x77 }, + { 0x800103, 0x01 }, + { 0x800104, 0x02 }, + { 0x800105, 0xb0 }, + { 0x800106, 0x02 }, + { 0x800107, 0x7a }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x800117, 0x02 }, + { 0x800118, 0x80 }, + { 0x80011a, 0xc0 }, + { 0x80011b, 0x7a }, + { 0x80011c, 0xac }, + { 0x80011d, 0x8c }, + { 0x800122, 0x02 }, + { 0x800123, 0x70 }, + { 0x800124, 0xa4 }, + { 0x800127, 0x00 }, + { 0x800128, 0x07 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x01 }, + { 0x800138, 0x00 }, + { 0x800139, 0x07 }, + { 0x80013a, 0x00 }, + { 0x80013b, 0x06 }, + { 0x80013d, 0x00 }, + { 0x80013e, 0x01 }, + { 0x80013f, 0x5b }, + { 0x800140, 0xc0 }, + { 0x800141, 0x59 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f085, 0xc0 }, + { 0x80f086, 0x01 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 AX Omega LNA config 2 tuner init + * AF9033_TUNER_IT9135_52 = 0x52 + */ +static const struct reg_val tuner_init_it9135_52[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x52 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x10 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0xa0 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x80007f, 0x00 }, + { 0x800081, 0x0a }, + { 0x800082, 0x17 }, + { 0x800083, 0x03 }, + { 0x800084, 0x0a }, + { 0x800085, 0x03 }, + { 0x800086, 0xb3 }, + { 0x800087, 0x97 }, + { 0x800088, 0xc0 }, + { 0x800089, 0x9e }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5c }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3c }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cc, 0x2e }, + { 0x8000cd, 0x51 }, + { 0x8000ce, 0x33 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x91 }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x02 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x74 }, + { 0x800102, 0x77 }, + { 0x800103, 0x02 }, + { 0x800104, 0x02 }, + { 0x800105, 0xa4 }, + { 0x800106, 0x02 }, + { 0x800107, 0x6e }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x800117, 0x02 }, + { 0x800118, 0x80 }, + { 0x80011a, 0xcd }, + { 0x80011b, 0x62 }, + { 0x80011c, 0xa4 }, + { 0x80011d, 0x8c }, + { 0x800122, 0x03 }, + { 0x800123, 0x18 }, + { 0x800124, 0x9e }, + { 0x800127, 0x00 }, + { 0x800128, 0x07 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x00 }, + { 0x800138, 0x00 }, + { 0x800139, 0x07 }, + { 0x80013a, 0x00 }, + { 0x80013b, 0x06 }, + { 0x80013d, 0x00 }, + { 0x80013e, 0x01 }, + { 0x80013f, 0x5b }, + { 0x800140, 0xb6 }, + { 0x800141, 0x59 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f085, 0xc0 }, + { 0x80f086, 0x01 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 BX demod init + */ +static const struct reg_val ofsm_init_it9135_v2[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c2, 0x05 }, + { 0x8000c6, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 BX Omega tuner init + * AF9033_TUNER_IT9135_60 = 0x60 + */ +static const struct reg_val tuner_init_it9135_60[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x60 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x80006a, 0x03 }, + { 0x800070, 0x0a }, + { 0x800071, 0x0a }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0x8c }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x800082, 0x18 }, + { 0x800084, 0x0a }, + { 0x800085, 0x33 }, + { 0x800086, 0xbe }, + { 0x800087, 0xa0 }, + { 0x800088, 0xc6 }, + { 0x800089, 0xb6 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3a }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cb, 0x32 }, + { 0x8000cc, 0x2c }, + { 0x8000cd, 0x4f }, + { 0x8000ce, 0x30 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0xa0 }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x03 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x0a }, + { 0x800100, 0x50 }, + { 0x800101, 0x7b }, + { 0x800102, 0x8c }, + { 0x800103, 0x00 }, + { 0x800104, 0x02 }, + { 0x800105, 0xbe }, + { 0x800106, 0x00 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x80011a, 0xbe }, + { 0x800124, 0xae }, + { 0x800127, 0x00 }, + { 0x80012a, 0x56 }, + { 0x80012b, 0x50 }, + { 0x80012c, 0x47 }, + { 0x80012d, 0x42 }, + { 0x800137, 0x00 }, + { 0x80013b, 0x08 }, + { 0x80013f, 0x5b }, + { 0x800141, 0x59 }, + { 0x800142, 0xf9 }, + { 0x800143, 0x19 }, + { 0x800144, 0x00 }, + { 0x800145, 0x8c }, + { 0x800146, 0x8c }, + { 0x800147, 0x8c }, + { 0x800148, 0x6e }, + { 0x800149, 0x8c }, + { 0x80014a, 0x50 }, + { 0x80014b, 0x8c }, + { 0x80014d, 0xac }, + { 0x80014e, 0xc6 }, + { 0x800151, 0x1e }, + { 0x800153, 0xbc }, + { 0x800178, 0x09 }, + { 0x800181, 0x94 }, + { 0x800182, 0x6e }, + { 0x800185, 0x24 }, + { 0x800189, 0xbe }, + { 0x80018c, 0x03 }, + { 0x80018d, 0x5f }, + { 0x80018f, 0xa0 }, + { 0x800190, 0x5a }, + { 0x800191, 0x00 }, + { 0x80ed02, 0x40 }, + { 0x80ee42, 0x40 }, + { 0x80ee82, 0x40 }, + { 0x80f000, 0x0f }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 BX Omega LNA config 1 tuner init + * AF9033_TUNER_IT9135_61 = 0x61 + */ +static const struct reg_val tuner_init_it9135_61[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x61 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x06 }, + { 0x80006a, 0x03 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0x90 }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800084, 0x0a }, + { 0x800085, 0x33 }, + { 0x800086, 0xbc }, + { 0x800087, 0x9c }, + { 0x800088, 0xcc }, + { 0x800089, 0xa8 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5c }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3a }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cb, 0x32 }, + { 0x8000cc, 0x2c }, + { 0x8000cd, 0x4f }, + { 0x8000ce, 0x30 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0xa0 }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x03 }, + { 0x8000fd, 0x03 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x08 }, + { 0x800100, 0x50 }, + { 0x800101, 0x7b }, + { 0x800102, 0x8c }, + { 0x800103, 0x01 }, + { 0x800104, 0x02 }, + { 0x800105, 0xc8 }, + { 0x800106, 0x00 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x80011a, 0xc6 }, + { 0x800124, 0xa8 }, + { 0x800127, 0x00 }, + { 0x80012a, 0x59 }, + { 0x80012b, 0x50 }, + { 0x80012c, 0x47 }, + { 0x80012d, 0x42 }, + { 0x800137, 0x00 }, + { 0x80013b, 0x05 }, + { 0x80013f, 0x5b }, + { 0x800141, 0x59 }, + { 0x800142, 0xf9 }, + { 0x800143, 0x59 }, + { 0x800144, 0x01 }, + { 0x800145, 0x8c }, + { 0x800146, 0x8c }, + { 0x800147, 0x8c }, + { 0x800148, 0x7b }, + { 0x800149, 0x8c }, + { 0x80014a, 0x50 }, + { 0x80014b, 0x8c }, + { 0x80014d, 0xa8 }, + { 0x80014e, 0xc6 }, + { 0x800151, 0x28 }, + { 0x800153, 0xcc }, + { 0x800178, 0x09 }, + { 0x800181, 0x9c }, + { 0x800182, 0x76 }, + { 0x800185, 0x28 }, + { 0x800189, 0xaa }, + { 0x80018c, 0x03 }, + { 0x80018d, 0x5f }, + { 0x80018f, 0xfb }, + { 0x800190, 0x5c }, + { 0x800191, 0x00 }, + { 0x80ed02, 0x40 }, + { 0x80ee42, 0x40 }, + { 0x80ee82, 0x40 }, + { 0x80f000, 0x0f }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* + * ITE Tech IT9133 BX Omega LNA config 2 tuner init + * AF9033_TUNER_IT9135_62 = 0x62 + */ +static const struct reg_val tuner_init_it9135_62[] = { + { 0x800043, 0x00 }, + { 0x800046, 0x62 }, + { 0x800051, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x0a }, + { 0x80006a, 0x03 }, + { 0x800070, 0x0a }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800075, 0x8c }, + { 0x800076, 0x8c }, + { 0x800077, 0x8c }, + { 0x800078, 0x8c }, + { 0x800079, 0x01 }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x800082, 0x12 }, + { 0x800084, 0x0a }, + { 0x800085, 0x33 }, + { 0x800086, 0xb8 }, + { 0x800087, 0x9c }, + { 0x800088, 0xb2 }, + { 0x800089, 0xa6 }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x800099, 0x01 }, + { 0x80009b, 0x3c }, + { 0x80009c, 0x28 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a4, 0x5a }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000b3, 0x02 }, + { 0x8000b4, 0x3a }, + { 0x8000b6, 0x14 }, + { 0x8000c0, 0x11 }, + { 0x8000c1, 0x00 }, + { 0x8000c2, 0x05 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x00 }, + { 0x8000c6, 0x19 }, + { 0x8000c7, 0x00 }, + { 0x8000cb, 0x32 }, + { 0x8000cc, 0x2c }, + { 0x8000cd, 0x4f }, + { 0x8000ce, 0x30 }, + { 0x8000f3, 0x05 }, + { 0x8000f4, 0x8c }, + { 0x8000f5, 0x8c }, + { 0x8000f8, 0x03 }, + { 0x8000f9, 0x06 }, + { 0x8000fa, 0x06 }, + { 0x8000fc, 0x02 }, + { 0x8000fd, 0x03 }, + { 0x8000fe, 0x02 }, + { 0x8000ff, 0x09 }, + { 0x800100, 0x50 }, + { 0x800101, 0x6e }, + { 0x800102, 0x8c }, + { 0x800103, 0x02 }, + { 0x800104, 0x02 }, + { 0x800105, 0xc2 }, + { 0x800106, 0x00 }, + { 0x800109, 0x02 }, + { 0x800115, 0x0a }, + { 0x800116, 0x03 }, + { 0x80011a, 0xb8 }, + { 0x800124, 0xa8 }, + { 0x800127, 0x00 }, + { 0x80012a, 0x53 }, + { 0x80012b, 0x51 }, + { 0x80012c, 0x4e }, + { 0x80012d, 0x43 }, + { 0x800137, 0x00 }, + { 0x80013b, 0x05 }, + { 0x80013f, 0x5b }, + { 0x800141, 0x59 }, + { 0x800142, 0xf9 }, + { 0x800143, 0x59 }, + { 0x800144, 0x00 }, + { 0x800145, 0x8c }, + { 0x800146, 0x8c }, + { 0x800147, 0x8c }, + { 0x800148, 0x7b }, + { 0x800149, 0x8c }, + { 0x80014a, 0x50 }, + { 0x80014b, 0x70 }, + { 0x80014d, 0x96 }, + { 0x80014e, 0xd0 }, + { 0x80014f, 0x03 }, + { 0x800151, 0x28 }, + { 0x800153, 0xb2 }, + { 0x800178, 0x09 }, + { 0x800181, 0x9c }, + { 0x800182, 0x6e }, + { 0x800185, 0x24 }, + { 0x800189, 0xb8 }, + { 0x80018c, 0x03 }, + { 0x80018d, 0x5f }, + { 0x80018f, 0xfb }, + { 0x800190, 0x5a }, + { 0x80ed02, 0xff }, + { 0x80ee42, 0xff }, + { 0x80ee82, 0xff }, + { 0x80f000, 0x0f }, + { 0x80f01f, 0x8c }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x8c }, + { 0x80f02a, 0x00 }, + { 0x80f02b, 0x00 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f077, 0x01 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f24c, 0x88 }, + { 0x80f24d, 0x95 }, + { 0x80f24e, 0x9a }, + { 0x80f24f, 0x90 }, + { 0x80f25a, 0x07 }, + { 0x80f25b, 0xe8 }, + { 0x80f25c, 0x03 }, + { 0x80f25d, 0xb0 }, + { 0x80f25e, 0x04 }, + { 0x80f270, 0x01 }, + { 0x80f271, 0x02 }, + { 0x80f272, 0x01 }, + { 0x80f273, 0x02 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* NorDig power reference table */ +static const int power_reference[][5] = { + {-93, -91, -90, -89, -88}, /* QPSK 1/2 ~ 7/8 */ + {-87, -85, -84, -83, -82}, /* 16QAM 1/2 ~ 7/8 */ + {-82, -80, -78, -77, -76}, /* 64QAM 1/2 ~ 7/8 */ +}; +#endif /* AF9033_PRIV_H */ diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c new file mode 100644 index 0000000000..bc72d954dc --- /dev/null +++ b/drivers/media/dvb-frontends/as102_fe.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> + */ + +#include <media/dvb_frontend.h> + +#include "as102_fe.h" + +struct as102_state { + struct dvb_frontend frontend; + struct as10x_demod_stats demod_stats; + + const struct as102_fe_ops *ops; + void *priv; + uint8_t elna_cfg; + + /* signal strength */ + uint16_t signal_strength; + /* bit error rate */ + uint32_t ber; +}; + +static uint8_t as102_fe_get_code_rate(enum fe_code_rate arg) +{ + uint8_t c; + + switch (arg) { + case FEC_1_2: + c = CODE_RATE_1_2; + break; + case FEC_2_3: + c = CODE_RATE_2_3; + break; + case FEC_3_4: + c = CODE_RATE_3_4; + break; + case FEC_5_6: + c = CODE_RATE_5_6; + break; + case FEC_7_8: + c = CODE_RATE_7_8; + break; + default: + c = CODE_RATE_UNKNOWN; + break; + } + + return c; +} + +static int as102_fe_set_frontend(struct dvb_frontend *fe) +{ + struct as102_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct as10x_tune_args tune_args = { 0 }; + + /* set frequency */ + tune_args.freq = c->frequency / 1000; + + /* fix interleaving_mode */ + tune_args.interleaving_mode = INTLV_NATIVE; + + switch (c->bandwidth_hz) { + case 8000000: + tune_args.bandwidth = BW_8_MHZ; + break; + case 7000000: + tune_args.bandwidth = BW_7_MHZ; + break; + case 6000000: + tune_args.bandwidth = BW_6_MHZ; + break; + default: + tune_args.bandwidth = BW_8_MHZ; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_32: + tune_args.guard_interval = GUARD_INT_1_32; + break; + case GUARD_INTERVAL_1_16: + tune_args.guard_interval = GUARD_INT_1_16; + break; + case GUARD_INTERVAL_1_8: + tune_args.guard_interval = GUARD_INT_1_8; + break; + case GUARD_INTERVAL_1_4: + tune_args.guard_interval = GUARD_INT_1_4; + break; + case GUARD_INTERVAL_AUTO: + default: + tune_args.guard_interval = GUARD_UNKNOWN; + break; + } + + switch (c->modulation) { + case QPSK: + tune_args.modulation = CONST_QPSK; + break; + case QAM_16: + tune_args.modulation = CONST_QAM16; + break; + case QAM_64: + tune_args.modulation = CONST_QAM64; + break; + default: + tune_args.modulation = CONST_UNKNOWN; + break; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + tune_args.transmission_mode = TRANS_MODE_2K; + break; + case TRANSMISSION_MODE_8K: + tune_args.transmission_mode = TRANS_MODE_8K; + break; + default: + tune_args.transmission_mode = TRANS_MODE_UNKNOWN; + } + + switch (c->hierarchy) { + case HIERARCHY_NONE: + tune_args.hierarchy = HIER_NONE; + break; + case HIERARCHY_1: + tune_args.hierarchy = HIER_ALPHA_1; + break; + case HIERARCHY_2: + tune_args.hierarchy = HIER_ALPHA_2; + break; + case HIERARCHY_4: + tune_args.hierarchy = HIER_ALPHA_4; + break; + case HIERARCHY_AUTO: + tune_args.hierarchy = HIER_UNKNOWN; + break; + } + + pr_debug("as102: tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n", + c->frequency, + tune_args.bandwidth, + tune_args.guard_interval); + + /* + * Detect a hierarchy selection + * if HP/LP are both set to FEC_NONE, HP will be selected. + */ + if ((tune_args.hierarchy != HIER_NONE) && + ((c->code_rate_LP == FEC_NONE) || + (c->code_rate_HP == FEC_NONE))) { + + if (c->code_rate_LP == FEC_NONE) { + tune_args.hier_select = HIER_HIGH_PRIORITY; + tune_args.code_rate = + as102_fe_get_code_rate(c->code_rate_HP); + } + + if (c->code_rate_HP == FEC_NONE) { + tune_args.hier_select = HIER_LOW_PRIORITY; + tune_args.code_rate = + as102_fe_get_code_rate(c->code_rate_LP); + } + + pr_debug("as102: \thierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x\n", + tune_args.hierarchy, + tune_args.hier_select == HIER_HIGH_PRIORITY ? + "HP" : "LP", + tune_args.hier_select == HIER_HIGH_PRIORITY ? + "HP" : "LP", + tune_args.code_rate); + } else { + tune_args.code_rate = + as102_fe_get_code_rate(c->code_rate_HP); + } + + /* Set frontend arguments */ + return state->ops->set_tune(state->priv, &tune_args); +} + +static int as102_fe_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct as102_state *state = fe->demodulator_priv; + int ret = 0; + struct as10x_tps tps = { 0 }; + + /* send abilis command: GET_TPS */ + ret = state->ops->get_tps(state->priv, &tps); + if (ret < 0) + return ret; + + /* extract constellation */ + switch (tps.modulation) { + case CONST_QPSK: + c->modulation = QPSK; + break; + case CONST_QAM16: + c->modulation = QAM_16; + break; + case CONST_QAM64: + c->modulation = QAM_64; + break; + } + + /* extract hierarchy */ + switch (tps.hierarchy) { + case HIER_NONE: + c->hierarchy = HIERARCHY_NONE; + break; + case HIER_ALPHA_1: + c->hierarchy = HIERARCHY_1; + break; + case HIER_ALPHA_2: + c->hierarchy = HIERARCHY_2; + break; + case HIER_ALPHA_4: + c->hierarchy = HIERARCHY_4; + break; + } + + /* extract code rate HP */ + switch (tps.code_rate_HP) { + case CODE_RATE_1_2: + c->code_rate_HP = FEC_1_2; + break; + case CODE_RATE_2_3: + c->code_rate_HP = FEC_2_3; + break; + case CODE_RATE_3_4: + c->code_rate_HP = FEC_3_4; + break; + case CODE_RATE_5_6: + c->code_rate_HP = FEC_5_6; + break; + case CODE_RATE_7_8: + c->code_rate_HP = FEC_7_8; + break; + } + + /* extract code rate LP */ + switch (tps.code_rate_LP) { + case CODE_RATE_1_2: + c->code_rate_LP = FEC_1_2; + break; + case CODE_RATE_2_3: + c->code_rate_LP = FEC_2_3; + break; + case CODE_RATE_3_4: + c->code_rate_LP = FEC_3_4; + break; + case CODE_RATE_5_6: + c->code_rate_LP = FEC_5_6; + break; + case CODE_RATE_7_8: + c->code_rate_LP = FEC_7_8; + break; + } + + /* extract guard interval */ + switch (tps.guard_interval) { + case GUARD_INT_1_32: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case GUARD_INT_1_16: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case GUARD_INT_1_8: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case GUARD_INT_1_4: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + /* extract transmission mode */ + switch (tps.transmission_mode) { + case TRANS_MODE_2K: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case TRANS_MODE_8K: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + return 0; +} + +static int as102_fe_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *settings) +{ + + settings->min_delay_ms = 1000; + + return 0; +} + +static int as102_fe_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + int ret = 0; + struct as102_state *state = fe->demodulator_priv; + struct as10x_tune_status tstate = { 0 }; + + /* send abilis command: GET_TUNE_STATUS */ + ret = state->ops->get_status(state->priv, &tstate); + if (ret < 0) + return ret; + + state->signal_strength = tstate.signal_strength; + state->ber = tstate.BER; + + switch (tstate.tune_state) { + case TUNE_STATUS_SIGNAL_DVB_OK: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + break; + case TUNE_STATUS_STREAM_DETECTED: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | + FE_HAS_VITERBI; + break; + case TUNE_STATUS_STREAM_TUNED: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | + FE_HAS_LOCK | FE_HAS_VITERBI; + break; + default: + *status = TUNE_STATUS_NOT_TUNED; + } + + pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n", + tstate.tune_state, tstate.signal_strength, + tstate.PER, tstate.BER); + + if (!(*status & FE_HAS_LOCK)) { + memset(&state->demod_stats, 0, sizeof(state->demod_stats)); + return 0; + } + + ret = state->ops->get_stats(state->priv, &state->demod_stats); + if (ret < 0) + memset(&state->demod_stats, 0, sizeof(state->demod_stats)); + + return ret; +} + +/* + * Note: + * - in AS102 SNR=MER + * - the SNR will be returned in linear terms, i.e. not in dB + * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB + * - the accuracy is >2dB for SNR values outside this range + */ +static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct as102_state *state = fe->demodulator_priv; + + *snr = state->demod_stats.mer; + + return 0; +} + +static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct as102_state *state = fe->demodulator_priv; + + *ber = state->ber; + + return 0; +} + +static int as102_fe_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct as102_state *state = fe->demodulator_priv; + + *strength = (((0xffff * 400) * state->signal_strength + 41000) * 2); + + return 0; +} + +static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct as102_state *state = fe->demodulator_priv; + + if (state->demod_stats.has_started) + *ucblocks = state->demod_stats.bad_frame_count; + else + *ucblocks = 0; + + return 0; +} + +static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct as102_state *state = fe->demodulator_priv; + + return state->ops->stream_ctrl(state->priv, acquire, + state->elna_cfg); +} + +static void as102_fe_release(struct dvb_frontend *fe) +{ + struct as102_state *state = fe->demodulator_priv; + + kfree(state); +} + + +static const struct dvb_frontend_ops as102_fe_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Abilis AS102 DVB-T", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_INVERSION_AUTO + | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 + | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO + | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK + | FE_CAN_QAM_AUTO + | FE_CAN_TRANSMISSION_MODE_AUTO + | FE_CAN_GUARD_INTERVAL_AUTO + | FE_CAN_HIERARCHY_AUTO + | FE_CAN_RECOVER + | FE_CAN_MUTE_TS + }, + + .set_frontend = as102_fe_set_frontend, + .get_frontend = as102_fe_get_frontend, + .get_tune_settings = as102_fe_get_tune_settings, + + .read_status = as102_fe_read_status, + .read_snr = as102_fe_read_snr, + .read_ber = as102_fe_read_ber, + .read_signal_strength = as102_fe_read_signal_strength, + .read_ucblocks = as102_fe_read_ucblocks, + .ts_bus_ctrl = as102_fe_ts_bus_ctrl, + .release = as102_fe_release, +}; + +struct dvb_frontend *as102_attach(const char *name, + const struct as102_fe_ops *ops, + void *priv, + uint8_t elna_cfg) +{ + struct as102_state *state; + struct dvb_frontend *fe; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + fe = &state->frontend; + fe->demodulator_priv = state; + state->ops = ops; + state->priv = priv; + state->elna_cfg = elna_cfg; + + /* init frontend callback ops */ + memcpy(&fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops)); + strscpy(fe->ops.info.name, name, sizeof(fe->ops.info.name)); + + return fe; + +} +EXPORT_SYMBOL_GPL(as102_attach); + +MODULE_DESCRIPTION("as102-fe"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>"); diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h new file mode 100644 index 0000000000..a6409b7ca8 --- /dev/null +++ b/drivers/media/dvb-frontends/as102_fe.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> + */ + +#include "as102_fe_types.h" + +struct as102_fe_ops { + int (*set_tune)(void *priv, struct as10x_tune_args *tune_args); + int (*get_tps)(void *priv, struct as10x_tps *tps); + int (*get_status)(void *priv, struct as10x_tune_status *tstate); + int (*get_stats)(void *priv, struct as10x_demod_stats *demod_stats); + int (*stream_ctrl)(void *priv, int acquire, uint32_t elna_cfg); +}; + +struct dvb_frontend *as102_attach(const char *name, + const struct as102_fe_ops *ops, + void *priv, + uint8_t elna_cfg); diff --git a/drivers/media/dvb-frontends/as102_fe_types.h b/drivers/media/dvb-frontends/as102_fe_types.h new file mode 100644 index 0000000000..297f9520eb --- /dev/null +++ b/drivers/media/dvb-frontends/as102_fe_types.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + */ +#ifndef _AS10X_TYPES_H_ +#define _AS10X_TYPES_H_ + +/*********************************/ +/* MACRO DEFINITIONS */ +/*********************************/ + +/* bandwidth constant values */ +#define BW_5_MHZ 0x00 +#define BW_6_MHZ 0x01 +#define BW_7_MHZ 0x02 +#define BW_8_MHZ 0x03 + +/* hierarchy priority selection values */ +#define HIER_NO_PRIORITY 0x00 +#define HIER_LOW_PRIORITY 0x01 +#define HIER_HIGH_PRIORITY 0x02 + +/* constellation available values */ +#define CONST_QPSK 0x00 +#define CONST_QAM16 0x01 +#define CONST_QAM64 0x02 +#define CONST_UNKNOWN 0xFF + +/* hierarchy available values */ +#define HIER_NONE 0x00 +#define HIER_ALPHA_1 0x01 +#define HIER_ALPHA_2 0x02 +#define HIER_ALPHA_4 0x03 +#define HIER_UNKNOWN 0xFF + +/* interleaving available values */ +#define INTLV_NATIVE 0x00 +#define INTLV_IN_DEPTH 0x01 +#define INTLV_UNKNOWN 0xFF + +/* code rate available values */ +#define CODE_RATE_1_2 0x00 +#define CODE_RATE_2_3 0x01 +#define CODE_RATE_3_4 0x02 +#define CODE_RATE_5_6 0x03 +#define CODE_RATE_7_8 0x04 +#define CODE_RATE_UNKNOWN 0xFF + +/* guard interval available values */ +#define GUARD_INT_1_32 0x00 +#define GUARD_INT_1_16 0x01 +#define GUARD_INT_1_8 0x02 +#define GUARD_INT_1_4 0x03 +#define GUARD_UNKNOWN 0xFF + +/* transmission mode available values */ +#define TRANS_MODE_2K 0x00 +#define TRANS_MODE_8K 0x01 +#define TRANS_MODE_4K 0x02 +#define TRANS_MODE_UNKNOWN 0xFF + +/* DVBH signalling available values */ +#define TIMESLICING_PRESENT 0x01 +#define MPE_FEC_PRESENT 0x02 + +/* tune state available */ +#define TUNE_STATUS_NOT_TUNED 0x00 +#define TUNE_STATUS_IDLE 0x01 +#define TUNE_STATUS_LOCKING 0x02 +#define TUNE_STATUS_SIGNAL_DVB_OK 0x03 +#define TUNE_STATUS_STREAM_DETECTED 0x04 +#define TUNE_STATUS_STREAM_TUNED 0x05 +#define TUNE_STATUS_ERROR 0xFF + +/* available TS FID filter types */ +#define TS_PID_TYPE_TS 0 +#define TS_PID_TYPE_PSI_SI 1 +#define TS_PID_TYPE_MPE 2 + +/* number of echos available */ +#define MAX_ECHOS 15 + +/* Context types */ +#define CONTEXT_LNA 1010 +#define CONTEXT_ELNA_HYSTERESIS 4003 +#define CONTEXT_ELNA_GAIN 4004 +#define CONTEXT_MER_THRESHOLD 5005 +#define CONTEXT_MER_OFFSET 5006 +#define CONTEXT_IR_STATE 7000 +#define CONTEXT_TSOUT_MSB_FIRST 7004 +#define CONTEXT_TSOUT_FALLING_EDGE 7005 + +/* Configuration modes */ +#define CFG_MODE_ON 0 +#define CFG_MODE_OFF 1 +#define CFG_MODE_AUTO 2 + +struct as10x_tps { + uint8_t modulation; + uint8_t hierarchy; + uint8_t interleaving_mode; + uint8_t code_rate_HP; + uint8_t code_rate_LP; + uint8_t guard_interval; + uint8_t transmission_mode; + uint8_t DVBH_mask_HP; + uint8_t DVBH_mask_LP; + uint16_t cell_ID; +} __packed; + +struct as10x_tune_args { + /* frequency */ + uint32_t freq; + /* bandwidth */ + uint8_t bandwidth; + /* hierarchy selection */ + uint8_t hier_select; + /* constellation */ + uint8_t modulation; + /* hierarchy */ + uint8_t hierarchy; + /* interleaving mode */ + uint8_t interleaving_mode; + /* code rate */ + uint8_t code_rate; + /* guard interval */ + uint8_t guard_interval; + /* transmission mode */ + uint8_t transmission_mode; +} __packed; + +struct as10x_tune_status { + /* tune status */ + uint8_t tune_state; + /* signal strength */ + int16_t signal_strength; + /* packet error rate 10^-4 */ + uint16_t PER; + /* bit error rate 10^-4 */ + uint16_t BER; +} __packed; + +struct as10x_demod_stats { + /* frame counter */ + uint32_t frame_count; + /* Bad frame counter */ + uint32_t bad_frame_count; + /* Number of wrong bytes fixed by Reed-Solomon */ + uint32_t bytes_fixed_by_rs; + /* Averaged MER */ + uint16_t mer; + /* statistics calculation state indicator (started or not) */ + uint8_t has_started; +} __packed; + +struct as10x_ts_filter { + uint16_t pid; /* valid PID value 0x00 : 0x2000 */ + uint8_t type; /* Red TS_PID_TYPE_<N> values */ + uint8_t idx; /* index in filtering table */ +} __packed; + +struct as10x_register_value { + uint8_t mode; + union { + uint8_t value8; /* 8 bit value */ + uint16_t value16; /* 16 bit value */ + uint32_t value32; /* 32 bit value */ + } __packed u; +} __packed; + +struct as10x_register_addr { + /* register addr */ + uint32_t addr; + /* register mode access */ + uint8_t mode; +}; + +#endif diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c new file mode 100644 index 0000000000..cf8e5f1bd1 --- /dev/null +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ascot2e.c + * + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <linux/types.h> +#include "ascot2e.h" +#include <media/dvb_frontend.h> + +#define MAX_WRITE_REGSIZE 10 + +enum ascot2e_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct ascot2e_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum ascot2e_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); +}; + +enum ascot2e_tv_system_t { + ASCOT2E_DTV_DVBT_5, + ASCOT2E_DTV_DVBT_6, + ASCOT2E_DTV_DVBT_7, + ASCOT2E_DTV_DVBT_8, + ASCOT2E_DTV_DVBT2_1_7, + ASCOT2E_DTV_DVBT2_5, + ASCOT2E_DTV_DVBT2_6, + ASCOT2E_DTV_DVBT2_7, + ASCOT2E_DTV_DVBT2_8, + ASCOT2E_DTV_DVBC_6, + ASCOT2E_DTV_DVBC_8, + ASCOT2E_DTV_DVBC2_6, + ASCOT2E_DTV_DVBC2_8, + ASCOT2E_DTV_UNKNOWN +}; + +struct ascot2e_band_sett { + u8 if_out_sel; + u8 agc_sel; + u8 mix_oll; + u8 rf_gain; + u8 if_bpf_gc; + u8 fif_offset; + u8 bw_offset; + u8 bw; + u8 rf_oldet; + u8 if_bpf_f0; +}; + +#define ASCOT2E_AUTO 0xff +#define ASCOT2E_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define ASCOT2E_BW_6 0x00 +#define ASCOT2E_BW_7 0x01 +#define ASCOT2E_BW_8 0x02 +#define ASCOT2E_BW_1_7 0x03 + +static struct ascot2e_band_sett ascot2e_sett[] = { + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03, + ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01, + ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2), ASCOT2E_BW_8, 0x09, 0x00 } +}; + +static void ascot2e_i2c_debug(struct ascot2e_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("ascot2e: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int ascot2e_write_regs(struct ascot2e_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 > sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + + ascot2e_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val) +{ + u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return ascot2e_write_regs(priv, reg, &tmp, 1); +} + +static int ascot2e_read_regs(struct ascot2e_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ascot2e_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val) +{ + return ascot2e_read_regs(priv, reg, val, 1); +} + +static int ascot2e_set_reg_bits(struct ascot2e_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = ascot2e_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return ascot2e_write_reg(priv, reg, data); +} + +static int ascot2e_enter_power_save(struct ascot2e_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + data[0] = 0x00; + data[1] = 0x04; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x01); + priv->state = STATE_SLEEP; + return 0; +} + +static int ascot2e_leave_power_save(struct ascot2e_priv *priv) +{ + u8 data[2] = { 0xFB, 0x0F }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x00); + priv->state = STATE_ACTIVE; + return 0; +} + +static int ascot2e_init(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return ascot2e_leave_power_save(priv); +} + +static void ascot2e_release(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int ascot2e_sleep(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + ascot2e_enter_power_save(priv); + return 0; +} + +static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe) +{ + enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ascot2e_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = ASCOT2E_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = ASCOT2E_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBT_8; + else { + system = ASCOT2E_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = ASCOT2E_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = ASCOT2E_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBT2_8; + else { + system = ASCOT2E_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, p->bandwidth_hz); + return system; +} + +static int ascot2e_set_params(struct dvb_frontend *fe) +{ + u8 data[10]; + u32 frequency; + enum ascot2e_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, p->frequency / 1000); + tv_system = ascot2e_get_tv_system(fe); + + if (tv_system == ASCOT2E_DTV_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + if (priv->state == STATE_SLEEP) + ascot2e_leave_power_save(priv); + + /* IF_OUT_SEL / AGC_SEL setting */ + data[0] = 0x00; + if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) { + /* AGC pin setting from parameter table */ + data[0] |= (u8)( + (ascot2e_sett[tv_system].agc_sel & 0x03) << 3); + } + if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) { + /* IFOUT pin setting from parameter table */ + data[0] |= (u8)( + (ascot2e_sett[tv_system].if_out_sel & 0x01) << 2); + } + /* Set bit[4:2] only */ + ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c); + /* 0x06 - 0x0F */ + /* REF_R setting (0x06) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + /* xtal, xtal*2 */ + data[0] = (frequency > 500000) ? 16 : 32; + } else { + /* xtal/8, xtal/4 */ + data[0] = (frequency > 500000) ? 2 : 4; + } + /* XOSC_SEL=100uA */ + data[1] = 0x04; + /* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + data[2] = 18; + data[3] = 120; + data[4] = 20; + } else { + data[2] = 48; + data[3] = 10; + data[4] = 30; + } + /* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) + data[5] = (frequency > 500000) ? 0x08 : 0x0c; + else + data[5] = (frequency > 500000) ? 0x30 : 0x38; + /* Set MIX_OLL (0x0C) value from parameter table */ + data[6] = ascot2e_sett[tv_system].mix_oll; + /* Set RF_GAIN (0x0D) setting from parameter table */ + if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) { + /* RF_GAIN auto control enable */ + ascot2e_write_reg(priv, 0x4E, 0x01); + /* RF_GAIN Default value */ + data[7] = 0x00; + } else { + /* RF_GAIN auto control disable */ + ascot2e_write_reg(priv, 0x4E, 0x00); + data[7] = ascot2e_sett[tv_system].rf_gain; + } + /* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */ + data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) | + (ascot2e_sett[tv_system].if_bpf_gc & 0x07)); + /* Set BW_OFFSET (0x0F) value from parameter table */ + data[9] = ascot2e_sett[tv_system].bw_offset; + ascot2e_write_regs(priv, 0x06, data, 10); + /* + * 0x45 - 0x47 + * LNA optimization setting + * RF_LNA_DIST1-5, RF_LNA_CM + */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + data[0] = 0x0F; + data[1] = 0x00; + data[2] = 0x01; + } else { + data[0] = 0x0F; + data[1] = 0x00; + data[2] = 0x03; + } + ascot2e_write_regs(priv, 0x45, data, 3); + /* 0x49 - 0x4A + Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */ + data[0] = ascot2e_sett[tv_system].rf_oldet; + /* Set IF_BPF_F0 value from parameter table */ + data[1] = ascot2e_sett[tv_system].if_bpf_f0; + ascot2e_write_regs(priv, 0x49, data, 2); + /* + * Tune now + * RFAGC fast mode / RFAGC auto control enable + * (set bit[7], bit[5:4] only) + * vco_cal = 1, set MIX_OL_CPU_EN + */ + ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0); + /* Logic wake up, CPU wake up */ + data[0] = 0xc4; + data[1] = 0x40; + ascot2e_write_regs(priv, 0x03, data, 2); + /* 0x10 - 0x14 */ + data[0] = (u8)(frequency & 0xFF); /* 0x10: FRF_L */ + data[1] = (u8)((frequency >> 8) & 0xFF); /* 0x11: FRF_M */ + data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */ + /* 0x12: BW (bit[5:4]) */ + data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4); + data[3] = 0xFF; /* 0x13: VCO calibration enable */ + data[4] = 0xFF; /* 0x14: Analog block enable */ + /* Tune (Burst write) */ + ascot2e_write_regs(priv, 0x10, data, 5); + msleep(50); + /* CPU deep sleep */ + ascot2e_write_reg(priv, 0x04, 0x00); + /* Logic sleep */ + ascot2e_write_reg(priv, 0x03, 0xC0); + /* RFAGC normal mode (set bit[5:4] only) */ + ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30); + priv->frequency = frequency; + return 0; +} + +static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static const struct dvb_tuner_ops ascot2e_tuner_ops = { + .info = { + .name = "Sony ASCOT2E", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1200 * MHz, + .frequency_step_hz = 25 * kHz, + }, + .init = ascot2e_init, + .release = ascot2e_release, + .sleep = ascot2e_sleep, + .set_params = ascot2e_set_params, + .get_frequency = ascot2e_get_frequency, +}; + +struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c) +{ + u8 data[4]; + struct ascot2e_priv *priv = NULL; + + priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* 16 MHz xTal frequency */ + data[0] = 16; + /* VCO current setting */ + data[1] = 0x06; + /* Logic wake up, CPU boot */ + data[2] = 0xC4; + data[3] = 0x40; + ascot2e_write_regs(priv, 0x01, data, 4); + /* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */ + data[0] = 0x10; + data[1] = 0x3F; + data[2] = 0x25; + ascot2e_write_regs(priv, 0x22, data, 3); + /* PLL mode setting */ + ascot2e_write_reg(priv, 0x28, 0x1e); + /* RSSI setting */ + ascot2e_write_reg(priv, 0x59, 0x04); + /* TODO check CPU HW error state here */ + msleep(80); + /* Xtal oscillator current control setting */ + ascot2e_write_reg(priv, 0x4c, 0x01); + /* XOSC_SEL=100uA */ + ascot2e_write_reg(priv, 0x07, 0x04); + /* CPU deep sleep */ + ascot2e_write_reg(priv, 0x04, 0x00); + /* Logic sleep */ + ascot2e_write_reg(priv, 0x03, 0xc0); + /* Power save setting */ + data[0] = 0x00; + data[1] = 0x04; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x01); + priv->state = STATE_SLEEP; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony ASCOT2E attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL_GPL(ascot2e_attach); + +MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ascot2e.h b/drivers/media/dvb-frontends/ascot2e.h new file mode 100644 index 0000000000..d86b3de85c --- /dev/null +++ b/drivers/media/dvb-frontends/ascot2e.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ascot2e.h + * + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#ifndef __DVB_ASCOT2E_H__ +#define __DVB_ASCOT2E_H__ + +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +/** + * struct ascot2e_config - the configuration of Ascot2E tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct ascot2e_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); +}; + +#if IS_REACHABLE(CONFIG_DVB_ASCOT2E) +/** + * ascot2e_attach - Attach an ascot2e tuner + * + * @fe: frontend to be attached + * @config: pointer to &struct ascot2e_config with tuner configuration. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c new file mode 100644 index 0000000000..778c865085 --- /dev/null +++ b/drivers/media/dvb-frontends/atbm8830.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> + */ + +#include <asm/div64.h> +#include <media/dvb_frontend.h> + +#include "atbm8830.h" +#include "atbm8830_priv.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "atbm8830: " args); \ + } while (0) + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) +{ + int ret = 0; + u8 dev_addr; + u8 buf1[] = { reg >> 8, reg & 0xFF }; + u8 buf2[] = { data }; + struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; + struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; + + dev_addr = priv->config->demod_address; + msg1.addr = dev_addr; + msg2.addr = dev_addr; + + if (debug >= 2) + dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg1, 1); + if (ret != 1) + return -EIO; + + ret = i2c_transfer(priv->i2c, &msg2, 1); + return (ret != 1) ? -EIO : 0; +} + +static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) +{ + int ret; + u8 dev_addr; + + u8 buf1[] = { reg >> 8, reg & 0xFF }; + u8 buf2[] = { 0 }; + struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; + struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; + + dev_addr = priv->config->demod_address; + msg1.addr = dev_addr; + msg2.addr = dev_addr; + + ret = i2c_transfer(priv->i2c, &msg1, 1); + if (ret != 1) { + dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret); + return -EIO; + } + + ret = i2c_transfer(priv->i2c, &msg2, 1); + if (ret != 1) + return -EIO; + + *p_data = buf2[0]; + if (debug >= 2) + dprintk("%s: reg=0x%04X, data=0x%02X\n", + __func__, reg, buf2[0]); + + return 0; +} + +/* Lock register latch so that multi-register read is atomic */ +static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) +{ + return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); +} + +static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +{ + u32 val; + u64 t; + + /* 0x100000 * freq / 30.4MHz */ + t = (u64)0x100000 * freq; + do_div(t, 30400); + val = t; + + atbm8830_write_reg(priv, REG_OSC_CLK, val); + atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); + atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); + + return 0; +} + +static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +{ + + u32 fs = priv->config->osc_clk_freq; + u64 t; + u32 val; + u8 dat; + + if (freq != 0) { + /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ + t = (u64) 2 * 31416 * (freq - fs); + t <<= 22; + do_div(t, fs); + do_div(t, 1000); + val = t; + + atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); + atbm8830_write_reg(priv, REG_IF_FREQ, val); + atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); + atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); + + atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); + dat &= 0xFC; + atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); + } else { + /* Zero IF */ + atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); + + atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); + dat &= 0xFC; + dat |= 0x02; + atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); + + if (priv->config->zif_swap_iq) + atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); + else + atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); + } + + return 0; +} + +static int is_locked(struct atbm_state *priv, u8 *locked) +{ + u8 status; + + atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); + + if (locked != NULL) + *locked = (status == 1); + return 0; +} + +static int set_agc_config(struct atbm_state *priv, + u8 min, u8 max, u8 hold_loop) +{ + /* no effect if both min and max are zero */ + if (!min && !max) + return 0; + + atbm8830_write_reg(priv, REG_AGC_MIN, min); + atbm8830_write_reg(priv, REG_AGC_MAX, max); + atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); + + return 0; +} + +static int set_static_channel_mode(struct atbm_state *priv) +{ + int i; + + for (i = 0; i < 5; i++) + atbm8830_write_reg(priv, 0x099B + i, 0x08); + + atbm8830_write_reg(priv, 0x095B, 0x7F); + atbm8830_write_reg(priv, 0x09CB, 0x01); + atbm8830_write_reg(priv, 0x09CC, 0x7F); + atbm8830_write_reg(priv, 0x09CD, 0x7F); + atbm8830_write_reg(priv, 0x0E01, 0x20); + + /* For single carrier */ + atbm8830_write_reg(priv, 0x0B03, 0x0A); + atbm8830_write_reg(priv, 0x0935, 0x10); + atbm8830_write_reg(priv, 0x0936, 0x08); + atbm8830_write_reg(priv, 0x093E, 0x08); + atbm8830_write_reg(priv, 0x096E, 0x06); + + /* frame_count_max0 */ + atbm8830_write_reg(priv, 0x0B09, 0x00); + /* frame_count_max1 */ + atbm8830_write_reg(priv, 0x0B0A, 0x08); + + return 0; +} + +static int set_ts_config(struct atbm_state *priv) +{ + const struct atbm8830_config *cfg = priv->config; + + /*Set parallel/serial ts mode*/ + atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); + atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); + /*Set ts sampling edge*/ + atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, + cfg->ts_sampling_edge ? 1 : 0); + /*Set ts clock freerun*/ + atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, + cfg->ts_clk_gated ? 0 : 1); + + return 0; +} + +static int atbm8830_init(struct dvb_frontend *fe) +{ + struct atbm_state *priv = fe->demodulator_priv; + const struct atbm8830_config *cfg = priv->config; + + /*Set oscillator frequency*/ + set_osc_freq(priv, cfg->osc_clk_freq); + + /*Set IF frequency*/ + set_if_freq(priv, cfg->if_freq); + + /*Set AGC Config*/ + set_agc_config(priv, cfg->agc_min, cfg->agc_max, + cfg->agc_hold_loop); + + /*Set static channel mode*/ + set_static_channel_mode(priv); + + set_ts_config(priv); + /*Turn off DSP reset*/ + atbm8830_write_reg(priv, 0x000A, 0); + + /*SW version test*/ + atbm8830_write_reg(priv, 0x020C, 11); + + /* Run */ + atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); + + return 0; +} + + +static void atbm8830_release(struct dvb_frontend *fe) +{ + struct atbm_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + + kfree(state); +} + +static int atbm8830_set_fe(struct dvb_frontend *fe) +{ + struct atbm_state *priv = fe->demodulator_priv; + int i; + u8 locked = 0; + dprintk("%s\n", __func__); + + /* set frequency */ + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* start auto lock */ + for (i = 0; i < 10; i++) { + mdelay(100); + dprintk("Try %d\n", i); + is_locked(priv, &locked); + if (locked != 0) { + dprintk("ATBM8830 locked!\n"); + break; + } + } + + return 0; +} + +static int atbm8830_get_fe(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + dprintk("%s\n", __func__); + + /* TODO: get real readings from device */ + /* inversion status */ + c->inversion = INVERSION_OFF; + + /* bandwidth */ + c->bandwidth_hz = 8000000; + + c->code_rate_HP = FEC_AUTO; + c->code_rate_LP = FEC_AUTO; + + c->modulation = QAM_AUTO; + + /* transmission mode */ + c->transmission_mode = TRANSMISSION_MODE_AUTO; + + /* guard interval */ + c->guard_interval = GUARD_INTERVAL_AUTO; + + /* hierarchy */ + c->hierarchy = HIERARCHY_NONE; + + return 0; +} + +static int atbm8830_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 0; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int atbm8830_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) +{ + struct atbm_state *priv = fe->demodulator_priv; + u8 locked = 0; + u8 agc_locked = 0; + + dprintk("%s\n", __func__); + *fe_status = 0; + + is_locked(priv, &locked); + if (locked) { + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } + dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); + + atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); + dprintk("AGC Lock: %d\n", agc_locked); + + return 0; +} + +static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct atbm_state *priv = fe->demodulator_priv; + u32 frame_err; + u8 t; + + dprintk("%s\n", __func__); + + atbm8830_reglatch_lock(priv, 1); + + atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); + frame_err = t & 0x7F; + frame_err <<= 8; + atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); + frame_err |= t; + + atbm8830_reglatch_lock(priv, 0); + + *ber = frame_err * 100 / 32767; + + dprintk("%s: ber=0x%x\n", __func__, *ber); + return 0; +} + +static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +{ + struct atbm_state *priv = fe->demodulator_priv; + u32 pwm; + u8 t; + + dprintk("%s\n", __func__); + atbm8830_reglatch_lock(priv, 1); + + atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); + pwm = t & 0x03; + pwm <<= 8; + atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); + pwm |= t; + + atbm8830_reglatch_lock(priv, 0); + + dprintk("AGC PWM = 0x%02X\n", pwm); + pwm = 0x400 - pwm; + + *signal = pwm * 0x10000 / 0x400; + + return 0; +} + +static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + dprintk("%s\n", __func__); + *snr = 0; + return 0; +} + +static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + dprintk("%s\n", __func__); + *ucblocks = 0; + return 0; +} + +static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct atbm_state *priv = fe->demodulator_priv; + + return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); +} + +static const struct dvb_frontend_ops atbm8830_ops = { + .delsys = { SYS_DTMB }, + .info = { + .name = "AltoBeam ATBM8830/8831 DMB-TH", + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, + .caps = + FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = atbm8830_release, + + .init = atbm8830_init, + .sleep = NULL, + .write = NULL, + .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, + + .set_frontend = atbm8830_set_fe, + .get_frontend = atbm8830_get_fe, + .get_tune_settings = atbm8830_get_tune_settings, + + .read_status = atbm8830_read_status, + .read_ber = atbm8830_read_ber, + .read_signal_strength = atbm8830_read_signal_strength, + .read_snr = atbm8830_read_snr, + .read_ucblocks = atbm8830_read_ucblocks, +}; + +struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c) +{ + struct atbm_state *priv = NULL; + u8 data = 0; + + dprintk("%s()\n", __func__); + + if (config == NULL || i2c == NULL) + return NULL; + + priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); + if (priv == NULL) + goto error_out; + + priv->config = config; + priv->i2c = i2c; + + /* check if the demod is there */ + if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { + dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", + __func__, priv->config->demod_address); + goto error_out; + } + dprintk("atbm8830 chip id: 0x%02X\n", data); + + memcpy(&priv->frontend.ops, &atbm8830_ops, + sizeof(struct dvb_frontend_ops)); + priv->frontend.demodulator_priv = priv; + + atbm8830_init(&priv->frontend); + + atbm8830_i2c_gate_ctrl(&priv->frontend, 1); + + return &priv->frontend; + +error_out: + dprintk("%s() error_out\n", __func__); + kfree(priv); + return NULL; + +} +EXPORT_SYMBOL_GPL(atbm8830_attach); + +MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); +MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/atbm8830.h b/drivers/media/dvb-frontends/atbm8830.h new file mode 100644 index 0000000000..7cb0d982ff --- /dev/null +++ b/drivers/media/dvb-frontends/atbm8830.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> + */ + +#ifndef __ATBM8830_H__ +#define __ATBM8830_H__ + +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#define ATBM8830_PROD_8830 0 +#define ATBM8830_PROD_8831 1 + +struct atbm8830_config { + + /* product type */ + u8 prod; + + /* the demodulator's i2c address */ + u8 demod_address; + + /* parallel or serial transport stream */ + u8 serial_ts; + + /* transport stream clock output only when receiving valid stream */ + u8 ts_clk_gated; + + /* Decoder sample TS data at rising edge of clock */ + u8 ts_sampling_edge; + + /* Oscillator clock frequency */ + u32 osc_clk_freq; /* in kHz */ + + /* IF frequency */ + u32 if_freq; /* in kHz */ + + /* Swap I/Q for zero IF */ + u8 zif_swap_iq; + + /* Tuner AGC settings */ + u8 agc_min; + u8 agc_max; + u8 agc_hold_loop; +}; + +#if IS_REACHABLE(CONFIG_DVB_ATBM8830) +extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ATBM8830 */ + +#endif /* __ATBM8830_H__ */ diff --git a/drivers/media/dvb-frontends/atbm8830_priv.h b/drivers/media/dvb-frontends/atbm8830_priv.h new file mode 100644 index 0000000000..a8cd39196d --- /dev/null +++ b/drivers/media/dvb-frontends/atbm8830_priv.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> + */ + +#ifndef __ATBM8830_PRIV_H +#define __ATBM8830_PRIV_H + +struct atbm_state { + struct i2c_adapter *i2c; + /* configuration settings */ + const struct atbm8830_config *config; + struct dvb_frontend frontend; +}; + +#define REG_CHIP_ID 0x0000 +#define REG_TUNER_BASEBAND 0x0001 +#define REG_DEMOD_RUN 0x0004 +#define REG_DSP_RESET 0x0005 +#define REG_RAM_RESET 0x0006 +#define REG_ADC_RESET 0x0007 +#define REG_TSPORT_RESET 0x0008 +#define REG_BLKERR_POL 0x000C +#define REG_I2C_GATE 0x0103 +#define REG_TS_SAMPLE_EDGE 0x0301 +#define REG_TS_PKT_LEN_204 0x0302 +#define REG_TS_PKT_LEN_AUTO 0x0303 +#define REG_TS_SERIAL 0x0305 +#define REG_TS_CLK_FREERUN 0x0306 +#define REG_TS_VALID_MODE 0x0307 +#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */ + +#define REG_TS_ERRBIT_USE 0x030C +#define REG_LOCK_STATUS 0x030D +#define REG_ADC_CONFIG 0x0602 +#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */ +#define REG_DETECTED_PN_MODE 0x082D +#define REG_READ_LATCH 0x084D +#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */ +#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */ +#define REG_BYPASS_CCI 0x0A06 +#define REG_ANALOG_LUMA_DETECTED 0x0A25 +#define REG_ANALOG_AUDIO_DETECTED 0x0A26 +#define REG_ANALOG_CHROMA_DETECTED 0x0A39 +#define REG_FRAME_ERR_CNT 0x0B04 +#define REG_USE_EXT_ADC 0x0C00 +#define REG_SWAP_I_Q 0x0C01 +#define REG_TPS_MANUAL 0x0D01 +#define REG_TPS_CONFIG 0x0D02 +#define REG_BYPASS_DEINTERLEAVER 0x0E00 +#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */ +#define REG_AGC_MIN 0x1020 +#define REG_AGC_MAX 0x1023 +#define REG_AGC_LOCK 0x1027 +#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */ +#define REG_AGC_HOLD_LOOP 0x1031 + +#endif + diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h new file mode 100644 index 0000000000..72e3328572 --- /dev/null +++ b/drivers/media/dvb-frontends/au8522.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> + + +*/ + +#ifndef __AU8522_H__ +#define __AU8522_H__ + +#include <linux/dvb/frontend.h> + +enum au8522_if_freq { + AU8522_IF_6MHZ = 0, + AU8522_IF_4MHZ, + AU8522_IF_3_25MHZ, +}; + +struct au8522_led_config { + u16 vsb8_strong; + u16 qam64_strong; + u16 qam256_strong; + + u16 gpio_output; + /* unset hi bits, set low bits */ + u16 gpio_output_enable; + u16 gpio_output_disable; + + u16 gpio_leds; + u8 *led_states; + unsigned int num_led_states; +}; + +struct au8522_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* Return lock status based on tuner lock, or demod lock */ +#define AU8522_TUNERLOCKING 0 +#define AU8522_DEMODLOCKING 1 + u8 status_mode; + + struct au8522_led_config *led_cfg; + + enum au8522_if_freq vsb_if; + enum au8522_if_freq qam_if; +}; + +#if IS_REACHABLE(CONFIG_DVB_AU8522_DTV) +extern struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_AU8522 */ + +/* Other modes may need to be added later */ +enum au8522_video_input { + AU8522_COMPOSITE_CH1 = 1, + AU8522_COMPOSITE_CH2, + AU8522_COMPOSITE_CH3, + AU8522_COMPOSITE_CH4, + AU8522_COMPOSITE_CH4_SIF, + AU8522_SVIDEO_CH13, + AU8522_SVIDEO_CH24, +}; + +enum au8522_audio_input { + AU8522_AUDIO_NONE, + AU8522_AUDIO_SIF, +}; +#endif /* __AU8522_H__ */ diff --git a/drivers/media/dvb-frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c new file mode 100644 index 0000000000..b9e77e86f4 --- /dev/null +++ b/drivers/media/dvb-frontends/au8522_common.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> + Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org> + Copyright (C) 2005-2008 Auvitek International, Ltd. + Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org> + + +*/ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> +#include "au8522_priv.h" + +static int debug; + +#define dprintk(arg...)\ + do { if (debug)\ + printk(arg);\ + } while (0) + +/* Despite the name "hybrid_tuner", the framework works just as well for + hybrid demodulators as well... */ +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(au8522_list_mutex); + +/* 16 bit registers, 8 bit values */ +int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) +{ + int ret; + u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; + + struct i2c_msg msg = { .addr = state->config.demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} +EXPORT_SYMBOL(au8522_writereg); + +u8 au8522_readreg(struct au8522_state *state, u16 reg) +{ + int ret; + u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, + .buf = b0, .len = 2 }, + { .addr = state->config.demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} +EXPORT_SYMBOL(au8522_readreg); + +int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're being asked to manage the gate even though we're + not in digital mode. This can occur if we get switched + over to analog mode before the dvb_frontend kernel thread + has completely shutdown */ + return 0; + } + + if (enable) + return au8522_writereg(state, 0x106, 1); + else + return au8522_writereg(state, 0x106, 0); +} +EXPORT_SYMBOL(au8522_i2c_gate_ctrl); + +int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return au8522_writereg(state, 0x106, 1); + else + return au8522_writereg(state, 0x106, 0); +} +EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl); + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, + u8 client_address) +{ + int ret; + + mutex_lock(&au8522_list_mutex); + ret = hybrid_tuner_request_state(struct au8522_state, (*state), + hybrid_tuner_instance_list, + i2c, client_address, "au8522"); + mutex_unlock(&au8522_list_mutex); + + return ret; +} +EXPORT_SYMBOL(au8522_get_state); + +void au8522_release_state(struct au8522_state *state) +{ + mutex_lock(&au8522_list_mutex); + if (state != NULL) + hybrid_tuner_release_state(state); + mutex_unlock(&au8522_list_mutex); +} +EXPORT_SYMBOL(au8522_release_state); + +static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) +{ + struct au8522_led_config *led_config = state->config.led_cfg; + u8 val; + + /* bail out if we can't control an LED */ + if (!led_config || !led_config->gpio_output || + !led_config->gpio_output_enable || !led_config->gpio_output_disable) + return 0; + + val = au8522_readreg(state, 0x4000 | + (led_config->gpio_output & ~0xc000)); + if (onoff) { + /* enable GPIO output */ + val &= ~((led_config->gpio_output_enable >> 8) & 0xff); + val |= (led_config->gpio_output_enable & 0xff); + } else { + /* disable GPIO output */ + val &= ~((led_config->gpio_output_disable >> 8) & 0xff); + val |= (led_config->gpio_output_disable & 0xff); + } + return au8522_writereg(state, 0x8000 | + (led_config->gpio_output & ~0xc000), val); +} + +/* led = 0 | off + * led = 1 | signal ok + * led = 2 | signal strong + * led < 0 | only light led if leds are currently off + */ +int au8522_led_ctrl(struct au8522_state *state, int led) +{ + struct au8522_led_config *led_config = state->config.led_cfg; + int i, ret = 0; + + /* bail out if we can't control an LED */ + if (!led_config || !led_config->gpio_leds || + !led_config->num_led_states || !led_config->led_states) + return 0; + + if (led < 0) { + /* if LED is already lit, then leave it as-is */ + if (state->led_state) + return 0; + else + led *= -1; + } + + /* toggle LED if changing state */ + if (state->led_state != led) { + u8 val; + + dprintk("%s: %d\n", __func__, led); + + au8522_led_gpio_enable(state, 1); + + val = au8522_readreg(state, 0x4000 | + (led_config->gpio_leds & ~0xc000)); + + /* start with all leds off */ + for (i = 0; i < led_config->num_led_states; i++) + val &= ~led_config->led_states[i]; + + /* set selected LED state */ + if (led < led_config->num_led_states) + val |= led_config->led_states[led]; + else if (led_config->num_led_states) + val |= + led_config->led_states[led_config->num_led_states - 1]; + + ret = au8522_writereg(state, 0x8000 | + (led_config->gpio_leds & ~0xc000), val); + if (ret < 0) + return ret; + + state->led_state = led; + + if (led == 0) + au8522_led_gpio_enable(state, 0); + } + + return 0; +} +EXPORT_SYMBOL(au8522_led_ctrl); + +int au8522_init(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + state->operational_mode = AU8522_DIGITAL_MODE; + + /* Clear out any state associated with the digital side of the + chip, so that when it gets powered back up it won't think + that it is already tuned */ + state->current_frequency = 0; + state->current_modulation = VSB_8; + + au8522_writereg(state, 0xa4, 1 << 5); + + au8522_i2c_gate_ctrl(fe, 1); + + return 0; +} +EXPORT_SYMBOL(au8522_init); + +int au8522_sleep(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + /* Only power down if the digital side is currently using the chip */ + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're not in one of the expected power modes, which means + that the DVB thread is probably telling us to go to sleep + even though the analog frontend has already started using + the chip. So ignore the request */ + return 0; + } + + /* turn off led */ + au8522_led_ctrl(state, 0); + + /* Power down the chip */ + au8522_writereg(state, 0xa4, 1 << 5); + + state->current_frequency = 0; + + return 0; +} +EXPORT_SYMBOL(au8522_sleep); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c new file mode 100644 index 0000000000..acc27376c2 --- /dev/null +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder + * + * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org> + * Copyright (C) 2005-2008 Auvitek International, Ltd. + */ + +/* Developer notes: + * + * Enough is implemented here for CVBS and S-Video inputs, but the actual + * analog demodulator code isn't implemented (not needed for xc5000 since it + * has its own demodulator and outputs CVBS) + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include "au8522.h" +#include "au8522_priv.h" + +MODULE_AUTHOR("Devin Heitmueller"); +MODULE_LICENSE("GPL"); + +static int au8522_analog_debug; + + +module_param_named(analog_debug, au8522_analog_debug, int, 0644); + +MODULE_PARM_DESC(analog_debug, + "Analog debugging messages [0=Off (default) 1=On]"); + +struct au8522_register_config { + u16 reg_name; + u8 reg_val[8]; +}; + + +/* Video Decoder Filter Coefficients + The values are as follows from left to right + 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13" +*/ +static const struct au8522_register_config filter_coef[] = { + {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} }, + {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} }, + {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} }, + {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} }, + {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} }, + {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} }, + {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} }, + {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} }, + {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} }, + {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} }, + {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} }, + {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} }, + {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} }, + {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} }, + {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} }, + {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} }, + {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} }, + {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} }, + {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} }, + {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} }, + {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} }, + {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} }, + +}; +#define NUM_FILTER_COEF (sizeof(filter_coef)\ + / sizeof(struct au8522_register_config)) + + +/* Registers 0x060b through 0x0652 are the LP Filter coefficients + The values are as follows from left to right + 0="SIF" 1="ATVRF/ATVRF13" + Note: the "ATVRF/ATVRF13" mode has never been tested +*/ +static const struct au8522_register_config lpfilter_coef[] = { + {0x060b, {0x21, 0x0b} }, + {0x060c, {0xad, 0xad} }, + {0x060d, {0x70, 0xf0} }, + {0x060e, {0xea, 0xe9} }, + {0x060f, {0xdd, 0xdd} }, + {0x0610, {0x08, 0x64} }, + {0x0611, {0x60, 0x60} }, + {0x0612, {0xf8, 0xb2} }, + {0x0613, {0x01, 0x02} }, + {0x0614, {0xe4, 0xb4} }, + {0x0615, {0x19, 0x02} }, + {0x0616, {0xae, 0x2e} }, + {0x0617, {0xee, 0xc5} }, + {0x0618, {0x56, 0x56} }, + {0x0619, {0x30, 0x58} }, + {0x061a, {0xf9, 0xf8} }, + {0x061b, {0x24, 0x64} }, + {0x061c, {0x07, 0x07} }, + {0x061d, {0x30, 0x30} }, + {0x061e, {0xa9, 0xed} }, + {0x061f, {0x09, 0x0b} }, + {0x0620, {0x42, 0xc2} }, + {0x0621, {0x1d, 0x2a} }, + {0x0622, {0xd6, 0x56} }, + {0x0623, {0x95, 0x8b} }, + {0x0624, {0x2b, 0x2b} }, + {0x0625, {0x30, 0x24} }, + {0x0626, {0x3e, 0x3e} }, + {0x0627, {0x62, 0xe2} }, + {0x0628, {0xe9, 0xf5} }, + {0x0629, {0x99, 0x19} }, + {0x062a, {0xd4, 0x11} }, + {0x062b, {0x03, 0x04} }, + {0x062c, {0xb5, 0x85} }, + {0x062d, {0x1e, 0x20} }, + {0x062e, {0x2a, 0xea} }, + {0x062f, {0xd7, 0xd2} }, + {0x0630, {0x15, 0x15} }, + {0x0631, {0xa3, 0xa9} }, + {0x0632, {0x1f, 0x1f} }, + {0x0633, {0xf9, 0xd1} }, + {0x0634, {0xc0, 0xc3} }, + {0x0635, {0x4d, 0x8d} }, + {0x0636, {0x21, 0x31} }, + {0x0637, {0x83, 0x83} }, + {0x0638, {0x08, 0x8c} }, + {0x0639, {0x19, 0x19} }, + {0x063a, {0x45, 0xa5} }, + {0x063b, {0xef, 0xec} }, + {0x063c, {0x8a, 0x8a} }, + {0x063d, {0xf4, 0xf6} }, + {0x063e, {0x8f, 0x8f} }, + {0x063f, {0x44, 0x0c} }, + {0x0640, {0xef, 0xf0} }, + {0x0641, {0x66, 0x66} }, + {0x0642, {0xcc, 0xd2} }, + {0x0643, {0x41, 0x41} }, + {0x0644, {0x63, 0x93} }, + {0x0645, {0x8e, 0x8e} }, + {0x0646, {0xa2, 0x42} }, + {0x0647, {0x7b, 0x7b} }, + {0x0648, {0x04, 0x04} }, + {0x0649, {0x00, 0x00} }, + {0x064a, {0x40, 0x40} }, + {0x064b, {0x8c, 0x98} }, + {0x064c, {0x00, 0x00} }, + {0x064d, {0x63, 0xc3} }, + {0x064e, {0x04, 0x04} }, + {0x064f, {0x20, 0x20} }, + {0x0650, {0x00, 0x00} }, + {0x0651, {0x40, 0x40} }, + {0x0652, {0x01, 0x01} }, +}; +#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\ + / sizeof(struct au8522_register_config)) + +static inline struct au8522_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct au8522_state, sd); +} + +static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo) +{ + int i; + int filter_coef_type; + + /* Provide reasonable defaults for picture tuning values */ + au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07); + au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed); + au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79); + au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80); + au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80); + au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00); + au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00); + + /* Other decoder registers */ + au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); + + if (is_svideo) + au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04); + else + au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00); + + au8522_writereg(state, AU8522_TVDEC_PGA_REG012H, + AU8522_TVDEC_PGA_REG012H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H, + AU8522_TVDEC_COMB_MODE_REG015H_CVBS); + au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H, + AU8522_TVDED_DBG_MODE_REG060H_CVBS); + + if (state->std == V4L2_STD_PAL_M) { + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, + AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO); + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, + AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_PAL_M); + } else { + /* NTSC */ + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, + AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN); + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, + AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC); + } + au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H, + AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS); + au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H, + AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H, + AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H, + AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H, + AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H, + AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H, + AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH, + AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH, + AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS); + if (is_svideo) { + au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, + AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO); + au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, + AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO); + } else { + au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, + AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, + AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS); + } + au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH, + AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS); + au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH, + AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H, + AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS); + au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS); + au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS); + au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H, + AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS); + au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS); + au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS); + au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H, + AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS); + au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H, + AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS); + au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H, + AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS); + au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH, + AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS); + au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH, + AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS); + au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H, + AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS); + au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H, + AU8522_TOREGAAGC_REG0E5H_CVBS); + au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS); + + /* + * Despite what the table says, for the HVR-950q we still need + * to be in CVBS mode for the S-Video input (reason unknown). + */ + /* filter_coef_type = 3; */ + filter_coef_type = 5; + + /* Load the Video Decoder Filter Coefficients */ + for (i = 0; i < NUM_FILTER_COEF; i++) { + au8522_writereg(state, filter_coef[i].reg_name, + filter_coef[i].reg_val[filter_coef_type]); + } + + /* It's not clear what these registers are for, but they are always + set to the same value regardless of what mode we're in */ + au8522_writereg(state, AU8522_REG42EH, 0x87); + au8522_writereg(state, AU8522_REG42FH, 0xa2); + au8522_writereg(state, AU8522_REG430H, 0xbf); + au8522_writereg(state, AU8522_REG431H, 0xcb); + au8522_writereg(state, AU8522_REG432H, 0xa1); + au8522_writereg(state, AU8522_REG433H, 0x41); + au8522_writereg(state, AU8522_REG434H, 0x88); + au8522_writereg(state, AU8522_REG435H, 0xc2); + au8522_writereg(state, AU8522_REG436H, 0x3c); +} + +static void au8522_setup_cvbs_mode(struct au8522_state *state, u8 input_mode) +{ + /* here we're going to try the pre-programmed route */ + au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, + AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS); + + /* PGA in automatic mode */ + au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); + + /* Enable clamping control */ + au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); + + au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); + + setup_decoder_defaults(state, false); + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +} + +static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state, + u8 input_mode) +{ + /* here we're going to try the pre-programmed route */ + au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, + AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS); + + /* It's not clear why we have to have the PGA in automatic mode while + enabling clamp control, but it's what Windows does */ + au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); + + /* Enable clamping control */ + au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e); + + /* Disable automatic PGA (since the CVBS is coming from the tuner) */ + au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10); + + /* Set input mode to CVBS on channel 4 with SIF audio input enabled */ + au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); + + setup_decoder_defaults(state, false); + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +} + +static void au8522_setup_svideo_mode(struct au8522_state *state, + u8 input_mode) +{ + au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, + AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO); + + /* Set input to Y on Channe1, C on Channel 3 */ + au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); + + /* PGA in automatic mode */ + au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); + + /* Enable clamping control */ + au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); + + setup_decoder_defaults(state, true); + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +} + +/* ----------------------------------------------------------------------- */ + +static void disable_audio_input(struct au8522_state *state) +{ + au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); + au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); + au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04); + au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02); + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO); +} + +/* 0=disable, 1=SIF */ +static void set_audio_input(struct au8522_state *state) +{ + int aud_input = state->aud_input; + int i; + + /* Note that this function needs to be used in conjunction with setting + the input routing via register 0x81 */ + + if (aud_input == AU8522_AUDIO_NONE) { + disable_audio_input(state); + return; + } + + if (aud_input != AU8522_AUDIO_SIF) { + /* The caller asked for a mode we don't currently support */ + printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n", + aud_input); + return; + } + + /* Load the Audio Decoder Filter Coefficients */ + for (i = 0; i < NUM_LPFILTER_COEF; i++) { + au8522_writereg(state, lpfilter_coef[i].reg_name, + lpfilter_coef[i].reg_val[0]); + } + + /* Set the volume */ + au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); + au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); + au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff); + + /* Not sure what this does */ + au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO); + + /* Setup the audio mode to stereo DBX */ + au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82); + msleep(70); + + /* Start the audio processing module */ + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d); + + /* Set the audio frequency to 48 KHz */ + au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03); + + /* Set the I2S parameters (WS, LSB, mode, sample rate */ + au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2); + + /* Enable the I2S output */ + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09); +} + +/* ----------------------------------------------------------------------- */ + +static int au8522_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct au8522_state *state = + container_of(ctrl->handler, struct au8522_state, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, + ctrl->val - 128); + break; + case V4L2_CID_CONTRAST: + au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, + ctrl->val); + break; + case V4L2_CID_SATURATION: + au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, + ctrl->val); + au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, + ctrl->val); + break; + case V4L2_CID_HUE: + au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, + ctrl->val >> 8); + au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, + ctrl->val & 0xFF); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int au8522_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct au8522_state *state = to_state(sd); + + reg->val = au8522_readreg(state, reg->reg & 0xffff); + return 0; +} + +static int au8522_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct au8522_state *state = to_state(sd); + + au8522_writereg(state, reg->reg, reg->val & 0xff); + return 0; +} +#endif + +static void au8522_video_set(struct au8522_state *state) +{ + u8 input_mode; + + au8522_writereg(state, 0xa4, 1 << 5); + + switch (state->vid_input) { + case AU8522_COMPOSITE_CH1: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH1; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_COMPOSITE_CH2: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH2; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_COMPOSITE_CH3: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH3; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_COMPOSITE_CH4: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH4; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_SVIDEO_CH13: + input_mode = AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13; + au8522_setup_svideo_mode(state, input_mode); + break; + case AU8522_SVIDEO_CH24: + input_mode = AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24; + au8522_setup_svideo_mode(state, input_mode); + break; + default: + case AU8522_COMPOSITE_CH4_SIF: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF; + au8522_setup_cvbs_tuner_mode(state, input_mode); + break; + } +} + +static int au8522_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct au8522_state *state = to_state(sd); + + if (enable) { + /* + * Clear out any state associated with the digital side of the + * chip, so that when it gets powered back up it won't think + * that it is already tuned + */ + state->current_frequency = 0; + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + 0x01); + msleep(10); + + au8522_video_set(state); + set_audio_input(state); + + state->operational_mode = AU8522_ANALOG_MODE; + } else { + /* This does not completely power down the device + (it only reduces it from around 140ma to 80ma) */ + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + 1 << 5); + state->operational_mode = AU8522_SUSPEND_MODE; + } + return 0; +} + +static int au8522_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct au8522_state *state = to_state(sd); + + switch (input) { + case AU8522_COMPOSITE_CH1: + case AU8522_SVIDEO_CH13: + case AU8522_COMPOSITE_CH4_SIF: + state->vid_input = input; + break; + default: + printk(KERN_ERR "au8522 mode not currently supported\n"); + return -EINVAL; + } + + if (state->operational_mode == AU8522_ANALOG_MODE) + au8522_video_set(state); + + return 0; +} + +static int au8522_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct au8522_state *state = to_state(sd); + + if ((std & (V4L2_STD_PAL_M | V4L2_STD_NTSC_M)) == 0) + return -EINVAL; + + state->std = std; + + if (state->operational_mode == AU8522_ANALOG_MODE) + au8522_video_set(state); + + return 0; +} + +static int au8522_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct au8522_state *state = to_state(sd); + + state->aud_input = input; + + if (state->operational_mode == AU8522_ANALOG_MODE) + set_audio_input(state); + + return 0; +} + +static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + int val = 0; + struct au8522_state *state = to_state(sd); + u8 lock_status; + u8 pll_status; + + /* Interrogate the decoder to see if we are getting a real signal */ + lock_status = au8522_readreg(state, 0x00); + pll_status = au8522_readreg(state, 0x7e); + if ((lock_status == 0xa2) && (pll_status & 0x10)) + vt->signal = 0xffff; + else + vt->signal = 0x00; + + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + + val = V4L2_TUNER_SUB_MONO; + vt->rxsubchans = val; + vt->audmode = V4L2_TUNER_MODE_STEREO; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops au8522_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = au8522_g_register, + .s_register = au8522_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = { + .g_tuner = au8522_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops au8522_audio_ops = { + .s_routing = au8522_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops au8522_video_ops = { + .s_routing = au8522_s_video_routing, + .s_stream = au8522_s_stream, + .s_std = au8522_s_std, +}; + +static const struct v4l2_subdev_ops au8522_ops = { + .core = &au8522_core_ops, + .tuner = &au8522_tuner_ops, + .audio = &au8522_audio_ops, + .video = &au8522_video_ops, +}; + +static const struct v4l2_ctrl_ops au8522_ctrl_ops = { + .s_ctrl = au8522_s_ctrl, +}; + +/* ----------------------------------------------------------------------- */ + +static int au8522_probe(struct i2c_client *client) +{ + struct au8522_state *state; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + int instance; +#ifdef CONFIG_MEDIA_CONTROLLER + int ret; +#endif + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + return -EIO; + } + + /* allocate memory for the internal state */ + instance = au8522_get_state(&state, client->adapter, client->addr); + switch (instance) { + case 0: + printk(KERN_ERR "au8522_decoder allocation failed\n"); + return -EIO; + case 1: + /* new demod instance */ + printk(KERN_INFO "au8522_decoder creating new instance...\n"); + break; + default: + /* existing demod instance */ + printk(KERN_INFO "au8522_decoder attach existing instance.\n"); + break; + } + + state->config.demod_address = 0x8e >> 1; + state->i2c = client->adapter; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &au8522_ops); +#if defined(CONFIG_MEDIA_CONTROLLER) + + state->pads[AU8522_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[AU8522_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG; + state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[AU8522_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; + state->pads[AU8522_PAD_AUDIO_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[AU8522_PAD_AUDIO_OUT].sig_type = PAD_SIGNAL_AUDIO; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), + state->pads); + if (ret < 0) { + v4l_info(client, "failed to initialize media entity!\n"); + return ret; + } +#endif + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 109); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, + AU8522_TVDEC_CONTRAST_REG00BH_CVBS); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops, + V4L2_CID_HUE, -32768, 32767, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + au8522_release_state(state); + return err; + } + + state->c = client; + state->std = V4L2_STD_NTSC_M; + state->vid_input = AU8522_COMPOSITE_CH1; + state->aud_input = AU8522_AUDIO_NONE; + state->id = 8522; + state->rev = 0; + + /* Jam open the i2c gate to the tuner */ + au8522_writereg(state, 0x106, 1); + + return 0; +} + +static void au8522_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + au8522_release_state(to_state(sd)); +} + +static const struct i2c_device_id au8522_id[] = { + {"au8522", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, au8522_id); + +static struct i2c_driver au8522_driver = { + .driver = { + .name = "au8522", + }, + .probe = au8522_probe, + .remove = au8522_remove, + .id_table = au8522_id, +}; + +module_i2c_driver(au8522_driver); diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c new file mode 100644 index 0000000000..230436bf6c --- /dev/null +++ b/drivers/media/dvb-frontends/au8522_dig.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <media/dvb_frontend.h> +#include "au8522.h" +#include "au8522_priv.h" + +static int debug; +static int zv_mode = 1; /* default to on */ + +#define dprintk(arg...)\ + do { if (debug)\ + printk(arg);\ + } while (0) + +struct mse2snr_tab { + u16 val; + u16 data; +}; + +/* VSB SNR lookup table */ +static struct mse2snr_tab vsb_mse2snr_tab[] = { + { 0, 270 }, + { 2, 250 }, + { 3, 240 }, + { 5, 230 }, + { 7, 220 }, + { 9, 210 }, + { 12, 200 }, + { 13, 195 }, + { 15, 190 }, + { 17, 185 }, + { 19, 180 }, + { 21, 175 }, + { 24, 170 }, + { 27, 165 }, + { 31, 160 }, + { 32, 158 }, + { 33, 156 }, + { 36, 152 }, + { 37, 150 }, + { 39, 148 }, + { 40, 146 }, + { 41, 144 }, + { 43, 142 }, + { 44, 140 }, + { 48, 135 }, + { 50, 130 }, + { 43, 142 }, + { 53, 125 }, + { 56, 120 }, + { 256, 115 }, +}; + +/* QAM64 SNR lookup table */ +static struct mse2snr_tab qam64_mse2snr_tab[] = { + { 15, 0 }, + { 16, 290 }, + { 17, 288 }, + { 18, 286 }, + { 19, 284 }, + { 20, 282 }, + { 21, 281 }, + { 22, 279 }, + { 23, 277 }, + { 24, 275 }, + { 25, 273 }, + { 26, 271 }, + { 27, 269 }, + { 28, 268 }, + { 29, 266 }, + { 30, 264 }, + { 31, 262 }, + { 32, 260 }, + { 33, 259 }, + { 34, 258 }, + { 35, 256 }, + { 36, 255 }, + { 37, 254 }, + { 38, 252 }, + { 39, 251 }, + { 40, 250 }, + { 41, 249 }, + { 42, 248 }, + { 43, 246 }, + { 44, 245 }, + { 45, 244 }, + { 46, 242 }, + { 47, 241 }, + { 48, 240 }, + { 50, 239 }, + { 51, 238 }, + { 53, 237 }, + { 54, 236 }, + { 56, 235 }, + { 57, 234 }, + { 59, 233 }, + { 60, 232 }, + { 62, 231 }, + { 63, 230 }, + { 65, 229 }, + { 67, 228 }, + { 68, 227 }, + { 70, 226 }, + { 71, 225 }, + { 73, 224 }, + { 74, 223 }, + { 76, 222 }, + { 78, 221 }, + { 80, 220 }, + { 82, 219 }, + { 85, 218 }, + { 88, 217 }, + { 90, 216 }, + { 92, 215 }, + { 93, 214 }, + { 94, 212 }, + { 95, 211 }, + { 97, 210 }, + { 99, 209 }, + { 101, 208 }, + { 102, 207 }, + { 104, 206 }, + { 107, 205 }, + { 111, 204 }, + { 114, 203 }, + { 118, 202 }, + { 122, 201 }, + { 125, 200 }, + { 128, 199 }, + { 130, 198 }, + { 132, 197 }, + { 256, 190 }, +}; + +/* QAM256 SNR lookup table */ +static struct mse2snr_tab qam256_mse2snr_tab[] = { + { 15, 0 }, + { 16, 400 }, + { 17, 398 }, + { 18, 396 }, + { 19, 394 }, + { 20, 392 }, + { 21, 390 }, + { 22, 388 }, + { 23, 386 }, + { 24, 384 }, + { 25, 382 }, + { 26, 380 }, + { 27, 379 }, + { 28, 378 }, + { 29, 377 }, + { 30, 376 }, + { 31, 375 }, + { 32, 374 }, + { 33, 373 }, + { 34, 372 }, + { 35, 371 }, + { 36, 370 }, + { 37, 362 }, + { 38, 354 }, + { 39, 346 }, + { 40, 338 }, + { 41, 330 }, + { 42, 328 }, + { 43, 326 }, + { 44, 324 }, + { 45, 322 }, + { 46, 320 }, + { 47, 319 }, + { 48, 318 }, + { 49, 317 }, + { 50, 316 }, + { 51, 315 }, + { 52, 314 }, + { 53, 313 }, + { 54, 312 }, + { 55, 311 }, + { 56, 310 }, + { 57, 308 }, + { 58, 306 }, + { 59, 304 }, + { 60, 302 }, + { 61, 300 }, + { 62, 298 }, + { 65, 295 }, + { 68, 294 }, + { 70, 293 }, + { 73, 292 }, + { 76, 291 }, + { 78, 290 }, + { 79, 289 }, + { 81, 288 }, + { 82, 287 }, + { 83, 286 }, + { 84, 285 }, + { 85, 284 }, + { 86, 283 }, + { 88, 282 }, + { 89, 281 }, + { 256, 280 }, +}; + +static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse, + u16 *snr) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < sz; i++) { + if (mse < tab[i].val) { + *snr = tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __func__, *snr); + return ret; +} + +static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq) +{ + struct au8522_state *state = fe->demodulator_priv; + u8 r0b5, r0b6, r0b7; + char *ifmhz; + + switch (if_freq) { + case AU8522_IF_3_25MHZ: + ifmhz = "3.25"; + r0b5 = 0x00; + r0b6 = 0x3d; + r0b7 = 0xa0; + break; + case AU8522_IF_4MHZ: + ifmhz = "4.00"; + r0b5 = 0x00; + r0b6 = 0x4b; + r0b7 = 0xd9; + break; + case AU8522_IF_6MHZ: + ifmhz = "6.00"; + r0b5 = 0xfb; + r0b6 = 0x8e; + r0b7 = 0x39; + break; + default: + dprintk("%s() IF Frequency not supported\n", __func__); + return -EINVAL; + } + dprintk("%s() %s MHz\n", __func__, ifmhz); + au8522_writereg(state, 0x00b5, r0b5); + au8522_writereg(state, 0x00b6, r0b6); + au8522_writereg(state, 0x00b7, r0b7); + + return 0; +} + +/* VSB Modulation table */ +static struct { + u16 reg; + u16 data; +} VSB_mod_tab[] = { + { 0x0090, 0x84 }, + { 0x2005, 0x00 }, + { 0x0091, 0x80 }, + { 0x00a3, 0x0c }, + { 0x00a4, 0xe8 }, + { 0x0081, 0xc4 }, + { 0x00a5, 0x40 }, + { 0x00a7, 0x40 }, + { 0x00a6, 0x67 }, + { 0x0262, 0x20 }, + { 0x021c, 0x30 }, + { 0x00d8, 0x1a }, + { 0x0227, 0xa0 }, + { 0x0121, 0xff }, + { 0x00a8, 0xf0 }, + { 0x00a9, 0x05 }, + { 0x00aa, 0x77 }, + { 0x00ab, 0xf0 }, + { 0x00ac, 0x05 }, + { 0x00ad, 0x77 }, + { 0x00ae, 0x41 }, + { 0x00af, 0x66 }, + { 0x021b, 0xcc }, + { 0x021d, 0x80 }, + { 0x00a4, 0xe8 }, + { 0x0231, 0x13 }, +}; + +/* QAM64 Modulation table */ +static struct { + u16 reg; + u16 data; +} QAM64_mod_tab[] = { + { 0x00a3, 0x09 }, + { 0x00a4, 0x00 }, + { 0x0081, 0xc4 }, + { 0x00a5, 0x40 }, + { 0x00aa, 0x77 }, + { 0x00ad, 0x77 }, + { 0x00a6, 0x67 }, + { 0x0262, 0x20 }, + { 0x021c, 0x30 }, + { 0x00b8, 0x3e }, + { 0x00b9, 0xf0 }, + { 0x00ba, 0x01 }, + { 0x00bb, 0x18 }, + { 0x00bc, 0x50 }, + { 0x00bd, 0x00 }, + { 0x00be, 0xea }, + { 0x00bf, 0xef }, + { 0x00c0, 0xfc }, + { 0x00c1, 0xbd }, + { 0x00c2, 0x1f }, + { 0x00c3, 0xfc }, + { 0x00c4, 0xdd }, + { 0x00c5, 0xaf }, + { 0x00c6, 0x00 }, + { 0x00c7, 0x38 }, + { 0x00c8, 0x30 }, + { 0x00c9, 0x05 }, + { 0x00ca, 0x4a }, + { 0x00cb, 0xd0 }, + { 0x00cc, 0x01 }, + { 0x00cd, 0xd9 }, + { 0x00ce, 0x6f }, + { 0x00cf, 0xf9 }, + { 0x00d0, 0x70 }, + { 0x00d1, 0xdf }, + { 0x00d2, 0xf7 }, + { 0x00d3, 0xc2 }, + { 0x00d4, 0xdf }, + { 0x00d5, 0x02 }, + { 0x00d6, 0x9a }, + { 0x00d7, 0xd0 }, + { 0x0250, 0x0d }, + { 0x0251, 0xcd }, + { 0x0252, 0xe0 }, + { 0x0253, 0x05 }, + { 0x0254, 0xa7 }, + { 0x0255, 0xff }, + { 0x0256, 0xed }, + { 0x0257, 0x5b }, + { 0x0258, 0xae }, + { 0x0259, 0xe6 }, + { 0x025a, 0x3d }, + { 0x025b, 0x0f }, + { 0x025c, 0x0d }, + { 0x025d, 0xea }, + { 0x025e, 0xf2 }, + { 0x025f, 0x51 }, + { 0x0260, 0xf5 }, + { 0x0261, 0x06 }, + { 0x021a, 0x00 }, + { 0x0546, 0x40 }, + { 0x0210, 0xc7 }, + { 0x0211, 0xaa }, + { 0x0212, 0xab }, + { 0x0213, 0x02 }, + { 0x0502, 0x00 }, + { 0x0121, 0x04 }, + { 0x0122, 0x04 }, + { 0x052e, 0x10 }, + { 0x00a4, 0xca }, + { 0x00a7, 0x40 }, + { 0x0526, 0x01 }, +}; + +/* QAM256 Modulation table */ +static struct { + u16 reg; + u16 data; +} QAM256_mod_tab[] = { + { 0x00a3, 0x09 }, + { 0x00a4, 0x00 }, + { 0x0081, 0xc4 }, + { 0x00a5, 0x40 }, + { 0x00aa, 0x77 }, + { 0x00ad, 0x77 }, + { 0x00a6, 0x67 }, + { 0x0262, 0x20 }, + { 0x021c, 0x30 }, + { 0x00b8, 0x3e }, + { 0x00b9, 0xf0 }, + { 0x00ba, 0x01 }, + { 0x00bb, 0x18 }, + { 0x00bc, 0x50 }, + { 0x00bd, 0x00 }, + { 0x00be, 0xea }, + { 0x00bf, 0xef }, + { 0x00c0, 0xfc }, + { 0x00c1, 0xbd }, + { 0x00c2, 0x1f }, + { 0x00c3, 0xfc }, + { 0x00c4, 0xdd }, + { 0x00c5, 0xaf }, + { 0x00c6, 0x00 }, + { 0x00c7, 0x38 }, + { 0x00c8, 0x30 }, + { 0x00c9, 0x05 }, + { 0x00ca, 0x4a }, + { 0x00cb, 0xd0 }, + { 0x00cc, 0x01 }, + { 0x00cd, 0xd9 }, + { 0x00ce, 0x6f }, + { 0x00cf, 0xf9 }, + { 0x00d0, 0x70 }, + { 0x00d1, 0xdf }, + { 0x00d2, 0xf7 }, + { 0x00d3, 0xc2 }, + { 0x00d4, 0xdf }, + { 0x00d5, 0x02 }, + { 0x00d6, 0x9a }, + { 0x00d7, 0xd0 }, + { 0x0250, 0x0d }, + { 0x0251, 0xcd }, + { 0x0252, 0xe0 }, + { 0x0253, 0x05 }, + { 0x0254, 0xa7 }, + { 0x0255, 0xff }, + { 0x0256, 0xed }, + { 0x0257, 0x5b }, + { 0x0258, 0xae }, + { 0x0259, 0xe6 }, + { 0x025a, 0x3d }, + { 0x025b, 0x0f }, + { 0x025c, 0x0d }, + { 0x025d, 0xea }, + { 0x025e, 0xf2 }, + { 0x025f, 0x51 }, + { 0x0260, 0xf5 }, + { 0x0261, 0x06 }, + { 0x021a, 0x00 }, + { 0x0546, 0x40 }, + { 0x0210, 0x26 }, + { 0x0211, 0xf6 }, + { 0x0212, 0x84 }, + { 0x0213, 0x02 }, + { 0x0502, 0x01 }, + { 0x0121, 0x04 }, + { 0x0122, 0x04 }, + { 0x052e, 0x10 }, + { 0x00a4, 0xca }, + { 0x00a7, 0x40 }, + { 0x0526, 0x01 }, +}; + +static struct { + u16 reg; + u16 data; +} QAM256_mod_tab_zv_mode[] = { + { 0x80a3, 0x09 }, + { 0x80a4, 0x00 }, + { 0x8081, 0xc4 }, + { 0x80a5, 0x40 }, + { 0x80b5, 0xfb }, + { 0x80b6, 0x8e }, + { 0x80b7, 0x39 }, + { 0x80aa, 0x77 }, + { 0x80ad, 0x77 }, + { 0x80a6, 0x67 }, + { 0x8262, 0x20 }, + { 0x821c, 0x30 }, + { 0x80b8, 0x3e }, + { 0x80b9, 0xf0 }, + { 0x80ba, 0x01 }, + { 0x80bb, 0x18 }, + { 0x80bc, 0x50 }, + { 0x80bd, 0x00 }, + { 0x80be, 0xea }, + { 0x80bf, 0xef }, + { 0x80c0, 0xfc }, + { 0x80c1, 0xbd }, + { 0x80c2, 0x1f }, + { 0x80c3, 0xfc }, + { 0x80c4, 0xdd }, + { 0x80c5, 0xaf }, + { 0x80c6, 0x00 }, + { 0x80c7, 0x38 }, + { 0x80c8, 0x30 }, + { 0x80c9, 0x05 }, + { 0x80ca, 0x4a }, + { 0x80cb, 0xd0 }, + { 0x80cc, 0x01 }, + { 0x80cd, 0xd9 }, + { 0x80ce, 0x6f }, + { 0x80cf, 0xf9 }, + { 0x80d0, 0x70 }, + { 0x80d1, 0xdf }, + { 0x80d2, 0xf7 }, + { 0x80d3, 0xc2 }, + { 0x80d4, 0xdf }, + { 0x80d5, 0x02 }, + { 0x80d6, 0x9a }, + { 0x80d7, 0xd0 }, + { 0x8250, 0x0d }, + { 0x8251, 0xcd }, + { 0x8252, 0xe0 }, + { 0x8253, 0x05 }, + { 0x8254, 0xa7 }, + { 0x8255, 0xff }, + { 0x8256, 0xed }, + { 0x8257, 0x5b }, + { 0x8258, 0xae }, + { 0x8259, 0xe6 }, + { 0x825a, 0x3d }, + { 0x825b, 0x0f }, + { 0x825c, 0x0d }, + { 0x825d, 0xea }, + { 0x825e, 0xf2 }, + { 0x825f, 0x51 }, + { 0x8260, 0xf5 }, + { 0x8261, 0x06 }, + { 0x821a, 0x01 }, + { 0x8546, 0x40 }, + { 0x8210, 0x26 }, + { 0x8211, 0xf6 }, + { 0x8212, 0x84 }, + { 0x8213, 0x02 }, + { 0x8502, 0x01 }, + { 0x8121, 0x04 }, + { 0x8122, 0x04 }, + { 0x852e, 0x10 }, + { 0x80a4, 0xca }, + { 0x80a7, 0x40 }, + { 0x8526, 0x01 }, +}; + +static int au8522_enable_modulation(struct dvb_frontend *fe, + enum fe_modulation m) +{ + struct au8522_state *state = fe->demodulator_priv; + int i; + + dprintk("%s(0x%08x)\n", __func__, m); + + switch (m) { + case VSB_8: + dprintk("%s() VSB_8\n", __func__); + for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++) + au8522_writereg(state, + VSB_mod_tab[i].reg, + VSB_mod_tab[i].data); + au8522_set_if(fe, state->config.vsb_if); + break; + case QAM_64: + dprintk("%s() QAM 64\n", __func__); + for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++) + au8522_writereg(state, + QAM64_mod_tab[i].reg, + QAM64_mod_tab[i].data); + au8522_set_if(fe, state->config.qam_if); + break; + case QAM_256: + if (zv_mode) { + dprintk("%s() QAM 256 (zv_mode)\n", __func__); + for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab_zv_mode); i++) + au8522_writereg(state, + QAM256_mod_tab_zv_mode[i].reg, + QAM256_mod_tab_zv_mode[i].data); + au8522_set_if(fe, state->config.qam_if); + msleep(100); + au8522_writereg(state, 0x821a, 0x00); + } else { + dprintk("%s() QAM 256\n", __func__); + for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++) + au8522_writereg(state, + QAM256_mod_tab[i].reg, + QAM256_mod_tab[i].data); + au8522_set_if(fe, state->config.qam_if); + } + break; + default: + dprintk("%s() Invalid modulation\n", __func__); + return -EINVAL; + } + + state->current_modulation = m; + + return 0; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int au8522_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; + + dprintk("%s(frequency=%d)\n", __func__, c->frequency); + + if ((state->current_frequency == c->frequency) && + (state->current_modulation == c->modulation)) + return 0; + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + if (ret < 0) + return ret; + + /* Allow the tuner to settle */ + if (zv_mode) { + dprintk("%s() increase tuner settling time for zv_mode\n", + __func__); + msleep(250); + } else + msleep(100); + + au8522_enable_modulation(fe, c->modulation); + + state->current_frequency = c->frequency; + + return 0; +} + +static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct au8522_state *state = fe->demodulator_priv; + u8 reg; + u32 tuner_status = 0; + + *status = 0; + + if (state->current_modulation == VSB_8) { + dprintk("%s() Checking VSB_8\n", __func__); + reg = au8522_readreg(state, 0x0088); + if ((reg & 0x03) == 0x03) + *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; + } else { + dprintk("%s() Checking QAM\n", __func__); + reg = au8522_readreg(state, 0x0541); + if (reg & 0x80) + *status |= FE_HAS_VITERBI; + if (reg & 0x20) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + } + + switch (state->config.status_mode) { + case AU8522_DEMODLOCKING: + dprintk("%s() DEMODLOCKING\n", __func__); + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case AU8522_TUNERLOCKING: + /* Get the tuner status */ + dprintk("%s() TUNERLOCKING\n", __func__); + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + state->fe_status = *status; + + if (*status & FE_HAS_LOCK) + /* turn on LED, if it isn't on already */ + au8522_led_ctrl(state, -1); + else + /* turn off LED */ + au8522_led_ctrl(state, 0); + + dprintk("%s() status 0x%08x\n", __func__, *status); + + return 0; +} + +static int au8522_led_status(struct au8522_state *state, const u16 *snr) +{ + struct au8522_led_config *led_config = state->config.led_cfg; + int led; + u16 strong; + + /* bail out if we can't control an LED */ + if (!led_config) + return 0; + + if (0 == (state->fe_status & FE_HAS_LOCK)) + return au8522_led_ctrl(state, 0); + else if (state->current_modulation == QAM_256) + strong = led_config->qam256_strong; + else if (state->current_modulation == QAM_64) + strong = led_config->qam64_strong; + else /* (state->current_modulation == VSB_8) */ + strong = led_config->vsb8_strong; + + if (*snr >= strong) + led = 2; + else + led = 1; + + if ((state->led_state) && + (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10)) + /* snr didn't change enough to bother + * changing the color of the led */ + return 0; + + return au8522_led_ctrl(state, led); +} + +static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; + + dprintk("%s()\n", __func__); + + if (state->current_modulation == QAM_256) + ret = au8522_mse2snr_lookup(qam256_mse2snr_tab, + ARRAY_SIZE(qam256_mse2snr_tab), + au8522_readreg(state, 0x0522), + snr); + else if (state->current_modulation == QAM_64) + ret = au8522_mse2snr_lookup(qam64_mse2snr_tab, + ARRAY_SIZE(qam64_mse2snr_tab), + au8522_readreg(state, 0x0522), + snr); + else /* VSB_8 */ + ret = au8522_mse2snr_lookup(vsb_mse2snr_tab, + ARRAY_SIZE(vsb_mse2snr_tab), + au8522_readreg(state, 0x0311), + snr); + + if (state->config.led_cfg) + au8522_led_status(state, snr); + + return ret; +} + +static int au8522_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + u16 snr; + u32 tmp; + int ret = au8522_read_snr(fe, &snr); + + *signal_strength = 0; + + if (0 == ret) { + /* The following calculation method was chosen + * purely for the sake of code re-use from the + * other demod drivers that use this method */ + + /* Convert from SNR in dB * 10 to 8.24 fixed-point */ + tmp = (snr * ((1 << 24) / 10)); + + /* Convert from 8.24 fixed-point to + * scale the range 0 - 35*2^24 into 0 - 65535*/ + if (tmp >= 8960 * 0x10000) + *signal_strength = 0xffff; + else + *signal_strength = tmp / 8960; + } + + return ret; +} + +static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct au8522_state *state = fe->demodulator_priv; + + if (state->current_modulation == VSB_8) + *ucblocks = au8522_readreg(state, 0x0087); + else + *ucblocks = au8522_readreg(state, 0x0543); + + return 0; +} + +static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return au8522_read_ucblocks(fe, ber); +} + +static int au8522_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct au8522_state *state = fe->demodulator_priv; + + c->frequency = state->current_frequency; + c->modulation = state->current_modulation; + + return 0; +} + +static int au8522_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static const struct dvb_frontend_ops au8522_ops; + + +static void au8522_release(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + au8522_release_state(state); +} + +struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c) +{ + struct au8522_state *state = NULL; + int instance; + + /* allocate memory for the internal state */ + instance = au8522_get_state(&state, i2c, config->demod_address); + switch (instance) { + case 0: + dprintk("%s state allocation failed\n", __func__); + break; + case 1: + /* new demod instance */ + dprintk("%s using new instance\n", __func__); + break; + default: + /* existing demod instance */ + dprintk("%s using existing instance\n", __func__); + break; + } + + /* setup the state */ + state->config = *config; + state->i2c = i2c; + state->operational_mode = AU8522_DIGITAL_MODE; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &au8522_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + state->frontend.ops.analog_ops.i2c_gate_ctrl = au8522_analog_i2c_gate_ctrl; + + if (au8522_init(&state->frontend) != 0) { + printk(KERN_ERR "%s: Failed to initialize correctly\n", + __func__); + goto error; + } + + /* Note: Leaving the I2C gate open here. */ + au8522_i2c_gate_ctrl(&state->frontend, 1); + + return &state->frontend; + +error: + au8522_release_state(state); + return NULL; +} +EXPORT_SYMBOL_GPL(au8522_attach); + +static const struct dvb_frontend_ops au8522_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Auvitek AU8522 QAM/8VSB Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = au8522_init, + .sleep = au8522_sleep, + .i2c_gate_ctrl = au8522_i2c_gate_ctrl, + .set_frontend = au8522_set_frontend, + .get_frontend = au8522_get_frontend, + .get_tune_settings = au8522_get_tune_settings, + .read_status = au8522_read_status, + .read_ber = au8522_read_ber, + .read_signal_strength = au8522_read_signal_strength, + .read_snr = au8522_read_snr, + .read_ucblocks = au8522_read_ucblocks, + .release = au8522_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +module_param(zv_mode, int, 0644); +MODULE_PARM_DESC(zv_mode, "Turn on/off ZeeVee modulator compatibility mode (default:on).\n" + "\t\ton - modified AU8522 QAM256 initialization.\n" + "\t\tProvides faster lock when using ZeeVee modulator based sources"); + +MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h new file mode 100644 index 0000000000..19887aa809 --- /dev/null +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> + Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org> + Copyright (C) 2005-2008 Auvitek International, Ltd. + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-mc.h> +#include <linux/i2c.h> +#include <media/dvb_frontend.h> +#include "au8522.h" +#include "tuner-i2c.h" + +#define AU8522_ANALOG_MODE 0 +#define AU8522_DIGITAL_MODE 1 +#define AU8522_SUSPEND_MODE 2 + +enum au8522_pads { + AU8522_PAD_IF_INPUT, + AU8522_PAD_VID_OUT, + AU8522_PAD_AUDIO_OUT, + AU8522_NUM_PADS +}; + +struct au8522_state { + struct i2c_client *c; + struct i2c_adapter *i2c; + + u8 operational_mode; + + /* Used for sharing of the state between analog and digital mode */ + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; + + /* configuration settings */ + struct au8522_config config; + + struct dvb_frontend frontend; + + u32 current_frequency; + enum fe_modulation current_modulation; + + u32 fe_status; + unsigned int led_state; + + /* Analog settings */ + struct v4l2_subdev sd; + v4l2_std_id std; + int vid_input; + int aud_input; + u32 id; + u32 rev; + struct v4l2_ctrl_handler hdl; + +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_pad pads[AU8522_NUM_PADS]; +#endif +}; + +/* These are routines shared by both the VSB/QAM demodulator and the analog + decoder */ +int au8522_writereg(struct au8522_state *state, u16 reg, u8 data); +u8 au8522_readreg(struct au8522_state *state, u16 reg); +int au8522_init(struct dvb_frontend *fe); +int au8522_sleep(struct dvb_frontend *fe); + +int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, + u8 client_address); +void au8522_release_state(struct au8522_state *state); +int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); +int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); +int au8522_led_ctrl(struct au8522_state *state, int led); + +/* REGISTERS */ +#define AU8522_INPUT_CONTROL_REG081H 0x081 +#define AU8522_PGA_CONTROL_REG082H 0x082 +#define AU8522_CLAMPING_CONTROL_REG083H 0x083 + +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3 +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5 +#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6 +#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7 +#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8 +#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9 +#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA +#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB +#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC +#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD +#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE +#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF + +/* Receiver registers */ +#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0 +#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1 +#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2 +#define AU8522_TOREGAGC1_REG0B3H 0x0B3 +#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4 +#define AU8522_FRMREGBBH_REG0B5H 0x0B5 +#define AU8522_FRMREGBBM_REG0B6H 0x0B6 +#define AU8522_FRMREGBBL_REG0B7H 0x0B7 +/* 0xB8 TO 0xD7 are the filter coefficients */ +#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8 +#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9 +#define AU8522_TOREGAGC2_REG0DAH 0x0DA +#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB +#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC +#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD +#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE +#define AU8522_TOREGFREQ_REG0DFH 0x0DF + +#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB +#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC +#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED + +#define AU8522_CHIP_MODE_REG0FEH 0x0FE + +/* I2C bus control registers */ +#define AU8522_I2C_CONTROL_REG0_REG090H 0x090 +#define AU8522_I2C_CONTROL_REG1_REG091H 0x091 +#define AU8522_I2C_STATUS_REG092H 0x092 +#define AU8522_I2C_WR_DATA0_REG093H 0x093 +#define AU8522_I2C_WR_DATA1_REG094H 0x094 +#define AU8522_I2C_WR_DATA2_REG095H 0x095 +#define AU8522_I2C_WR_DATA3_REG096H 0x096 +#define AU8522_I2C_WR_DATA4_REG097H 0x097 +#define AU8522_I2C_WR_DATA5_REG098H 0x098 +#define AU8522_I2C_WR_DATA6_REG099H 0x099 +#define AU8522_I2C_WR_DATA7_REG09AH 0x09A +#define AU8522_I2C_RD_DATA0_REG09BH 0x09B +#define AU8522_I2C_RD_DATA1_REG09CH 0x09C +#define AU8522_I2C_RD_DATA2_REG09DH 0x09D +#define AU8522_I2C_RD_DATA3_REG09EH 0x09E +#define AU8522_I2C_RD_DATA4_REG09FH 0x09F +#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0 +#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1 +#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2 + +#define AU8522_ENA_USB_REG101H 0x101 + +#define AU8522_I2S_CTRL_0_REG110H 0x110 +#define AU8522_I2S_CTRL_1_REG111H 0x111 +#define AU8522_I2S_CTRL_2_REG112H 0x112 + +#define AU8522_FRMREGFFECONTROL_REG121H 0x121 +#define AU8522_FRMREGDFECONTROL_REG122H 0x122 + +#define AU8522_CARRFREQOFFSET0_REG201H 0x201 +#define AU8522_CARRFREQOFFSET1_REG202H 0x202 + +#define AU8522_DECIMATION_GAIN_REG21AH 0x21A +#define AU8522_FRMREGIFSLP_REG21BH 0x21B +#define AU8522_FRMREGTHRDL2_REG21CH 0x21C +#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D +#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E +#define AU8522_FRMREGPLLMODE_REG21FH 0x21F +#define AU8522_FRMREGCSTHRD_REG220H 0x220 +#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221 +#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222 +#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223 +#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224 +#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225 +#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226 +#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227 +#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228 + +/* Analog TV Decoder */ +#define AU8522_TVDEC_STATUS_REG000H 0x000 +#define AU8522_TVDEC_INT_STATUS_REG001H 0x001 +#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002 +#define AU8522_TVDEC_SHARPNESSREG009H 0x009 +#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A +#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B +#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C +#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D +#define AU8522_TVDEC_HUE_H_REG00EH 0x00E +#define AU8522_TVDEC_HUE_L_REG00FH 0x00F +#define AU8522_TVDEC_INT_MASK_REG010H 0x010 +#define AU8522_VIDEO_MODE_REG011H 0x011 +#define AU8522_TVDEC_PGA_REG012H 0x012 +#define AU8522_TVDEC_COMB_MODE_REG015H 0x015 +#define AU8522_REG016H 0x016 +#define AU8522_TVDED_DBG_MODE_REG060H 0x060 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061 +#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062 +#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063 +#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064 +#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065 +#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066 +#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067 +#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068 +#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069 +#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A +#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B +#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C +#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D +#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E +#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F +#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070 +#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073 +#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077 +#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078 +#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079 +#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A +#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B +#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E +#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F + +#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4 +#define AU8522_TOREGAAGC_REG0E5H 0x0E5 + +#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401 +#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402 +#define AU8522_FILTER_COEF_R410 0x410 +#define AU8522_FILTER_COEF_R411 0x411 +#define AU8522_FILTER_COEF_R412 0x412 +#define AU8522_FILTER_COEF_R413 0x413 +#define AU8522_FILTER_COEF_R414 0x414 +#define AU8522_FILTER_COEF_R415 0x415 +#define AU8522_FILTER_COEF_R416 0x416 +#define AU8522_FILTER_COEF_R417 0x417 +#define AU8522_FILTER_COEF_R418 0x418 +#define AU8522_FILTER_COEF_R419 0x419 +#define AU8522_FILTER_COEF_R41A 0x41A +#define AU8522_FILTER_COEF_R41B 0x41B +#define AU8522_FILTER_COEF_R41C 0x41C +#define AU8522_FILTER_COEF_R41D 0x41D +#define AU8522_FILTER_COEF_R41E 0x41E +#define AU8522_FILTER_COEF_R41F 0x41F +#define AU8522_FILTER_COEF_R420 0x420 +#define AU8522_FILTER_COEF_R421 0x421 +#define AU8522_FILTER_COEF_R422 0x422 +#define AU8522_FILTER_COEF_R423 0x423 +#define AU8522_FILTER_COEF_R424 0x424 +#define AU8522_FILTER_COEF_R425 0x425 +#define AU8522_FILTER_COEF_R426 0x426 +#define AU8522_FILTER_COEF_R427 0x427 +#define AU8522_FILTER_COEF_R428 0x428 +#define AU8522_FILTER_COEF_R429 0x429 +#define AU8522_FILTER_COEF_R42A 0x42A +#define AU8522_FILTER_COEF_R42B 0x42B +#define AU8522_FILTER_COEF_R42C 0x42C +#define AU8522_FILTER_COEF_R42D 0x42D + +/* VBI Control Registers */ +#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004 +#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005 +#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006 +#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007 +#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017 +#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018 +#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019 +#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A +#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B +#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C +#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E +#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F +#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020 +#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021 +#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022 +#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023 + +#define AU8522_REG071H 0x071 +#define AU8522_REG072H 0x072 +#define AU8522_REG074H 0x074 +#define AU8522_REG075H 0x075 + +/* Digital Demodulator Registers */ +#define AU8522_FRAME_COUNT0_REG084H 0x084 +#define AU8522_RS_STATUS_G0_REG085H 0x085 +#define AU8522_RS_STATUS_B0_REG086H 0x086 +#define AU8522_RS_STATUS_E_REG087H 0x087 +#define AU8522_DEMODULATION_STATUS_REG088H 0x088 +#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6 +#define AU8522_TSPORT_CONTROL_REG10BH 0x10B +#define AU8522_TSTHES_REG10CH 0x10C +#define AU8522_FRMREGDFEKEEP_REG301H 0x301 +#define AU8522_DFE_AVERAGE_REG302H 0x302 +#define AU8522_FRMREGEQLERRWIN_REG303H 0x303 +#define AU8522_FRMREGFFEKEEP_REG304H 0x304 +#define AU8522_FRMREGDFECONTROL1_REG305H 0x305 +#define AU8522_FRMREGEQLERRLOW_REG306H 0x306 + +#define AU8522_REG42EH 0x42E +#define AU8522_REG42FH 0x42F +#define AU8522_REG430H 0x430 +#define AU8522_REG431H 0x431 +#define AU8522_REG432H 0x432 +#define AU8522_REG433H 0x433 +#define AU8522_REG434H 0x434 +#define AU8522_REG435H 0x435 +#define AU8522_REG436H 0x436 + +/* GPIO Registers */ +#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0 +#define AU8522_GPIO_STATUS_REG0E1H 0x0E1 +#define AU8522_GPIO_DATA_REG0E2H 0x0E2 + +/* Audio Control Registers */ +#define AU8522_AUDIOAGC_REG0EEH 0x0EE +#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0 +#define AU8522_AUDIO_MODE_REG0F1H 0x0F1 +#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2 +#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3 +#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4 +#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7 +#define AU8522_REG0F9H 0x0F9 + +#define AU8522_AUDIOAGC2_REG605H 0x605 +#define AU8522_AUDIOFREQ_REG606H 0x606 + + +/**************************************************************/ + +/* Format control 1 */ + +/* VCR Mode 7-6 */ +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_YES 0x80 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_NO 0x40 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_AUTO 0x00 +/* Field len 5-4 */ +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_625 0x20 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 0x10 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_AUTO 0x00 +/* Line len (us) 3-2 */ +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_64_000 0x0b +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 0x08 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_556 0x04 +/* Subcarrier freq 1-0 */ +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO 0x03 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_443 0x02 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN 0x01 +#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_50 0x00 + +/* Format control 2 */ +#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_AUTODETECT 0x00 +#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC 0x01 +#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_PAL_M 0x02 + + +#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4 +#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4 +#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4 +#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4 +#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4 +#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20 +#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2 +#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0 +#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69 +#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68 +#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28 +/* CH1 AS Y,CH3 AS C */ +#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23 +/* CH2 AS Y,CH4 AS C */ +#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20 +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09 +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09 +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12 +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A +#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02 + +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00 +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8 +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD +#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD + +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01 +#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01 + +/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */ +#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79 +#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80 +#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80 +#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00 +#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00 +#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F +#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00 +#define AU8522_REG016H_CVBS 0x00 +#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00 +#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19 +#define AU8522_REG0F9H_AUDIO 0x20 +#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7 +#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A +#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32 +#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19 +#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23 +#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41 +#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A +#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32 +#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34 +#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO 0x2a +#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05 +#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO 0x15 +#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E +#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F +#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80 +#define AU8522_REG071H_CVBS 0x18 +#define AU8522_REG072H_CVBS 0x30 +#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0 +#define AU8522_REG074H_CVBS 0x80 +#define AU8522_REG075H_CVBS 0xF0 +#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB +#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04 +#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00 +#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00 +#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE +#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE +#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00 +#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40 + +/* Enables Closed captioning */ +#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21 diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c new file mode 100644 index 0000000000..b3f5c49acc --- /dev/null +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -0,0 +1,870 @@ +/* + * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) + * + * Copyright (C) 2001-5, B2C2 inc. + * + * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de> + * + * This driver is "hard-coded" to be used with the 1st generation of + * Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming + * (Panasonic CT10S) is located here, which is actually wrong. Unless there is + * another device with a BCM3510, this is no problem. + * + * The driver works also with QAM64 DVB-C, but had an unreasonable high + * UNC. (Tested with the Air2PC ATSC 1st generation) + * + * You'll need a firmware for this driver in order to get it running. It is + * called "dvb-fe-bcm3510-01.fw". + * + * 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 <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/mutex.h> + +#include <media/dvb_frontend.h> +#include "bcm3510.h" +#include "bcm3510_priv.h" + +/* Max transfer size done by bcm3510_do_hab_cmd() function */ +#define MAX_XFER_SIZE 128 + +struct bcm3510_state { + + struct i2c_adapter* i2c; + const struct bcm3510_config* config; + struct dvb_frontend frontend; + + /* demodulator private data */ + struct mutex hab_mutex; + u8 firmware_loaded:1; + + unsigned long next_status_check; + unsigned long status_check_interval; + struct bcm3510_hab_cmd_status1 status1; + struct bcm3510_hab_cmd_status2 status2; +}; + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able))."); + +#define dprintk(level,x...) if (level & debug) printk(x) +#define dbufout(b,l,m) {\ + int i; \ + for (i = 0; i < l; i++) \ + m("%02x ",b[i]); \ +} +#define deb_info(args...) dprintk(0x01,args) +#define deb_i2c(args...) dprintk(0x02,args) +#define deb_hab(args...) dprintk(0x04,args) + +/* transfer functions */ +static int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) +{ + u8 b[256]; + int err; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 }; + + b[0] = reg; + memcpy(&b[1],buf,len); + + deb_i2c("i2c wr %02x: ",reg); + dbufout(buf,len,deb_i2c); + deb_i2c("\n"); + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + + deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n", + __func__, state->config->demod_address, reg, err); + return -EREMOTEIO; + } + + return 0; +} + +static int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) +{ + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } + }; + int err; + + memset(buf,0,len); + + if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { + deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n", + __func__, state->config->demod_address, reg, err); + return -EREMOTEIO; + } + deb_i2c("i2c rd %02x: ",reg); + dbufout(buf,len,deb_i2c); + deb_i2c("\n"); + + return 0; +} + +static int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v) +{ + return bcm3510_writebytes(state,reg,&v.raw,1); +} + +static int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v) +{ + return bcm3510_readbytes(state,reg,&v->raw,1); +} + +/* Host Access Buffer transfers */ +static int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len) +{ + bcm3510_register_value v; + int ret,i; + + v.HABADR_a6.HABADR = 0; + if ((ret = bcm3510_writeB(st,0xa6,v)) < 0) + return ret; + + for (i = 0; i < len; i++) { + if ((ret = bcm3510_readB(st,0xa7,&v)) < 0) + return ret; + buf[i] = v.HABDATA_a7; + } + return 0; +} + +static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len) +{ + bcm3510_register_value v,hab; + int ret,i; + unsigned long t; + +/* Check if any previous HAB request still needs to be serviced by the + * Acquisition Processor before sending new request */ + if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) + return ret; + if (v.HABSTAT_a8.HABR) { + deb_info("HAB is running already - clearing it.\n"); + v.HABSTAT_a8.HABR = 0; + bcm3510_writeB(st,0xa8,v); +// return -EBUSY; + } + +/* Send the start HAB Address (automatically incremented after write of + * HABDATA) and write the HAB Data */ + hab.HABADR_a6.HABADR = 0; + if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0) + return ret; + + for (i = 0; i < len; i++) { + hab.HABDATA_a7 = buf[i]; + if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0) + return ret; + } + +/* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to + * be written) */ + v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1; + if ((ret = bcm3510_writeB(st,0xa8,v)) < 0) + return ret; + +/* Polling method: Wait until the AP finishes processing the HAB request */ + t = jiffies + 1*HZ; + while (time_before(jiffies, t)) { + deb_info("waiting for HAB to complete\n"); + msleep(10); + if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) + return ret; + + if (!v.HABSTAT_a8.HABR) + return 0; + } + + deb_info("send_request execution timed out.\n"); + return -ETIMEDOUT; +} + +static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen) +{ + u8 ob[MAX_XFER_SIZE], ib[MAX_XFER_SIZE]; + int ret = 0; + + if (ilen + 2 > sizeof(ib)) { + deb_hab("do_hab_cmd: ilen=%d is too big!\n", ilen); + return -EINVAL; + } + + if (olen + 2 > sizeof(ob)) { + deb_hab("do_hab_cmd: olen=%d is too big!\n", olen); + return -EINVAL; + } + + ob[0] = cmd; + ob[1] = msgid; + memcpy(&ob[2],obuf,olen); + + deb_hab("hab snd: "); + dbufout(ob,olen+2,deb_hab); + deb_hab("\n"); + + if (mutex_lock_interruptible(&st->hab_mutex) < 0) + return -EAGAIN; + + if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 || + (ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0) + goto error; + + deb_hab("hab get: "); + dbufout(ib,ilen+2,deb_hab); + deb_hab("\n"); + + memcpy(ibuf,&ib[2],ilen); +error: + mutex_unlock(&st->hab_mutex); + return ret; +} + +#if 0 +/* not needed, we use a semaphore to prevent HAB races */ +static int bcm3510_is_ap_ready(struct bcm3510_state *st) +{ + bcm3510_register_value ap,hab; + int ret; + + if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 || + (ret = bcm3510_readB(st,0xa2,&ap) < 0)) + return ret; + + if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) { + deb_info("AP is busy\n"); + return -EBUSY; + } + + return 0; +} +#endif + +static int bcm3510_bert_reset(struct bcm3510_state *st) +{ + bcm3510_register_value b; + int ret; + + if ((ret = bcm3510_readB(st,0xfa,&b)) < 0) + return ret; + + b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); + b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b); + b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); + b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b); + + /* clear residual bit counter TODO */ + return 0; +} + +static int bcm3510_refresh_state(struct bcm3510_state *st) +{ + if (time_after(jiffies,st->next_status_check)) { + bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1)); + bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2)); + st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; + } + return 0; +} + +static int bcm3510_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + + *status = 0; + if (st->status1.STATUS1.RECEIVER_LOCK) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + + if (st->status1.STATUS1.FEC_LOCK) + *status |= FE_HAS_VITERBI; + + if (st->status1.STATUS1.OUT_PLL_LOCK) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + if (*status & FE_HAS_LOCK) + st->status_check_interval = 1500; + else /* more frequently checks if no lock has been achieved yet */ + st->status_check_interval = 500; + + deb_info("real_status: %02x\n",*status); + return 0; +} + +static int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + + *ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2; + return 0; +} + +static int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + *unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1; + return 0; +} + +static int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct bcm3510_state* st = fe->demodulator_priv; + s32 t; + + bcm3510_refresh_state(st); + t = st->status2.SIGNAL; + + if (t > 190) + t = 190; + if (t < 90) + t = 90; + + t -= 90; + t = t * 0xff / 100; + /* normalize if necessary */ + *strength = (t << 8) | t; + return 0; +} + +static int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + + *snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8); + return 0; +} + +/* tuner frontend programming */ +static int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a) +{ + struct bcm3510_hab_cmd_tune c; + memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune)); + +/* I2C Mode disabled, set 16 control / Data pairs */ + c.length = 0x10; + c.clock_width = 0; +/* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to + * logic high (as Configuration) */ + c.misc = 0x10; +/* Set duration of the initial state of TUNCTL = 3.34 micro Sec */ + c.TUNCTL_state = 0x40; + +/* PRESCALER DIVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */ + c.ctl_dat[0].ctrl.size = BITS_8; + c.ctl_dat[0].data = 0x80 | bc; + +/* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */ + c.ctl_dat[1].ctrl.size = BITS_8; + c.ctl_dat[1].data = 4; + +/* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */ + c.ctl_dat[2].ctrl.size = BITS_3; + c.ctl_dat[2].data = 0x20; + +/* control CS0 pin, pulse byte ? */ + c.ctl_dat[3].ctrl.size = BITS_3; + c.ctl_dat[3].ctrl.clk_off = 1; + c.ctl_dat[3].ctrl.cs0 = 1; + c.ctl_dat[3].data = 0x40; + +/* PGM_S18 to PGM_S11 */ + c.ctl_dat[4].ctrl.size = BITS_8; + c.ctl_dat[4].data = n >> 3; + +/* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */ + c.ctl_dat[5].ctrl.size = BITS_8; + c.ctl_dat[5].data = ((n & 0x7) << 5) | (a >> 2); + +/* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */ + c.ctl_dat[6].ctrl.size = BITS_3; + c.ctl_dat[6].data = (a << 6) & 0xdf; + +/* control CS0 pin, pulse byte ? */ + c.ctl_dat[7].ctrl.size = BITS_3; + c.ctl_dat[7].ctrl.clk_off = 1; + c.ctl_dat[7].ctrl.cs0 = 1; + c.ctl_dat[7].data = 0x40; + +/* PRESCALER DIVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */ + c.ctl_dat[8].ctrl.size = BITS_8; + c.ctl_dat[8].data = 0x80; + +/* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */ + c.ctl_dat[9].ctrl.size = BITS_8; + c.ctl_dat[9].data = 0x10; + +/* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */ + c.ctl_dat[10].ctrl.size = BITS_3; + c.ctl_dat[10].data = 0x20; + +/* pulse byte */ + c.ctl_dat[11].ctrl.size = BITS_3; + c.ctl_dat[11].ctrl.clk_off = 1; + c.ctl_dat[11].ctrl.cs1 = 1; + c.ctl_dat[11].data = 0x40; + +/* PGM_S18 to PGM_S11 */ + c.ctl_dat[12].ctrl.size = BITS_8; + c.ctl_dat[12].data = 0x2a; + +/* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */ + c.ctl_dat[13].ctrl.size = BITS_8; + c.ctl_dat[13].data = 0x8e; + +/* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */ + c.ctl_dat[14].ctrl.size = BITS_3; + c.ctl_dat[14].data = 0; + +/* Pulse Byte */ + c.ctl_dat[15].ctrl.size = BITS_3; + c.ctl_dat[15].ctrl.clk_off = 1; + c.ctl_dat[15].ctrl.cs1 = 1; + c.ctl_dat[15].data = 0x40; + + return bcm3510_do_hab_cmd(st,CMD_TUNE, MSGID_TUNE,(u8 *) &c,sizeof(c), NULL, 0); +} + +static int bcm3510_set_freq(struct bcm3510_state* st,u32 freq) +{ + u8 bc,a; + u16 n; + s32 YIntercept,Tfvco1; + + freq /= 1000; + + deb_info("%dkHz:",freq); + /* set Band Switch */ + if (freq <= 168000) + bc = 0x1c; + else if (freq <= 378000) + bc = 0x2c; + else + bc = 0x30; + + if (freq >= 470000) { + freq -= 470001; + YIntercept = 18805; + } else if (freq >= 90000) { + freq -= 90001; + YIntercept = 15005; + } else if (freq >= 76000){ + freq -= 76001; + YIntercept = 14865; + } else { + freq -= 54001; + YIntercept = 14645; + } + + Tfvco1 = (((freq/6000)*60 + YIntercept)*4)/10; + + n = Tfvco1 >> 6; + a = Tfvco1 & 0x3f; + + deb_info(" BC1_2_3_4: %x, N: %x A: %x\n", bc, n, a); + if (n >= 16 && n <= 2047) + return bcm3510_tuner_cmd(st,bc,n,a); + + return -EINVAL; +} + +static int bcm3510_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct bcm3510_state* st = fe->demodulator_priv; + struct bcm3510_hab_cmd_ext_acquire cmd; + struct bcm3510_hab_cmd_bert_control bert; + int ret; + + memset(&cmd,0,sizeof(cmd)); + switch (c->modulation) { + case QAM_256: + cmd.ACQUIRE0.MODE = 0x1; + cmd.ACQUIRE1.SYM_RATE = 0x1; + cmd.ACQUIRE1.IF_FREQ = 0x1; + break; + case QAM_64: + cmd.ACQUIRE0.MODE = 0x2; + cmd.ACQUIRE1.SYM_RATE = 0x2; + cmd.ACQUIRE1.IF_FREQ = 0x1; + break; +#if 0 + case QAM_256: + cmd.ACQUIRE0.MODE = 0x3; + break; + case QAM_128: + cmd.ACQUIRE0.MODE = 0x4; + break; + case QAM_64: + cmd.ACQUIRE0.MODE = 0x5; + break; + case QAM_32: + cmd.ACQUIRE0.MODE = 0x6; + break; + case QAM_16: + cmd.ACQUIRE0.MODE = 0x7; + break; +#endif + case VSB_8: + cmd.ACQUIRE0.MODE = 0x8; + cmd.ACQUIRE1.SYM_RATE = 0x0; + cmd.ACQUIRE1.IF_FREQ = 0x0; + break; + case VSB_16: + cmd.ACQUIRE0.MODE = 0x9; + cmd.ACQUIRE1.SYM_RATE = 0x0; + cmd.ACQUIRE1.IF_FREQ = 0x0; + break; + default: + return -EINVAL; + } + cmd.ACQUIRE0.OFFSET = 0; + cmd.ACQUIRE0.NTSCSWEEP = 1; + cmd.ACQUIRE0.FA = 1; + cmd.ACQUIRE0.BW = 0; + +/* if (enableOffset) { + cmd.IF_OFFSET0 = xx; + cmd.IF_OFFSET1 = xx; + + cmd.SYM_OFFSET0 = xx; + cmd.SYM_OFFSET1 = xx; + if (enableNtscSweep) { + cmd.NTSC_OFFSET0; + cmd.NTSC_OFFSET1; + } + } */ + bcm3510_do_hab_cmd(st, CMD_ACQUIRE, MSGID_EXT_TUNER_ACQUIRE, (u8 *) &cmd, sizeof(cmd), NULL, 0); + +/* doing it with different MSGIDs, data book and source differs */ + bert.BE = 0; + bert.unused = 0; + bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_CONTROL, (u8 *) &bert, sizeof(bert), NULL, 0); + bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_SET, (u8 *) &bert, sizeof(bert), NULL, 0); + + bcm3510_bert_reset(st); + + ret = bcm3510_set_freq(st, c->frequency); + if (ret < 0) + return ret; + + memset(&st->status1,0,sizeof(st->status1)); + memset(&st->status2,0,sizeof(st->status2)); + st->status_check_interval = 500; + +/* Give the AP some time */ + msleep(200); + + return 0; +} + +static int bcm3510_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int bcm3510_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + s->step_size = 0; + s->max_drift = 0; + return 0; +} + +static void bcm3510_release(struct dvb_frontend* fe) +{ + struct bcm3510_state* state = fe->demodulator_priv; + kfree(state); +} + +/* firmware download: + * firmware file is build up like this: + * 16bit addr, 16bit length, 8byte of length + */ +#define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw" + +static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, const u8 *b, + u16 len) +{ + int ret = 0,i; + bcm3510_register_value vH, vL,vD; + + vH.MADRH_a9 = addr >> 8; + vL.MADRL_aa = addr; + if ((ret = bcm3510_writeB(st,0xa9,vH)) < 0) return ret; + if ((ret = bcm3510_writeB(st,0xaa,vL)) < 0) return ret; + + for (i = 0; i < len; i++) { + vD.MDATA_ab = b[i]; + if ((ret = bcm3510_writeB(st,0xab,vD)) < 0) + return ret; + } + + return 0; +} + +static int bcm3510_download_firmware(struct dvb_frontend* fe) +{ + struct bcm3510_state* st = fe->demodulator_priv; + const struct firmware *fw; + u16 addr,len; + const u8 *b; + int ret,i; + + deb_info("requesting firmware\n"); + if ((ret = st->config->request_firmware(fe, &fw, BCM3510_DEFAULT_FIRMWARE)) < 0) { + err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); + return ret; + } + deb_info("got firmware: %zu\n", fw->size); + + b = fw->data; + for (i = 0; i < fw->size;) { + addr = le16_to_cpu(*((__le16 *)&b[i])); + len = le16_to_cpu(*((__le16 *)&b[i+2])); + deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size); + if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { + err("firmware download failed: %d\n",ret); + release_firmware(fw); + return ret; + } + i += 4 + len; + } + release_firmware(fw); + deb_info("firmware download successfully completed\n"); + return 0; +} + +static int bcm3510_check_firmware_version(struct bcm3510_state *st) +{ + struct bcm3510_hab_cmd_get_version_info ver; + bcm3510_do_hab_cmd(st,CMD_GET_VERSION_INFO,MSGID_GET_VERSION_INFO,NULL,0,(u8*)&ver,sizeof(ver)); + + deb_info("Version information: 0x%02x 0x%02x 0x%02x 0x%02x\n", + ver.microcode_version, ver.script_version, ver.config_version, ver.demod_version); + + if (ver.script_version == BCM3510_DEF_SCRIPT_VERSION && + ver.config_version == BCM3510_DEF_CONFIG_VERSION && + ver.demod_version == BCM3510_DEF_DEMOD_VERSION) + return 0; + + deb_info("version check failed\n"); + return -ENODEV; +} + +/* (un)resetting the AP */ +static int bcm3510_reset(struct bcm3510_state *st) +{ + int ret; + unsigned long t; + bcm3510_register_value v; + + bcm3510_readB(st,0xa0,&v); v.HCTL1_a0.RESET = 1; + if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) + return ret; + + t = jiffies + 3*HZ; + while (time_before(jiffies, t)) { + msleep(10); + if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) + return ret; + + if (v.APSTAT1_a2.RESET) + return 0; + } + deb_info("reset timed out\n"); + return -ETIMEDOUT; +} + +static int bcm3510_clear_reset(struct bcm3510_state *st) +{ + bcm3510_register_value v; + int ret; + unsigned long t; + + v.raw = 0; + if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) + return ret; + + t = jiffies + 3*HZ; + while (time_before(jiffies, t)) { + msleep(10); + if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) + return ret; + + /* verify that reset is cleared */ + if (!v.APSTAT1_a2.RESET) + return 0; + } + deb_info("reset clear timed out\n"); + return -ETIMEDOUT; +} + +static int bcm3510_init_cold(struct bcm3510_state *st) +{ + int ret; + bcm3510_register_value v; + + /* read Acquisation Processor status register and check it is not in RUN mode */ + if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) + return ret; + if (v.APSTAT1_a2.RUN) { + deb_info("AP is already running - firmware already loaded.\n"); + return 0; + } + + deb_info("reset?\n"); + if ((ret = bcm3510_reset(st)) < 0) + return ret; + + deb_info("tristate?\n"); + /* tri-state */ + v.TSTCTL_2e.CTL = 0; + if ((ret = bcm3510_writeB(st,0x2e,v)) < 0) + return ret; + + deb_info("firmware?\n"); + if ((ret = bcm3510_download_firmware(&st->frontend)) < 0 || + (ret = bcm3510_clear_reset(st)) < 0) + return ret; + + /* anything left here to Let the acquisition processor begin execution at program counter 0000 ??? */ + + return 0; +} + +static int bcm3510_init(struct dvb_frontend* fe) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_register_value j; + struct bcm3510_hab_cmd_set_agc c; + int ret; + + if ((ret = bcm3510_readB(st,0xca,&j)) < 0) + return ret; + + deb_info("JDEC: %02x\n",j.raw); + + switch (j.JDEC_ca.JDEC) { + case JDEC_WAIT_AT_RAM: + deb_info("attempting to download firmware\n"); + if ((ret = bcm3510_init_cold(st)) < 0) + return ret; + fallthrough; + case JDEC_EEPROM_LOAD_WAIT: + deb_info("firmware is loaded\n"); + bcm3510_check_firmware_version(st); + break; + default: + return -ENODEV; + } + + memset(&c,0,1); + c.SEL = 1; + bcm3510_do_hab_cmd(st,CMD_AUTO_PARAM,MSGID_SET_RF_AGC_SEL,(u8 *)&c,sizeof(c),NULL,0); + + return 0; +} + + +static const struct dvb_frontend_ops bcm3510_ops; + +struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, + struct i2c_adapter *i2c) +{ + struct bcm3510_state* state = NULL; + int ret; + bcm3510_register_value v; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct bcm3510_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + + state->config = config; + state->i2c = i2c; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &bcm3510_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + mutex_init(&state->hab_mutex); + + if ((ret = bcm3510_readB(state,0xe0,&v)) < 0) + goto error; + + deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER); + + if ((v.REVID_e0.REV != 0x1 && v.REVID_e0.LAYER != 0xb) && /* cold */ + (v.REVID_e0.REV != 0x8 && v.REVID_e0.LAYER != 0x0)) /* warm */ + goto error; + + info("Revision: 0x%1x, Layer: 0x%1x.",v.REVID_e0.REV,v.REVID_e0.LAYER); + + bcm3510_reset(state); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(bcm3510_attach); + +static const struct dvb_frontend_ops bcm3510_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Broadcom BCM3510 VSB/QAM frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 803 * MHz, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_8VSB | FE_CAN_16VSB | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 + }, + + .release = bcm3510_release, + + .init = bcm3510_init, + .sleep = bcm3510_sleep, + + .set_frontend = bcm3510_set_frontend, + .get_tune_settings = bcm3510_get_tune_settings, + + .read_status = bcm3510_read_status, + .read_ber = bcm3510_read_ber, + .read_signal_strength = bcm3510_read_signal_strength, + .read_snr = bcm3510_read_snr, + .read_ucblocks = bcm3510_read_unc, +}; + +MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver"); +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h new file mode 100644 index 0000000000..43a7a8b479 --- /dev/null +++ b/drivers/media/dvb-frontends/bcm3510.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) + * + * Copyright (C) 2001-5, B2C2 inc. + * + * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de> + */ +#ifndef BCM3510_H +#define BCM3510_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct bcm3510_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +#if IS_REACHABLE(CONFIG_DVB_BCM3510) +extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_BCM3510 + +#endif diff --git a/drivers/media/dvb-frontends/bcm3510_priv.h b/drivers/media/dvb-frontends/bcm3510_priv.h new file mode 100644 index 0000000000..2c9f3c430a --- /dev/null +++ b/drivers/media/dvb-frontends/bcm3510_priv.h @@ -0,0 +1,447 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) + * + * Copyright (C) 2001-5, B2C2 inc. + * + * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de> + */ +#ifndef __BCM3510_PRIV_H__ +#define __BCM3510_PRIV_H__ + +#define PACKED __attribute__((packed)) + +#undef err +#define err(format, arg...) printk(KERN_ERR "bcm3510: " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO "bcm3510: " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING "bcm3510: " format "\n" , ## arg) + + +#define PANASONIC_FIRST_IF_BASE_IN_KHz 1407500 +#define BCM3510_SYMBOL_RATE 5381000 + +typedef union { + u8 raw; + + struct { + u8 CTL :8; + } TSTCTL_2e; + + u8 LDCERC_4e; + u8 LDUERC_4f; + u8 LD_BER0_65; + u8 LD_BER1_66; + u8 LD_BER2_67; + u8 LD_BER3_68; + + struct { + u8 RESET :1; + u8 IDLE :1; + u8 STOP :1; + u8 HIRQ0 :1; + u8 HIRQ1 :1; + u8 na0 :1; + u8 HABAV :1; + u8 na1 :1; + } HCTL1_a0; + + struct { + u8 na0 :1; + u8 IDLMSK :1; + u8 STMSK :1; + u8 I0MSK :1; + u8 I1MSK :1; + u8 na1 :1; + u8 HABMSK :1; + u8 na2 :1; + } HCTLMSK_a1; + + struct { + u8 RESET :1; + u8 IDLE :1; + u8 STOP :1; + u8 RUN :1; + u8 HABAV :1; + u8 MEMAV :1; + u8 ALDONE :1; + u8 REIRQ :1; + } APSTAT1_a2; + + struct { + u8 RSTMSK :1; + u8 IMSK :1; + u8 SMSK :1; + u8 RMSK :1; + u8 HABMSK :1; + u8 MAVMSK :1; + u8 ALDMSK :1; + u8 REMSK :1; + } APMSK1_a3; + + u8 APSTAT2_a4; + u8 APMSK2_a5; + + struct { + u8 HABADR :7; + u8 na :1; + } HABADR_a6; + + u8 HABDATA_a7; + + struct { + u8 HABR :1; + u8 LDHABR :1; + u8 APMSK :1; + u8 HMSK :1; + u8 LDMSK :1; + u8 na :3; + } HABSTAT_a8; + + u8 MADRH_a9; + u8 MADRL_aa; + u8 MDATA_ab; + + struct { +#define JDEC_WAIT_AT_RAM 0x7 +#define JDEC_EEPROM_LOAD_WAIT 0x4 + u8 JDEC :3; + u8 na :5; + } JDEC_ca; + + struct { + u8 REV :4; + u8 LAYER :4; + } REVID_e0; + + struct { + u8 unk0 :1; + u8 CNTCTL :1; + u8 BITCNT :1; + u8 unk1 :1; + u8 RESYNC :1; + u8 unk2 :3; + } BERCTL_fa; + + struct { + u8 CSEL0 :1; + u8 CLKED0 :1; + u8 CSEL1 :1; + u8 CLKED1 :1; + u8 CLKLEV :1; + u8 SPIVAR :1; + u8 na :2; + } TUNSET_fc; + + struct { + u8 CLK :1; + u8 DATA :1; + u8 CS0 :1; + u8 CS1 :1; + u8 AGCSEL :1; + u8 na0 :1; + u8 TUNSEL :1; + u8 na1 :1; + } TUNCTL_fd; + + u8 TUNSEL0_fe; + u8 TUNSEL1_ff; + +} bcm3510_register_value; + +/* HAB commands */ + +/* version */ +#define CMD_GET_VERSION_INFO 0x3D +#define MSGID_GET_VERSION_INFO 0x15 +struct bcm3510_hab_cmd_get_version_info { + u8 microcode_version; + u8 script_version; + u8 config_version; + u8 demod_version; +} PACKED; + +#define BCM3510_DEF_MICROCODE_VERSION 0x0E +#define BCM3510_DEF_SCRIPT_VERSION 0x06 +#define BCM3510_DEF_CONFIG_VERSION 0x01 +#define BCM3510_DEF_DEMOD_VERSION 0xB1 + +/* acquire */ +#define CMD_ACQUIRE 0x38 + +#define MSGID_EXT_TUNER_ACQUIRE 0x0A +struct bcm3510_hab_cmd_ext_acquire { + struct { + u8 MODE :4; + u8 BW :1; + u8 FA :1; + u8 NTSCSWEEP :1; + u8 OFFSET :1; + } PACKED ACQUIRE0; /* control_byte */ + + struct { + u8 IF_FREQ :3; + u8 zero0 :1; + u8 SYM_RATE :3; + u8 zero1 :1; + } PACKED ACQUIRE1; /* sym_if */ + + u8 IF_OFFSET0; /* IF_Offset_10hz */ + u8 IF_OFFSET1; + u8 SYM_OFFSET0; /* SymbolRateOffset */ + u8 SYM_OFFSET1; + u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ + u8 NTSC_OFFSET1; +} PACKED; + +#define MSGID_INT_TUNER_ACQUIRE 0x0B +struct bcm3510_hab_cmd_int_acquire { + struct { + u8 MODE :4; + u8 BW :1; + u8 FA :1; + u8 NTSCSWEEP :1; + u8 OFFSET :1; + } PACKED ACQUIRE0; /* control_byte */ + + struct { + u8 IF_FREQ :3; + u8 zero0 :1; + u8 SYM_RATE :3; + u8 zero1 :1; + } PACKED ACQUIRE1; /* sym_if */ + + u8 TUNER_FREQ0; + u8 TUNER_FREQ1; + u8 TUNER_FREQ2; + u8 TUNER_FREQ3; + u8 IF_OFFSET0; /* IF_Offset_10hz */ + u8 IF_OFFSET1; + u8 SYM_OFFSET0; /* SymbolRateOffset */ + u8 SYM_OFFSET1; + u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ + u8 NTSC_OFFSET1; +} PACKED; + +/* modes */ +#define BCM3510_QAM16 = 0x01 +#define BCM3510_QAM32 = 0x02 +#define BCM3510_QAM64 = 0x03 +#define BCM3510_QAM128 = 0x04 +#define BCM3510_QAM256 = 0x05 +#define BCM3510_8VSB = 0x0B +#define BCM3510_16VSB = 0x0D + +/* IF_FREQS */ +#define BCM3510_IF_TERRESTRIAL 0x0 +#define BCM3510_IF_CABLE 0x1 +#define BCM3510_IF_USE_CMD 0x7 + +/* SYM_RATE */ +#define BCM3510_SR_8VSB 0x0 /* 5381119 s/sec */ +#define BCM3510_SR_256QAM 0x1 /* 5360537 s/sec */ +#define BCM3510_SR_16QAM 0x2 /* 5056971 s/sec */ +#define BCM3510_SR_MISC 0x3 /* 5000000 s/sec */ +#define BCM3510_SR_USE_CMD 0x7 + +/* special symbol rate */ +#define CMD_SET_VALUE_NOT_LISTED 0x2d +#define MSGID_SET_SYMBOL_RATE_NOT_LISTED 0x0c +struct bcm3510_hab_cmd_set_sr_not_listed { + u8 HOST_SYM_RATE0; + u8 HOST_SYM_RATE1; + u8 HOST_SYM_RATE2; + u8 HOST_SYM_RATE3; +} PACKED; + +/* special IF */ +#define MSGID_SET_IF_FREQ_NOT_LISTED 0x0d +struct bcm3510_hab_cmd_set_if_freq_not_listed { + u8 HOST_IF_FREQ0; + u8 HOST_IF_FREQ1; + u8 HOST_IF_FREQ2; + u8 HOST_IF_FREQ3; +} PACKED; + +/* auto reacquire */ +#define CMD_AUTO_PARAM 0x2a +#define MSGID_AUTO_REACQUIRE 0x0e +struct bcm3510_hab_cmd_auto_reacquire { + u8 ACQ :1; /* on/off*/ + u8 unused :7; +} PACKED; + +#define MSGID_SET_RF_AGC_SEL 0x12 +struct bcm3510_hab_cmd_set_agc { + u8 LVL :1; + u8 unused :6; + u8 SEL :1; +} PACKED; + +#define MSGID_SET_AUTO_INVERSION 0x14 +struct bcm3510_hab_cmd_auto_inversion { + u8 AI :1; + u8 unused :7; +} PACKED; + + +/* bert control */ +#define CMD_STATE_CONTROL 0x12 +#define MSGID_BERT_CONTROL 0x0e +#define MSGID_BERT_SET 0xfa +struct bcm3510_hab_cmd_bert_control { + u8 BE :1; + u8 unused :7; +} PACKED; + +#define MSGID_TRI_STATE 0x2e +struct bcm3510_hab_cmd_tri_state { + u8 RE :1; /* a/d ram port pins */ + u8 PE :1; /* baud clock pin */ + u8 AC :1; /* a/d clock pin */ + u8 BE :1; /* baud clock pin */ + u8 unused :4; +} PACKED; + + +/* tune */ +#define CMD_TUNE 0x38 +#define MSGID_TUNE 0x16 +struct bcm3510_hab_cmd_tune_ctrl_data_pair { + struct { +#define BITS_8 0x07 +#define BITS_7 0x06 +#define BITS_6 0x05 +#define BITS_5 0x04 +#define BITS_4 0x03 +#define BITS_3 0x02 +#define BITS_2 0x01 +#define BITS_1 0x00 + u8 size :3; + u8 unk :2; + u8 clk_off :1; + u8 cs0 :1; + u8 cs1 :1; + + } PACKED ctrl; + + u8 data; +} PACKED; + +struct bcm3510_hab_cmd_tune { + u8 length; + u8 clock_width; + u8 misc; + u8 TUNCTL_state; + + struct bcm3510_hab_cmd_tune_ctrl_data_pair ctl_dat[16]; +} PACKED; + +#define CMD_STATUS 0x38 +#define MSGID_STATUS1 0x08 +struct bcm3510_hab_cmd_status1 { + struct { + u8 EQ_MODE :4; + u8 reserved :2; + u8 QRE :1; /* if QSE and the spectrum is inversed */ + u8 QSE :1; /* automatic spectral inversion */ + } PACKED STATUS0; + + struct { + u8 RECEIVER_LOCK :1; + u8 FEC_LOCK :1; + u8 OUT_PLL_LOCK :1; + u8 reserved :5; + } PACKED STATUS1; + + struct { + u8 reserved :2; + u8 BW :1; + u8 NTE :1; /* NTSC filter sweep enabled */ + u8 AQI :1; /* currently acquiring */ + u8 FA :1; /* fast acquisition */ + u8 ARI :1; /* auto reacquire */ + u8 TI :1; /* programming the tuner */ + } PACKED STATUS2; + u8 STATUS3; + u8 SNR_EST0; + u8 SNR_EST1; + u8 TUNER_FREQ0; + u8 TUNER_FREQ1; + u8 TUNER_FREQ2; + u8 TUNER_FREQ3; + u8 SYM_RATE0; + u8 SYM_RATE1; + u8 SYM_RATE2; + u8 SYM_RATE3; + u8 SYM_OFFSET0; + u8 SYM_OFFSET1; + u8 SYM_ERROR0; + u8 SYM_ERROR1; + u8 IF_FREQ0; + u8 IF_FREQ1; + u8 IF_FREQ2; + u8 IF_FREQ3; + u8 IF_OFFSET0; + u8 IF_OFFSET1; + u8 IF_ERROR0; + u8 IF_ERROR1; + u8 NTSC_FILTER0; + u8 NTSC_FILTER1; + u8 NTSC_FILTER2; + u8 NTSC_FILTER3; + u8 NTSC_OFFSET0; + u8 NTSC_OFFSET1; + u8 NTSC_ERROR0; + u8 NTSC_ERROR1; + u8 INT_AGC_LEVEL0; + u8 INT_AGC_LEVEL1; + u8 EXT_AGC_LEVEL0; + u8 EXT_AGC_LEVEL1; +} PACKED; + +#define MSGID_STATUS2 0x14 +struct bcm3510_hab_cmd_status2 { + struct { + u8 EQ_MODE :4; + u8 reserved :2; + u8 QRE :1; + u8 QSR :1; + } PACKED STATUS0; + struct { + u8 RL :1; + u8 FL :1; + u8 OL :1; + u8 reserved :5; + } PACKED STATUS1; + u8 SYMBOL_RATE0; + u8 SYMBOL_RATE1; + u8 SYMBOL_RATE2; + u8 SYMBOL_RATE3; + u8 LDCERC0; + u8 LDCERC1; + u8 LDCERC2; + u8 LDCERC3; + u8 LDUERC0; + u8 LDUERC1; + u8 LDUERC2; + u8 LDUERC3; + u8 LDBER0; + u8 LDBER1; + u8 LDBER2; + u8 LDBER3; + struct { + u8 MODE_TYPE :4; /* acquire mode 0 */ + u8 reservd :4; + } MODE_TYPE; + u8 SNR_EST0; + u8 SNR_EST1; + u8 SIGNAL; +} PACKED; + +#define CMD_SET_RF_BW_NOT_LISTED 0x3f +#define MSGID_SET_RF_BW_NOT_LISTED 0x11 +/* TODO */ + +#endif diff --git a/drivers/media/dvb-frontends/bsbe1-d01a.h b/drivers/media/dvb-frontends/bsbe1-d01a.h new file mode 100644 index 0000000000..700b429b7b --- /dev/null +++ b/drivers/media/dvb-frontends/bsbe1-d01a.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * bsbe1-d01a.h - ALPS BSBE1-D01A tuner support + * + * Copyright (C) 2011 Oliver Endriss <o.endriss@gmx.de> + * + * the project's page is at https://linuxtv.org + */ + +#ifndef BSBE1_D01A_H +#define BSBE1_D01A_H + +#include "stb6000.h" +#include "stv0288.h" + +static u8 stv0288_bsbe1_d01a_inittab[] = { + 0x01, 0x15, + 0x02, 0x20, + 0x09, 0x0, + 0x0a, 0x4, + 0x0b, 0x0, + 0x0c, 0x0, + 0x0d, 0x0, + 0x0e, 0xd4, + 0x0f, 0x30, + 0x11, 0x80, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0x45, + 0x16, 0xb7, + 0x17, 0x9c, + 0x18, 0x0, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0x0, + 0x23, 0x0, + 0x2b, 0xff, + 0x2c, 0xf7, + 0x30, 0x0, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbd, + 0x3a, 0x03, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x60, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x50, 0x10, + 0x51, 0x36, + 0x52, 0x09, + 0x53, 0x94, + 0x54, 0x62, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x0, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0x0, + 0x5f, 0xff, + 0x70, 0x0, + 0x71, 0x0, + 0x72, 0x0, + 0x74, 0x0, + 0x75, 0x0, + 0x76, 0x0, + 0x81, 0x0, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x0, + 0x85, 0x0, + 0x88, 0x0, + 0x89, 0x0, + 0x8a, 0x0, + 0x8b, 0x0, + 0x8c, 0x0, + 0x90, 0x0, + 0x91, 0x0, + 0x92, 0x0, + 0x93, 0x0, + 0x94, 0x1c, + 0x97, 0x0, + 0xa0, 0x48, + 0xa1, 0x0, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x0, + 0xf0, 0x0, + 0xf1, 0x0, + 0xf2, 0xc0, + 0xff, 0xff, +}; + +static struct stv0288_config stv0288_bsbe1_d01a_config = { + .demod_address = 0x68, + .min_delay_ms = 100, + .inittab = stv0288_bsbe1_d01a_inittab, +}; + +#endif diff --git a/drivers/media/dvb-frontends/bsbe1.h b/drivers/media/dvb-frontends/bsbe1.h new file mode 100644 index 0000000000..b4b81e1dd8 --- /dev/null +++ b/drivers/media/dvb-frontends/bsbe1.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * bsbe1.h - ALPS BSBE1 tuner support + * + * the project's page is at https://linuxtv.org + */ + +#ifndef BSBE1_H +#define BSBE1_H + +static u8 alps_bsbe1_inittab[] = { + 0x01, 0x15, /* XTAL = 4MHz, VCO = 352 MHz */ + 0x02, 0x30, /* MCLK = 88 MHz */ + 0x03, 0x00, /* ACR output 0 */ + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x05, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x00, /* DAC output 0 */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1/OP0 normal, val = 1 (LNB power on) */ + 0x0d, 0x82, /* DC offset compensation = on, beta_agc1 = 2 */ + 0x0f, 0x92, /* AGC1R */ + 0x10, 0x34, /* AGC2O */ + 0x11, 0x84, /* TLSR */ + 0x12, 0xb9, /* CFD */ + 0x15, 0xc9, /* lock detector threshold */ + 0x28, 0x00, /* out imp: normal, type: parallel, FEC mode: QPSK */ + 0x33, 0xfc, /* RS control */ + 0x34, 0x93, /* count viterbi bit errors per 2E18 bytes */ + 0xff, 0xff +}; + + +static int alps_bsbe1_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; } + else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; } + else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; } + else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; } + else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; } + else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio ) & 0xf0); + + return 0; +} + +static int alps_bsbe1_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int ret; + u8 data[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + struct i2c_adapter *i2c = fe->tuner_priv; + + if ((p->frequency < 950000) || (p->frequency > 2150000)) + return -EINVAL; + + div = p->frequency / 1000; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x80 | ((div & 0x18000) >> 10) | 0x1; + data[3] = 0xe0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer(i2c, &msg, 1); + return (ret != 1) ? -EIO : 0; +} + +static struct stv0299_config alps_bsbe1_config = { + .demod_address = 0x68, + .inittab = alps_bsbe1_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsbe1_set_symbol_rate, +}; + +#endif diff --git a/drivers/media/dvb-frontends/bsru6.h b/drivers/media/dvb-frontends/bsru6.h new file mode 100644 index 0000000000..a5ea22ff45 --- /dev/null +++ b/drivers/media/dvb-frontends/bsru6.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * bsru6.h - ALPS BSRU6 tuner support (moved from budget-ci.c) + * + * the project's page is at https://linuxtv.org + */ + +#ifndef BSRU6_H +#define BSRU6_H + +static u8 alps_bsru6_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x52, + 0xff, 0xff +}; + +static int alps_bsru6_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; + bclk = 0x47; + } else if (srate < 3000000) { + aclk = 0xb7; + bclk = 0x4b; + } else if (srate < 7000000) { + aclk = 0xb7; + bclk = 0x4f; + } else if (srate < 14000000) { + aclk = 0xb7; + bclk = 0x53; + } else if (srate < 30000000) { + aclk = 0xb6; + bclk = 0x53; + } else if (srate < 45000000) { + aclk = 0xb4; + bclk = 0x51; + } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, ratio & 0xf0); + + return 0; +} + +static int alps_bsru6_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u8 buf[4]; + u32 div; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + struct i2c_adapter *i2c = fe->tuner_priv; + + if ((p->frequency < 950000) || (p->frequency > 2150000)) + return -EINVAL; + + div = (p->frequency + (125 - 1)) / 125; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0xC4; + + if (p->frequency > 1530000) + buf[3] = 0xc0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(i2c, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct stv0299_config alps_bsru6_config = { + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, +}; + +#endif diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c new file mode 100644 index 0000000000..1d04c0a652 --- /dev/null +++ b/drivers/media/dvb-frontends/cx22700.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Conexant cx22700 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <media/dvb_frontend.h> +#include "cx22700.h" + + +struct cx22700_state { + + struct i2c_adapter* i2c; + + const struct cx22700_config* config; + + struct dvb_frontend frontend; +}; + + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "cx22700: " args); \ + } while (0) + +static u8 init_tab [] = { + 0x04, 0x10, + 0x05, 0x09, + 0x06, 0x00, + 0x08, 0x04, + 0x09, 0x00, + 0x0a, 0x01, + 0x15, 0x40, + 0x16, 0x10, + 0x17, 0x87, + 0x18, 0x17, + 0x1a, 0x10, + 0x25, 0x04, + 0x2e, 0x00, + 0x39, 0x00, + 0x3a, 0x04, + 0x45, 0x08, + 0x46, 0x02, + 0x47, 0x05, +}; + + +static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + dprintk ("%s\n", __func__); + + ret = i2c_transfer (state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int cx22700_readreg (struct cx22700_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + dprintk ("%s\n", __func__); + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) return -EIO; + + return b1[0]; +} + +static int cx22700_set_inversion (struct cx22700_state* state, int inversion) +{ + u8 val; + + dprintk ("%s\n", __func__); + + switch (inversion) { + case INVERSION_AUTO: + return -EOPNOTSUPP; + case INVERSION_ON: + val = cx22700_readreg (state, 0x09); + return cx22700_writereg (state, 0x09, val | 0x01); + case INVERSION_OFF: + val = cx22700_readreg (state, 0x09); + return cx22700_writereg (state, 0x09, val & 0xfe); + default: + return -EINVAL; + } +} + +static int cx22700_set_tps(struct cx22700_state *state, + struct dtv_frontend_properties *p) +{ + static const u8 qam_tab [4] = { 0, 1, 0, 2 }; + static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; + u8 val; + + dprintk ("%s\n", __func__); + + if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) + return -EINVAL; + + if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) + return -EINVAL; + + if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) + return -EINVAL; + + if ((int)p->guard_interval < GUARD_INTERVAL_1_32 || + p->guard_interval > GUARD_INTERVAL_1_4) + return -EINVAL; + + if (p->transmission_mode != TRANSMISSION_MODE_2K && + p->transmission_mode != TRANSMISSION_MODE_8K) + return -EINVAL; + + if (p->modulation != QPSK && + p->modulation != QAM_16 && + p->modulation != QAM_64) + return -EINVAL; + + if ((int)p->hierarchy < HIERARCHY_NONE || + p->hierarchy > HIERARCHY_4) + return -EINVAL; + + if (p->bandwidth_hz > 8000000 || p->bandwidth_hz < 6000000) + return -EINVAL; + + if (p->bandwidth_hz == 7000000) + cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10)); + else + cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10)); + + val = qam_tab[p->modulation - QPSK]; + val |= p->hierarchy - HIERARCHY_NONE; + + cx22700_writereg (state, 0x04, val); + + if (p->code_rate_HP - FEC_1_2 >= sizeof(fec_tab) || + p->code_rate_LP - FEC_1_2 >= sizeof(fec_tab)) + return -EINVAL; + val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; + val |= fec_tab[p->code_rate_LP - FEC_1_2]; + + cx22700_writereg (state, 0x05, val); + + val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; + val |= p->transmission_mode - TRANSMISSION_MODE_2K; + + cx22700_writereg (state, 0x06, val); + + cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */ + cx22700_writereg (state, 0x08, 0x04); /* restart acquisition */ + + return 0; +} + +static int cx22700_get_tps(struct cx22700_state *state, + struct dtv_frontend_properties *p) +{ + static const enum fe_modulation qam_tab[3] = { QPSK, QAM_16, QAM_64 }; + static const enum fe_code_rate fec_tab[5] = { + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8 + }; + u8 val; + + dprintk ("%s\n", __func__); + + if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */ + return -EAGAIN; + + val = cx22700_readreg (state, 0x01); + + if ((val & 0x7) > 4) + p->hierarchy = HIERARCHY_AUTO; + else + p->hierarchy = HIERARCHY_NONE + (val & 0x7); + + if (((val >> 3) & 0x3) > 2) + p->modulation = QAM_AUTO; + else + p->modulation = qam_tab[(val >> 3) & 0x3]; + + val = cx22700_readreg (state, 0x02); + + if (((val >> 3) & 0x07) > 4) + p->code_rate_HP = FEC_AUTO; + else + p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; + + if ((val & 0x07) > 4) + p->code_rate_LP = FEC_AUTO; + else + p->code_rate_LP = fec_tab[val & 0x07]; + + val = cx22700_readreg (state, 0x03); + + p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); + p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); + + return 0; +} + +static int cx22700_init (struct dvb_frontend* fe) + +{ struct cx22700_state* state = fe->demodulator_priv; + int i; + + dprintk("cx22700_init: init chip\n"); + + cx22700_writereg (state, 0x00, 0x02); /* soft reset */ + cx22700_writereg (state, 0x00, 0x00); + + msleep(10); + + for (i=0; i<sizeof(init_tab); i+=2) + cx22700_writereg (state, init_tab[i], init_tab[i+1]); + + cx22700_writereg (state, 0x00, 0x01); + + return 0; +} + +static int cx22700_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx22700_state* state = fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + u8 sync = cx22700_readreg (state, 0x07); + + *status = 0; + + if (rs_ber < 0xff00) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x20) + *status |= FE_HAS_CARRIER; + + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + + if (sync & 0x10) + *status |= FE_HAS_SYNC; + + if (*status == 0x0f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct cx22700_state* state = fe->demodulator_priv; + + *ber = cx22700_readreg (state, 0x0c) & 0x7f; + cx22700_writereg (state, 0x0c, 0x00); + + return 0; +} + +static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct cx22700_state* state = fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + *signal_strength = ~rs_ber; + + return 0; +} + +static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct cx22700_state* state = fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + *snr = ~rs_ber; + + return 0; +} + +static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct cx22700_state* state = fe->demodulator_priv; + + *ucblocks = cx22700_readreg (state, 0x0f); + cx22700_writereg (state, 0x0f, 0x00); + + return 0; +} + +static int cx22700_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx22700_state* state = fe->demodulator_priv; + + cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ + cx22700_writereg (state, 0x00, 0x00); + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + cx22700_set_inversion(state, c->inversion); + cx22700_set_tps(state, c); + cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ + cx22700_writereg (state, 0x00, 0x01); /* restart acquire */ + + return 0; +} + +static int cx22700_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cx22700_state* state = fe->demodulator_priv; + u8 reg09 = cx22700_readreg (state, 0x09); + + c->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; + return cx22700_get_tps(state, c); +} + +static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct cx22700_state* state = fe->demodulator_priv; + + if (enable) { + return cx22700_writereg(state, 0x0a, 0x00); + } else { + return cx22700_writereg(state, 0x0a, 0x01); + } +} + +static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 150; + fesettings->step_size = 166667; + fesettings->max_drift = 166667*2; + return 0; +} + +static void cx22700_release(struct dvb_frontend* fe) +{ + struct cx22700_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops cx22700_ops; + +struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c) +{ + struct cx22700_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if (cx22700_readreg(state, 0x07) < 0) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx22700_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops cx22700_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Conexant CX22700 DVB-T", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_RECOVER + }, + + .release = cx22700_release, + + .init = cx22700_init, + .i2c_gate_ctrl = cx22700_i2c_gate_ctrl, + + .set_frontend = cx22700_set_frontend, + .get_frontend = cx22700_get_frontend, + .get_tune_settings = cx22700_get_tune_settings, + + .read_status = cx22700_read_status, + .read_ber = cx22700_read_ber, + .read_signal_strength = cx22700_read_signal_strength, + .read_snr = cx22700_read_snr, + .read_ucblocks = cx22700_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(cx22700_attach); diff --git a/drivers/media/dvb-frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h new file mode 100644 index 0000000000..178dd6b3af --- /dev/null +++ b/drivers/media/dvb-frontends/cx22700.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Conexant CX22700 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + +*/ + +#ifndef CX22700_H +#define CX22700_H + +#include <linux/dvb/frontend.h> + +struct cx22700_config +{ + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_CX22700) +extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_CX22700 + +#endif // CX22700_H diff --git a/drivers/media/dvb-frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c new file mode 100644 index 0000000000..61ad34b700 --- /dev/null +++ b/drivers/media/dvb-frontends/cx22702.c @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Conexant 22702 DVB OFDM demodulator driver + + based on: + Alps TDMB7 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + Copyright (C) 2004 Steven Toth <stoth@linuxtv.org> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <media/dvb_frontend.h> +#include "cx22702.h" + +struct cx22702_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct cx22702_config *config; + + struct dvb_frontend frontend; + + /* previous uncorrected block counter */ + u8 prevUCBlocks; +}; + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +#define dprintk if (debug) printk + +/* Register values to initialise the demod */ +static const u8 init_tab[] = { + 0x00, 0x00, /* Stop acquisition */ + 0x0B, 0x06, + 0x09, 0x01, + 0x0D, 0x41, + 0x16, 0x32, + 0x20, 0x0A, + 0x21, 0x17, + 0x24, 0x3e, + 0x26, 0xff, + 0x27, 0x10, + 0x28, 0x00, + 0x29, 0x00, + 0x2a, 0x10, + 0x2b, 0x00, + 0x2c, 0x10, + 0x2d, 0x00, + 0x48, 0xd4, + 0x49, 0x56, + 0x6b, 0x1e, + 0xc8, 0x02, + 0xf9, 0x00, + 0xfa, 0x00, + 0xfb, 0x00, + 0xfc, 0x00, + 0xfd, 0x00, +}; + +static int cx22702_writereg(struct cx22702_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, .flags = 0, + .buf = buf, .len = 2 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (unlikely(ret != 1)) { + printk(KERN_ERR + "%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + return -1; + } + + return 0; +} + +static u8 cx22702_readreg(struct cx22702_state *state, u8 reg) +{ + int ret; + u8 data; + + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = &data, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (unlikely(ret != 2)) { + printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + return 0; + } + + return data; +} + +static int cx22702_set_inversion(struct cx22702_state *state, int inversion) +{ + u8 val; + + val = cx22702_readreg(state, 0x0C); + switch (inversion) { + case INVERSION_AUTO: + return -EOPNOTSUPP; + case INVERSION_ON: + val |= 0x01; + break; + case INVERSION_OFF: + val &= 0xfe; + break; + default: + return -EINVAL; + } + return cx22702_writereg(state, 0x0C, val); +} + +/* Retrieve the demod settings */ +static int cx22702_get_tps(struct cx22702_state *state, + struct dtv_frontend_properties *p) +{ + u8 val; + + /* Make sure the TPS regs are valid */ + if (!(cx22702_readreg(state, 0x0A) & 0x20)) + return -EAGAIN; + + val = cx22702_readreg(state, 0x01); + switch ((val & 0x18) >> 3) { + case 0: + p->modulation = QPSK; + break; + case 1: + p->modulation = QAM_16; + break; + case 2: + p->modulation = QAM_64; + break; + } + switch (val & 0x07) { + case 0: + p->hierarchy = HIERARCHY_NONE; + break; + case 1: + p->hierarchy = HIERARCHY_1; + break; + case 2: + p->hierarchy = HIERARCHY_2; + break; + case 3: + p->hierarchy = HIERARCHY_4; + break; + } + + + val = cx22702_readreg(state, 0x02); + switch ((val & 0x38) >> 3) { + case 0: + p->code_rate_HP = FEC_1_2; + break; + case 1: + p->code_rate_HP = FEC_2_3; + break; + case 2: + p->code_rate_HP = FEC_3_4; + break; + case 3: + p->code_rate_HP = FEC_5_6; + break; + case 4: + p->code_rate_HP = FEC_7_8; + break; + } + switch (val & 0x07) { + case 0: + p->code_rate_LP = FEC_1_2; + break; + case 1: + p->code_rate_LP = FEC_2_3; + break; + case 2: + p->code_rate_LP = FEC_3_4; + break; + case 3: + p->code_rate_LP = FEC_5_6; + break; + case 4: + p->code_rate_LP = FEC_7_8; + break; + } + + val = cx22702_readreg(state, 0x03); + switch ((val & 0x0c) >> 2) { + case 0: + p->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + p->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + p->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + p->guard_interval = GUARD_INTERVAL_1_4; + break; + } + switch (val & 0x03) { + case 0: + p->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + p->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + return 0; +} + +static int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct cx22702_state *state = fe->demodulator_priv; + u8 val; + + dprintk("%s(%d)\n", __func__, enable); + val = cx22702_readreg(state, 0x0D); + if (enable) + val &= 0xfe; + else + val |= 0x01; + return cx22702_writereg(state, 0x0D, val); +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int cx22702_set_tps(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u8 val; + struct cx22702_state *state = fe->demodulator_priv; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* set inversion */ + cx22702_set_inversion(state, p->inversion); + + /* set bandwidth */ + val = cx22702_readreg(state, 0x0C) & 0xcf; + switch (p->bandwidth_hz) { + case 6000000: + val |= 0x20; + break; + case 7000000: + val |= 0x10; + break; + case 8000000: + break; + default: + dprintk("%s: invalid bandwidth\n", __func__); + return -EINVAL; + } + cx22702_writereg(state, 0x0C, val); + + p->code_rate_LP = FEC_AUTO; /* temp hack as manual not working */ + + /* use auto configuration? */ + if ((p->hierarchy == HIERARCHY_AUTO) || + (p->modulation == QAM_AUTO) || + (p->code_rate_HP == FEC_AUTO) || + (p->code_rate_LP == FEC_AUTO) || + (p->guard_interval == GUARD_INTERVAL_AUTO) || + (p->transmission_mode == TRANSMISSION_MODE_AUTO)) { + + /* TPS Source - use hardware driven values */ + cx22702_writereg(state, 0x06, 0x10); + cx22702_writereg(state, 0x07, 0x9); + cx22702_writereg(state, 0x08, 0xC1); + cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) + & 0xfc); + cx22702_writereg(state, 0x0C, + (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); + cx22702_writereg(state, 0x00, 0x01); /* Begin acquisition */ + dprintk("%s: Autodetecting\n", __func__); + return 0; + } + + /* manually programmed values */ + switch (p->modulation) { /* mask 0x18 */ + case QPSK: + val = 0x00; + break; + case QAM_16: + val = 0x08; + break; + case QAM_64: + val = 0x10; + break; + default: + dprintk("%s: invalid modulation\n", __func__); + return -EINVAL; + } + switch (p->hierarchy) { /* mask 0x07 */ + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + val |= 0x01; + break; + case HIERARCHY_2: + val |= 0x02; + break; + case HIERARCHY_4: + val |= 0x03; + break; + default: + dprintk("%s: invalid hierarchy\n", __func__); + return -EINVAL; + } + cx22702_writereg(state, 0x06, val); + + switch (p->code_rate_HP) { /* mask 0x38 */ + case FEC_NONE: + case FEC_1_2: + val = 0x00; + break; + case FEC_2_3: + val = 0x08; + break; + case FEC_3_4: + val = 0x10; + break; + case FEC_5_6: + val = 0x18; + break; + case FEC_7_8: + val = 0x20; + break; + default: + dprintk("%s: invalid code_rate_HP\n", __func__); + return -EINVAL; + } + switch (p->code_rate_LP) { /* mask 0x07 */ + case FEC_NONE: + case FEC_1_2: + break; + case FEC_2_3: + val |= 0x01; + break; + case FEC_3_4: + val |= 0x02; + break; + case FEC_5_6: + val |= 0x03; + break; + case FEC_7_8: + val |= 0x04; + break; + default: + dprintk("%s: invalid code_rate_LP\n", __func__); + return -EINVAL; + } + cx22702_writereg(state, 0x07, val); + + switch (p->guard_interval) { /* mask 0x0c */ + case GUARD_INTERVAL_1_32: + val = 0x00; + break; + case GUARD_INTERVAL_1_16: + val = 0x04; + break; + case GUARD_INTERVAL_1_8: + val = 0x08; + break; + case GUARD_INTERVAL_1_4: + val = 0x0c; + break; + default: + dprintk("%s: invalid guard_interval\n", __func__); + return -EINVAL; + } + switch (p->transmission_mode) { /* mask 0x03 */ + case TRANSMISSION_MODE_2K: + break; + case TRANSMISSION_MODE_8K: + val |= 0x1; + break; + default: + dprintk("%s: invalid transmission_mode\n", __func__); + return -EINVAL; + } + cx22702_writereg(state, 0x08, val); + cx22702_writereg(state, 0x0B, + (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02); + cx22702_writereg(state, 0x0C, + (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); + + /* Begin channel acquisition */ + cx22702_writereg(state, 0x00, 0x01); + + return 0; +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int cx22702_init(struct dvb_frontend *fe) +{ + int i; + struct cx22702_state *state = fe->demodulator_priv; + + cx22702_writereg(state, 0x00, 0x02); + + msleep(10); + + for (i = 0; i < ARRAY_SIZE(init_tab); i += 2) + cx22702_writereg(state, init_tab[i], init_tab[i + 1]); + + cx22702_writereg(state, 0xf8, (state->config->output_mode << 1) + & 0x02); + + cx22702_i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int cx22702_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx22702_state *state = fe->demodulator_priv; + u8 reg0A; + u8 reg23; + + *status = 0; + + reg0A = cx22702_readreg(state, 0x0A); + reg23 = cx22702_readreg(state, 0x23); + + dprintk("%s: status demod=0x%02x agc=0x%02x\n" + , __func__, reg0A, reg23); + + if (reg0A & 0x10) { + *status |= FE_HAS_LOCK; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + } + + if (reg0A & 0x20) + *status |= FE_HAS_CARRIER; + + if (reg23 < 0xf0) + *status |= FE_HAS_SIGNAL; + + return 0; +} + +static int cx22702_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cx22702_state *state = fe->demodulator_priv; + + if (cx22702_readreg(state, 0xE4) & 0x02) { + /* Realtime statistics */ + *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 + | (cx22702_readreg(state, 0xDF) & 0x7F); + } else { + /* Averagtine statistics */ + *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 + | cx22702_readreg(state, 0xDF); + } + + return 0; +} + +static int cx22702_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct cx22702_state *state = fe->demodulator_priv; + u8 reg23; + + /* + * Experience suggests that the strength signal register works as + * follows: + * - In the absence of signal, value is 0xff. + * - In the presence of a weak signal, bit 7 is set, not sure what + * the lower 7 bits mean. + * - In the presence of a strong signal, the register holds a 7-bit + * value (bit 7 is cleared), with greater values standing for + * weaker signals. + */ + reg23 = cx22702_readreg(state, 0x23); + if (reg23 & 0x80) { + *signal_strength = 0; + } else { + reg23 = ~reg23 & 0x7f; + /* Scale to 16 bit */ + *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5); + } + + return 0; +} + +static int cx22702_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct cx22702_state *state = fe->demodulator_priv; + + u16 rs_ber; + if (cx22702_readreg(state, 0xE4) & 0x02) { + /* Realtime statistics */ + rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 + | (cx22702_readreg(state, 0xDF) & 0x7F); + } else { + /* Averagine statistics */ + rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 8 + | cx22702_readreg(state, 0xDF); + } + *snr = ~rs_ber; + + return 0; +} + +static int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct cx22702_state *state = fe->demodulator_priv; + + u8 _ucblocks; + + /* RS Uncorrectable Packet Count then reset */ + _ucblocks = cx22702_readreg(state, 0xE3); + if (state->prevUCBlocks < _ucblocks) + *ucblocks = (_ucblocks - state->prevUCBlocks); + else + *ucblocks = state->prevUCBlocks - _ucblocks; + state->prevUCBlocks = _ucblocks; + + return 0; +} + +static int cx22702_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cx22702_state *state = fe->demodulator_priv; + + u8 reg0C = cx22702_readreg(state, 0x0C); + + c->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; + return cx22702_get_tps(state, c); +} + +static int cx22702_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void cx22702_release(struct dvb_frontend *fe) +{ + struct cx22702_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops cx22702_ops; + +struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, + struct i2c_adapter *i2c) +{ + struct cx22702_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if (cx22702_readreg(state, 0x1f) != 0x3) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx22702_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(cx22702_attach); + +static const struct dvb_frontend_ops cx22702_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Conexant CX22702 DVB-T", + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + + .release = cx22702_release, + + .init = cx22702_init, + .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, + + .set_frontend = cx22702_set_tps, + .get_frontend = cx22702_get_frontend, + .get_tune_settings = cx22702_get_tune_settings, + + .read_status = cx22702_read_status, + .read_ber = cx22702_read_ber, + .read_signal_strength = cx22702_read_signal_strength, + .read_snr = cx22702_read_snr, + .read_ucblocks = cx22702_read_ucblocks, +}; + +MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cx22702.h b/drivers/media/dvb-frontends/cx22702.h new file mode 100644 index 0000000000..cfae96738e --- /dev/null +++ b/drivers/media/dvb-frontends/cx22702.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Conexant 22702 DVB OFDM demodulator driver + + based on: + Alps TDMB7 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler <holger@convergence.de> + + Copyright (C) 2004 Steven Toth <stoth@linuxtv.org> + + +*/ + +#ifndef CX22702_H +#define CX22702_H + +#include <linux/dvb/frontend.h> + +struct cx22702_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* serial/parallel output */ +#define CX22702_PARALLEL_OUTPUT 0 +#define CX22702_SERIAL_OUTPUT 1 + u8 output_mode; +}; + +#if IS_REACHABLE(CONFIG_DVB_CX22702) +extern struct dvb_frontend *cx22702_attach( + const struct cx22702_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *cx22702_attach( + const struct cx22702_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c new file mode 100644 index 0000000000..9aeea08975 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24110.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + cx24110 - Single Chip Satellite Channel Receiver driver module + + Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on + work + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + + +*/ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> + +#include <media/dvb_frontend.h> +#include "cx24110.h" + + +struct cx24110_state { + + struct i2c_adapter* i2c; + + const struct cx24110_config* config; + + struct dvb_frontend frontend; + + u32 lastber; + u32 lastbler; + u32 lastesn0; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "cx24110: " args); \ + } while (0) + +static struct {u8 reg; u8 data;} cx24110_regdata[]= + /* Comments beginning with @ denote this value should + be the default */ + {{0x09,0x01}, /* SoftResetAll */ + {0x09,0x00}, /* release reset */ + {0x01,0xe8}, /* MSB of code rate 27.5MS/s */ + {0x02,0x17}, /* middle byte " */ + {0x03,0x29}, /* LSB " */ + {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */ + {0x06,0xa5}, /* @ PLL 60MHz */ + {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */ + {0x0a,0x00}, /* @ partial chip disables, do not set */ + {0x0b,0x01}, /* set output clock in gapped mode, start signal low + active for first byte */ + {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */ + {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */ + {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1 + to avoid starting the BER counter. Reset the + CRC test bit. Finite counting selected */ + {0x15,0xff}, /* @ size of the limited time window for RS BER + estimation. It is <value>*256 RS blocks, this + gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */ + {0x16,0x00}, /* @ enable all RS output ports */ + {0x17,0x04}, /* @ time window allowed for the RS to sync */ + {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned + for automatically */ + /* leave the current code rate and normalization + registers as they are after reset... */ + {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting + only once */ + {0x23,0x18}, /* @ size of the limited time window for Viterbi BER + estimation. It is <value>*65536 channel bits, i.e. + approx. 38ms at 27.5MS/s, rate 3/4 */ + {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */ + /* leave front-end AGC parameters at default values */ + /* leave decimation AGC parameters at default values */ + {0x35,0x40}, /* disable all interrupts. They are not connected anyway */ + {0x36,0xff}, /* clear all interrupt pending flags */ + {0x37,0x00}, /* @ fully enable AutoAcqq state machine */ + {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */ + /* leave the equalizer parameters on their default values */ + /* leave the final AGC parameters on their default values */ + {0x41,0x00}, /* @ MSB of front-end derotator frequency */ + {0x42,0x00}, /* @ middle bytes " */ + {0x43,0x00}, /* @ LSB " */ + /* leave the carrier tracking loop parameters on default */ + /* leave the bit timing loop parameters at default */ + {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */ + /* the cx24108 data sheet for symbol rates above 15MS/s */ + {0x57,0x00}, /* @ Filter sigma delta enabled, positive */ + {0x61,0x95}, /* GPIO pins 1-4 have special function */ + {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */ + {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */ + {0x64,0x20}, /* GPIO 6 is input, all others are outputs */ + {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */ + {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */ + {0x73,0x00}, /* @ disable several demod bypasses */ + {0x74,0x00}, /* @ " */ + {0x75,0x00} /* @ " */ + /* the remaining registers are for SEC */ + }; + + +static int cx24110_writereg (struct cx24110_state* state, int reg, int data) +{ + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int err; + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { + dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int cx24110_readreg (struct cx24110_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) return ret; + + return b1[0]; +} + +static int cx24110_set_inversion(struct cx24110_state *state, + enum fe_spectral_inversion inversion) +{ +/* fixme (low): error handling */ + + switch (inversion) { + case INVERSION_OFF: + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); + /* AcqSpectrInvDis on. No idea why someone should want this */ + cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7); + /* Initial value 0 at start of acq */ + cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef); + /* current value 0 */ + /* The cx24110 manual tells us this reg is read-only. + But what the heck... set it ayways */ + break; + case INVERSION_ON: + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); + /* AcqSpectrInvDis on. No idea why someone should want this */ + cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08); + /* Initial value 1 at start of acq */ + cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10); + /* current value 1 */ + break; + case INVERSION_AUTO: + cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe); + /* AcqSpectrInvDis off. Leave initial & current states as is */ + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cx24110_set_fec(struct cx24110_state *state, enum fe_code_rate fec) +{ + static const int rate[FEC_AUTO] = {-1, 1, 2, 3, 5, 7, -1}; + static const int g1[FEC_AUTO] = {-1, 0x01, 0x02, 0x05, 0x15, 0x45, -1}; + static const int g2[FEC_AUTO] = {-1, 0x01, 0x03, 0x06, 0x1a, 0x7a, -1}; + + /* Well, the AutoAcq engine of the cx24106 and 24110 automatically + searches all enabled viterbi rates, and can handle non-standard + rates as well. */ + + if (fec > FEC_AUTO) + fec = FEC_AUTO; + + if (fec == FEC_AUTO) { /* (re-)establish AutoAcq behaviour */ + cx24110_writereg(state, 0x37, cx24110_readreg(state, 0x37) & 0xdf); + /* clear AcqVitDis bit */ + cx24110_writereg(state, 0x18, 0xae); + /* allow all DVB standard code rates */ + cx24110_writereg(state, 0x05, (cx24110_readreg(state, 0x05) & 0xf0) | 0x3); + /* set nominal Viterbi rate 3/4 */ + cx24110_writereg(state, 0x22, (cx24110_readreg(state, 0x22) & 0xf0) | 0x3); + /* set current Viterbi rate 3/4 */ + cx24110_writereg(state, 0x1a, 0x05); + cx24110_writereg(state, 0x1b, 0x06); + /* set the puncture registers for code rate 3/4 */ + return 0; + } else { + cx24110_writereg(state, 0x37, cx24110_readreg(state, 0x37) | 0x20); + /* set AcqVitDis bit */ + if (rate[fec] < 0) + return -EINVAL; + + cx24110_writereg(state, 0x05, (cx24110_readreg(state, 0x05) & 0xf0) | rate[fec]); + /* set nominal Viterbi rate */ + cx24110_writereg(state, 0x22, (cx24110_readreg(state, 0x22) & 0xf0) | rate[fec]); + /* set current Viterbi rate */ + cx24110_writereg(state, 0x1a, g1[fec]); + cx24110_writereg(state, 0x1b, g2[fec]); + /* not sure if this is the right way: I always used AutoAcq mode */ + } + return 0; +} + +static enum fe_code_rate cx24110_get_fec(struct cx24110_state *state) +{ + int i; + + i=cx24110_readreg(state,0x22)&0x0f; + if(!(i&0x08)) { + return FEC_1_2 + i - 1; + } else { +/* fixme (low): a special code rate has been selected. In theory, we need to + return a denominator value, a numerator value, and a pair of puncture + maps to correctly describe this mode. But this should never happen in + practice, because it cannot be set by cx24110_get_fec. */ + return FEC_NONE; + } +} + +static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) +{ +/* fixme (low): add error handling */ + u32 ratio; + u32 tmp, fclk, BDRI; + + static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; + int i; + + dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate); + if (srate>90999000UL/2) + srate=90999000UL/2; + if (srate<500000) + srate=500000; + + for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++) + ; + /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, + and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, + R06[3:0] PLLphaseDetGain */ + tmp=cx24110_readreg(state,0x07)&0xfc; + if(srate<90999000UL/4) { /* sample rate 45MHz*/ + cx24110_writereg(state,0x07,tmp); + cx24110_writereg(state,0x06,0x78); + fclk=90999000UL/2; + } else if(srate<60666000UL/2) { /* sample rate 60MHz */ + cx24110_writereg(state,0x07,tmp|0x1); + cx24110_writereg(state,0x06,0xa5); + fclk=60666000UL; + } else if(srate<80888000UL/2) { /* sample rate 80MHz */ + cx24110_writereg(state,0x07,tmp|0x2); + cx24110_writereg(state,0x06,0x87); + fclk=80888000UL; + } else { /* sample rate 90MHz */ + cx24110_writereg(state,0x07,tmp|0x3); + cx24110_writereg(state,0x06,0x78); + fclk=90999000UL; + } + dprintk("cx24110 debug: fclk %d Hz\n",fclk); + /* we need to divide two integers with approx. 27 bits in 32 bit + arithmetic giving a 25 bit result */ + /* the maximum dividend is 90999000/2, 0x02b6446c, this number is + also the most complex divisor. Hence, the dividend has, + assuming 32bit unsigned arithmetic, 6 clear bits on top, the + divisor 2 unused bits at the bottom. Also, the quotient is + always less than 1/2. Borrowed from VES1893.c, of course */ + + tmp=srate<<6; + BDRI=fclk>>2; + ratio=(tmp/BDRI); + + tmp=(tmp%BDRI)<<8; + ratio=(ratio<<8)+(tmp/BDRI); + + tmp=(tmp%BDRI)<<8; + ratio=(ratio<<8)+(tmp/BDRI); + + tmp=(tmp%BDRI)<<1; + ratio=(ratio<<1)+(tmp/BDRI); + + dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]); + dprintk("fclk = %d\n", fclk); + dprintk("ratio= %08x\n", ratio); + + cx24110_writereg(state, 0x1, (ratio>>16)&0xff); + cx24110_writereg(state, 0x2, (ratio>>8)&0xff); + cx24110_writereg(state, 0x3, (ratio)&0xff); + + return 0; + +} + +static int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len) +{ + struct cx24110_state *state = fe->demodulator_priv; + + if (len != 3) + return -EINVAL; + +/* tuner data is 21 bits long, must be left-aligned in data */ +/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ +/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ + + cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ + cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ + + /* if the auto tuner writer is still busy, clear it out */ + while (cx24110_readreg(state,0x6d)&0x80) + cx24110_writereg(state,0x72,0); + + /* write the topmost 8 bits */ + cx24110_writereg(state,0x72,buf[0]); + + /* wait for the send to be completed */ + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* send another 8 bytes */ + cx24110_writereg(state,0x72,buf[1]); + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* and the topmost 5 bits of this byte */ + cx24110_writereg(state,0x72,buf[2]); + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* now strobe the enable line once */ + cx24110_writereg(state,0x6d,0x32); + cx24110_writereg(state,0x6d,0x30); + + return 0; +} + +static int cx24110_initfe(struct dvb_frontend* fe) +{ + struct cx24110_state *state = fe->demodulator_priv; +/* fixme (low): error handling */ + int i; + + dprintk("%s: init chip\n", __func__); + + for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) { + cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); + } + + return 0; +} + +static int cx24110_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct cx24110_state *state = fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0); + case SEC_VOLTAGE_18: + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40); + default: + return -EINVAL; + } +} + +static int cx24110_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + int rv, bit; + struct cx24110_state *state = fe->demodulator_priv; + unsigned long timeout; + + if (burst == SEC_MINI_A) + bit = 0x00; + else if (burst == SEC_MINI_B) + bit = 0x08; + else + return -EINVAL; + + rv = cx24110_readreg(state, 0x77); + if (!(rv & 0x04)) + cx24110_writereg(state, 0x77, rv | 0x04); + + rv = cx24110_readreg(state, 0x76); + cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit)); + timeout = jiffies + msecs_to_jiffies(100); + while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) + ; /* wait for LNB ready */ + + return 0; +} + +static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *cmd) +{ + int i, rv; + struct cx24110_state *state = fe->demodulator_priv; + unsigned long timeout; + + if (cmd->msg_len < 3 || cmd->msg_len > 6) + return -EINVAL; /* not implemented */ + + for (i = 0; i < cmd->msg_len; i++) + cx24110_writereg(state, 0x79 + i, cmd->msg[i]); + + rv = cx24110_readreg(state, 0x77); + if (rv & 0x04) { + cx24110_writereg(state, 0x77, rv & ~0x04); + msleep(30); /* reportedly fixes switching problems */ + } + + rv = cx24110_readreg(state, 0x76); + + cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); + timeout = jiffies + msecs_to_jiffies(100); + while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) + ; /* wait for LNB ready */ + + return 0; +} + +static int cx24110_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct cx24110_state *state = fe->demodulator_priv; + + int sync = cx24110_readreg (state, 0x55); + + *status = 0; + + if (sync & 0x10) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x08) + *status |= FE_HAS_CARRIER; + + sync = cx24110_readreg (state, 0x08); + + if (sync & 0x40) + *status |= FE_HAS_VITERBI; + + if (sync & 0x20) + *status |= FE_HAS_SYNC; + + if ((sync & 0x60) == 0x60) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct cx24110_state *state = fe->demodulator_priv; + + /* fixme (maybe): value range is 16 bit. Scale? */ + if(cx24110_readreg(state,0x24)&0x10) { + /* the Viterbi error counter has finished one counting window */ + cx24110_writereg(state,0x24,0x04); /* select the ber reg */ + state->lastber=cx24110_readreg(state,0x25)| + (cx24110_readreg(state,0x26)<<8); + cx24110_writereg(state,0x24,0x04); /* start new count window */ + cx24110_writereg(state,0x24,0x14); + } + *ber = state->lastber; + + return 0; +} + +static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct cx24110_state *state = fe->demodulator_priv; + +/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ + u8 signal = cx24110_readreg (state, 0x27)+128; + *signal_strength = (signal << 8) | signal; + + return 0; +} + +static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct cx24110_state *state = fe->demodulator_priv; + + /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ + if(cx24110_readreg(state,0x6a)&0x80) { + /* the Es/N0 error counter has finished one counting window */ + state->lastesn0=cx24110_readreg(state,0x69)| + (cx24110_readreg(state,0x68)<<8); + cx24110_writereg(state,0x6a,0x84); /* start new count window */ + } + *snr = state->lastesn0; + + return 0; +} + +static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct cx24110_state *state = fe->demodulator_priv; + + if(cx24110_readreg(state,0x10)&0x40) { + /* the RS error counter has finished one counting window */ + cx24110_writereg(state,0x10,0x60); /* select the byer reg */ + (void)(cx24110_readreg(state, 0x12) | + (cx24110_readreg(state, 0x13) << 8) | + (cx24110_readreg(state, 0x14) << 16)); + cx24110_writereg(state,0x10,0x70); /* select the bler reg */ + state->lastbler=cx24110_readreg(state,0x12)| + (cx24110_readreg(state,0x13)<<8)| + (cx24110_readreg(state,0x14)<<16); + cx24110_writereg(state,0x10,0x20); /* start new count window */ + } + *ucblocks = state->lastbler; + + return 0; +} + +static int cx24110_set_frontend(struct dvb_frontend *fe) +{ + struct cx24110_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + cx24110_set_inversion(state, p->inversion); + cx24110_set_fec(state, p->fec_inner); + cx24110_set_symbolrate(state, p->symbol_rate); + cx24110_writereg(state,0x04,0x05); /* start acquisition */ + + return 0; +} + +static int cx24110_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct cx24110_state *state = fe->demodulator_priv; + s32 afc; unsigned sclk; + +/* cannot read back tuner settings (freq). Need to have some private storage */ + + sclk = cx24110_readreg (state, 0x07) & 0x03; +/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. + * Need 64 bit arithmetic. Is thiss possible in the kernel? */ + if (sclk==0) sclk=90999000L/2L; + else if (sclk==1) sclk=60666000L; + else if (sclk==2) sclk=80888000L; + else sclk=90999000L; + sclk>>=8; + afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+ + ((sclk*cx24110_readreg (state, 0x45))>>8)+ + ((sclk*cx24110_readreg (state, 0x46))>>16); + + p->frequency += afc; + p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ? + INVERSION_ON : INVERSION_OFF; + p->fec_inner = cx24110_get_fec(state); + + return 0; +} + +static int cx24110_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct cx24110_state *state = fe->demodulator_priv; + + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0)); +} + +static void cx24110_release(struct dvb_frontend* fe) +{ + struct cx24110_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops cx24110_ops; + +struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c) +{ + struct cx24110_state* state = NULL; + int ret; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->lastber = 0; + state->lastbler = 0; + state->lastesn0 = 0; + + /* check if the demod is there */ + ret = cx24110_readreg(state, 0x00); + if ((ret != 0x5a) && (ret != 0x69)) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx24110_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops cx24110_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Conexant CX24110 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 29500 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24110_release, + + .init = cx24110_initfe, + .write = _cx24110_pll_write, + .set_frontend = cx24110_set_frontend, + .get_frontend = cx24110_get_frontend, + .read_status = cx24110_read_status, + .read_ber = cx24110_read_ber, + .read_signal_strength = cx24110_read_signal_strength, + .read_snr = cx24110_read_snr, + .read_ucblocks = cx24110_read_ucblocks, + + .diseqc_send_master_cmd = cx24110_send_diseqc_msg, + .set_tone = cx24110_set_tone, + .set_voltage = cx24110_set_voltage, + .diseqc_send_burst = cx24110_diseqc_send_burst, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); +MODULE_AUTHOR("Peter Hettkamp"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(cx24110_attach); diff --git a/drivers/media/dvb-frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h new file mode 100644 index 0000000000..834b011d34 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24110.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + cx24110 - Single Chip Satellite Channel Receiver driver module + + Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on + work + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + + +*/ + +#ifndef CX24110_H +#define CX24110_H + +#include <linux/dvb/frontend.h> + +struct cx24110_config +{ + /* the demodulator's i2c address */ + u8 demod_address; +}; + +static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) +{ + u8 buf[] = { + (u8)((val >> 24) & 0xff), + (u8)((val >> 16) & 0xff), + (u8)((val >> 8) & 0xff) + }; + + if (fe->ops.write) + return fe->ops.write(fe, buf, 3); + return 0; +} + +#if IS_REACHABLE(CONFIG_DVB_CX24110) +extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_CX24110 + +#endif // CX24110_H diff --git a/drivers/media/dvb-frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c new file mode 100644 index 0000000000..203cb6b3f9 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24113.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Conexant CX24113/CX24128 Tuner (Satellite) + * + * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org> + * + * Developed for BBTI / Technisat + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> + +#include <media/dvb_frontend.h> +#include "cx24113.h" + +static int debug; + +#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) +#define cx_err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) + +#define dprintk(args...) \ + do { \ + if (debug) { \ + printk(KERN_DEBUG "CX24113: %s: ", __func__); \ + printk(args); \ + } \ + } while (0) + +struct cx24113_state { + struct i2c_adapter *i2c; + const struct cx24113_config *config; + +#define REV_CX24113 0x23 + u8 rev; + u8 ver; + + u8 icp_mode:1; + +#define ICP_LEVEL1 0 +#define ICP_LEVEL2 1 +#define ICP_LEVEL3 2 +#define ICP_LEVEL4 3 + u8 icp_man:2; + u8 icp_auto_low:2; + u8 icp_auto_mlow:2; + u8 icp_auto_mhi:2; + u8 icp_auto_hi:2; + u8 icp_dig; + +#define LNA_MIN_GAIN 0 +#define LNA_MID_GAIN 1 +#define LNA_MAX_GAIN 2 + u8 lna_gain:2; + + u8 acp_on:1; + + u8 vco_mode:2; + u8 vco_shift:1; +#define VCOBANDSEL_6 0x80 +#define VCOBANDSEL_5 0x01 +#define VCOBANDSEL_4 0x02 +#define VCOBANDSEL_3 0x04 +#define VCOBANDSEL_2 0x08 +#define VCOBANDSEL_1 0x10 + u8 vco_band; + +#define VCODIV4 4 +#define VCODIV2 2 + u8 vcodiv; + + u8 bs_delay:4; + u16 bs_freqcnt:13; + u16 bs_rdiv; + u8 prescaler_mode:1; + + u8 rfvga_bias_ctrl; + + s16 tuner_gain_thres; + u8 gain_level; + + u32 frequency; + + u8 refdiv; + + u8 Fwindow_enabled; +}; + +static int cx24113_writereg(struct cx24113_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->i2c_addr, + .flags = 0, .buf = buf, .len = 2 }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, err, reg, data); + return err; + } + + return 0; +} + +static int cx24113_readreg(struct cx24113_state *state, u8 reg) +{ + int ret; + u8 b; + struct i2c_msg msg[] = { + { .addr = state->config->i2c_addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->i2c_addr, + .flags = I2C_M_RD, .buf = &b, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", + __func__, reg, ret); + return ret; + } + + return b; +} + +static void cx24113_set_parameters(struct cx24113_state *state) +{ + u8 r; + + r = cx24113_readreg(state, 0x10) & 0x82; + r |= state->icp_mode; + r |= state->icp_man << 4; + r |= state->icp_dig << 2; + r |= state->prescaler_mode << 5; + cx24113_writereg(state, 0x10, r); + + r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) + | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); + cx24113_writereg(state, 0x11, r); + + if (state->rev == REV_CX24113) { + r = cx24113_readreg(state, 0x20) & 0xec; + r |= state->lna_gain; + r |= state->rfvga_bias_ctrl << 4; + cx24113_writereg(state, 0x20, r); + } + + r = cx24113_readreg(state, 0x12) & 0x03; + r |= state->acp_on << 2; + r |= state->bs_delay << 4; + cx24113_writereg(state, 0x12, r); + + r = cx24113_readreg(state, 0x18) & 0x40; + r |= state->vco_shift; + if (state->vco_band == VCOBANDSEL_6) + r |= (1 << 7); + else + r |= (state->vco_band << 1); + cx24113_writereg(state, 0x18, r); + + r = cx24113_readreg(state, 0x14) & 0x20; + r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); + cx24113_writereg(state, 0x14, r); + cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); + + cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); + r = (cx24113_readreg(state, 0x17) & 0x0f) | + ((state->bs_rdiv & 0x0f) << 4); + cx24113_writereg(state, 0x17, r); +} + +#define VGA_0 0x00 +#define VGA_1 0x04 +#define VGA_2 0x02 +#define VGA_3 0x06 +#define VGA_4 0x01 +#define VGA_5 0x05 +#define VGA_6 0x03 +#define VGA_7 0x07 + +#define RFVGA_0 0x00 +#define RFVGA_1 0x01 +#define RFVGA_2 0x02 +#define RFVGA_3 0x03 + +static int cx24113_set_gain_settings(struct cx24113_state *state, + s16 power_estimation) +{ + u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, + vga = cx24113_readreg(state, 0x1f) & 0x3f, + rfvga = cx24113_readreg(state, 0x20) & 0xf3; + u8 gain_level = power_estimation >= state->tuner_gain_thres; + + dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", + power_estimation, state->tuner_gain_thres, + state->gain_level, gain_level); + + if (gain_level == state->gain_level) + return 0; /* nothing to be done */ + + ampout |= 0xf; + + if (gain_level) { + rfvga |= RFVGA_0 << 2; + vga |= (VGA_7 << 3) | VGA_7; + } else { + rfvga |= RFVGA_2 << 2; + vga |= (VGA_6 << 3) | VGA_2; + } + state->gain_level = gain_level; + + cx24113_writereg(state, 0x1d, ampout); + cx24113_writereg(state, 0x1f, vga); + cx24113_writereg(state, 0x20, rfvga); + + return 1; /* did something */ +} + +static int cx24113_set_Fref(struct cx24113_state *state, u8 high) +{ + u8 xtal = cx24113_readreg(state, 0x02); + if (state->rev == 0x43 && state->vcodiv == VCODIV4) + high = 1; + + xtal &= ~0x2; + if (high) + xtal |= high << 1; + return cx24113_writereg(state, 0x02, xtal); +} + +static int cx24113_enable(struct cx24113_state *state, u8 enable) +{ + u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; + if (state->rev == REV_CX24113) + r21 |= (1 << 1); + return cx24113_writereg(state, 0x21, r21); +} + +static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) +{ + u8 r; + + if (bandwidth_khz <= 19000) + r = 0x03 << 6; + else if (bandwidth_khz <= 25000) + r = 0x02 << 6; + else + r = 0x01 << 6; + + dprintk("bandwidth to be set: %d\n", bandwidth_khz); + bandwidth_khz *= 10; + bandwidth_khz -= 10000; + bandwidth_khz /= 1000; + bandwidth_khz += 5; + bandwidth_khz /= 10; + + dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); + + r |= bandwidth_khz & 0x3f; + + return cx24113_writereg(state, 0x1e, r); +} + +static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) +{ + u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); + return cx24113_writereg(state, 0x10, r); +} + +static int cx24113_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct cx24113_state *state = fe->tuner_priv; + u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; + if (r) + *status |= TUNER_STATUS_LOCKED; + dprintk("PLL locked: %d\n", r); + return 0; +} + +static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) +{ + if (state->rev == 0x43 && state->vcodiv == VCODIV4) + refdiv = 2; + return state->refdiv = refdiv; +} + +static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) +{ + s32 N; + s64 F; + u64 dividend; + u8 R, r; + u8 vcodiv; + u8 factor; + s32 freq_hz = state->frequency * 1000; + + if (state->config->xtal_khz < 20000) + factor = 1; + else + factor = 2; + + if (state->rev == REV_CX24113) { + if (state->frequency >= 1100000) + vcodiv = VCODIV2; + else + vcodiv = VCODIV4; + } else { + if (state->frequency >= 1165000) + vcodiv = VCODIV2; + else + vcodiv = VCODIV4; + } + state->vcodiv = vcodiv; + + dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); + R = 0; + do { + R = cx24113_set_ref_div(state, R + 1); + + /* calculate tuner PLL settings: */ + N = (freq_hz / 100 * vcodiv) * R; + N /= (state->config->xtal_khz) * factor * 2; + N += 5; /* For round up. */ + N /= 10; + N -= 32; + } while (N < 6 && R < 3); + + if (N < 6) { + cx_err("strange frequency: N < 6\n"); + return; + } + F = freq_hz; + F *= (u64) (R * vcodiv * 262144); + dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + /* do_div needs an u64 as first argument */ + dividend = F; + do_div(dividend, state->config->xtal_khz * 1000 * factor * 2); + F = dividend; + dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + F -= (N + 32) * 262144; + + dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + + if (state->Fwindow_enabled) { + if (F > (262144 / 2 - 1638)) + F = 262144 / 2 - 1638; + if (F < (-262144 / 2 + 1638)) + F = -262144 / 2 + 1638; + if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { + F = 0; + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r | (1 << 6)); + } + } + dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + + *n = (u16) N; + *f = (s32) F; +} + + +static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) +{ + u8 reg; + cx24113_writereg(state, 0x19, (n >> 1) & 0xff); + + reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); + cx24113_writereg(state, 0x1a, reg); + + cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); + + reg = cx24113_readreg(state, 0x1c) & 0x1f; + cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); + + cx24113_set_Fref(state, r - 1); +} + +static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) +{ + u8 r; + u16 n = 6; + s32 f = 0; + + r = cx24113_readreg(state, 0x14); + cx24113_writereg(state, 0x14, r & 0x3f); + + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r & 0xbf); + + state->frequency = frequency; + + dprintk("tuning to frequency: %d\n", frequency); + + cx24113_calc_pll_nf(state, &n, &f); + cx24113_set_nfr(state, n, f, state->refdiv); + + r = cx24113_readreg(state, 0x18) & 0xbf; + if (state->vcodiv != VCODIV2) + r |= 1 << 6; + cx24113_writereg(state, 0x18, r); + + /* The need for this sleep is not clear. But helps in some cases */ + msleep(5); + + r = cx24113_readreg(state, 0x1c) & 0xef; + cx24113_writereg(state, 0x1c, r | (1 << 4)); + return 0; +} + +static int cx24113_init(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + int ret; + + state->tuner_gain_thres = -50; + state->gain_level = 255; /* to force a gain-setting initialization */ + state->icp_mode = 0; + + if (state->config->xtal_khz < 11000) { + state->icp_auto_hi = ICP_LEVEL4; + state->icp_auto_mhi = ICP_LEVEL4; + state->icp_auto_mlow = ICP_LEVEL3; + state->icp_auto_low = ICP_LEVEL3; + } else { + state->icp_auto_hi = ICP_LEVEL4; + state->icp_auto_mhi = ICP_LEVEL4; + state->icp_auto_mlow = ICP_LEVEL3; + state->icp_auto_low = ICP_LEVEL2; + } + + state->icp_dig = ICP_LEVEL3; + state->icp_man = ICP_LEVEL1; + state->acp_on = 1; + state->vco_mode = 0; + state->vco_shift = 0; + state->vco_band = VCOBANDSEL_1; + state->bs_delay = 8; + state->bs_freqcnt = 0x0fff; + state->bs_rdiv = 0x0fff; + state->prescaler_mode = 0; + state->lna_gain = LNA_MAX_GAIN; + state->rfvga_bias_ctrl = 1; + state->Fwindow_enabled = 1; + + cx24113_set_Fref(state, 0); + cx24113_enable(state, 0x3d); + cx24113_set_parameters(state); + + cx24113_set_gain_settings(state, -30); + + cx24113_set_bandwidth(state, 18025); + cx24113_set_clk_inversion(state, 1); + + if (state->config->xtal_khz >= 40000) + ret = cx24113_writereg(state, 0x02, + (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); + else + ret = cx24113_writereg(state, 0x02, + (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); + + return ret; +} + +static int cx24113_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24113_state *state = fe->tuner_priv; + /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ + u32 roll_off = 675; + u32 bw; + + bw = ((c->symbol_rate/100) * roll_off) / 1000; + bw += (10000000/100) + 5; + bw /= 10; + bw += 1000; + cx24113_set_bandwidth(state, bw); + + cx24113_set_frequency(state, c->frequency); + msleep(5); + return cx24113_get_status(fe, &bw); +} + +static s8 cx24113_agc_table[2][10] = { + {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, + {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, +}; + +void cx24113_agc_callback(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + s16 s, i; + if (!fe->ops.read_signal_strength) + return; + + do { + /* this only works with the current CX24123 implementation */ + fe->ops.read_signal_strength(fe, (u16 *) &s); + s >>= 8; + dprintk("signal strength: %d\n", s); + for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) + if (cx24113_agc_table[state->gain_level][i] > s) + break; + s = -25 - i*5; + } while (cx24113_set_gain_settings(state, s)); +} +EXPORT_SYMBOL(cx24113_agc_callback); + +static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct cx24113_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static void cx24113_release(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + dprintk("\n"); + fe->tuner_priv = NULL; + kfree(state); +} + +static const struct dvb_tuner_ops cx24113_tuner_ops = { + .info = { + .name = "Conexant CX24113", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 125 * kHz, + }, + + .release = cx24113_release, + + .init = cx24113_init, + + .set_params = cx24113_set_params, + .get_frequency = cx24113_get_frequency, + .get_status = cx24113_get_status, +}; + +struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, + const struct cx24113_config *config, struct i2c_adapter *i2c) +{ + /* allocate memory for the internal state */ + struct cx24113_state *state = kzalloc(sizeof(*state), GFP_KERNEL); + int rc; + + if (!state) + return NULL; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + cx_info("trying to detect myself\n"); + + /* making a dummy read, because of some expected troubles + * after power on */ + cx24113_readreg(state, 0x00); + + rc = cx24113_readreg(state, 0x00); + if (rc < 0) { + cx_info("CX24113 not found.\n"); + goto error; + } + state->rev = rc; + + switch (rc) { + case 0x43: + cx_info("detected CX24113 variant\n"); + break; + case REV_CX24113: + cx_info("successfully detected\n"); + break; + default: + cx_err("unsupported device id: %x\n", state->rev); + goto error; + } + state->ver = cx24113_readreg(state, 0x01); + cx_info("version: %x\n", state->ver); + + /* create dvb_frontend */ + memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = state; + return fe; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL_GPL(cx24113_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); +MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/dvb-frontends/cx24113.h b/drivers/media/dvb-frontends/cx24113.h new file mode 100644 index 0000000000..c5460c2aec --- /dev/null +++ b/drivers/media/dvb-frontends/cx24113.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Conexant CX24113/CX24128 Tuner (Satellite) + * + * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org> + */ + +#ifndef CX24113_H +#define CX24113_H + +struct dvb_frontend; + +struct cx24113_config { + u8 i2c_addr; /* 0x14 or 0x54 */ + + u32 xtal_khz; +}; + +#if IS_REACHABLE(CONFIG_DVB_TUNER_CX24113) +extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *, + const struct cx24113_config *config, struct i2c_adapter *i2c); + +extern void cx24113_agc_callback(struct dvb_frontend *fe); +#else +static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, + const struct cx24113_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline void cx24113_agc_callback(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} +#endif + +#endif /* CX24113_H */ diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c new file mode 100644 index 0000000000..8b978a9f74 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24116.c @@ -0,0 +1,1492 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com> + Copyright (C) 2006-2007 Georg Acher + Copyright (C) 2007-2008 Darron Broad + March 2007 + Fixed some bugs. + Added diseqc support. + Added corrected signal strength support. + August 2007 + Sync with legacy version. + Some clean ups. + Copyright (C) 2008 Igor Liplianin + September, 9th 2008 + Fixed locking on high symbol rates (>30000). + Implement MPEG initialization parameter. + January, 17th 2009 + Fill set_voltage with actually control voltage code. + Correct set tone to not affect voltage. + +*/ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/firmware.h> + +#include <media/dvb_frontend.h> +#include "cx24116.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_INFO "cx24116: " args); \ + } while (0) + +#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" +#define CX24116_SEARCH_RANGE_KHZ 5000 + +/* known registers */ +#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */ +#define CX24116_REG_EXECUTE (0x1f) /* execute command */ +#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */ +#define CX24116_REG_RESET (0x20) /* reset status > 0 */ +#define CX24116_REG_SIGNAL (0x9e) /* signal low */ +#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */ +#define CX24116_REG_QUALITY8 (0xa3) +#define CX24116_REG_QSTATUS (0xbc) +#define CX24116_REG_QUALITY0 (0xd5) +#define CX24116_REG_BER0 (0xc9) +#define CX24116_REG_BER8 (0xc8) +#define CX24116_REG_BER16 (0xc7) +#define CX24116_REG_BER24 (0xc6) +#define CX24116_REG_UCB0 (0xcb) +#define CX24116_REG_UCB8 (0xca) +#define CX24116_REG_CLKDIV (0xf3) +#define CX24116_REG_RATEDIV (0xf9) + +/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */ +#define CX24116_REG_FECSTATUS (0x9c) + +/* FECSTATUS bits */ +/* mask to determine configured fec (not tuned) or actual fec (tuned) */ +#define CX24116_FEC_FECMASK (0x1f) + +/* Select DVB-S demodulator, else DVB-S2 */ +#define CX24116_FEC_DVBS (0x20) +#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */ + +/* Pilot mode requested when tuning else always reset when tuned */ +#define CX24116_FEC_PILOT (0x80) + +/* arg buffer size */ +#define CX24116_ARGLEN (0x1e) + +/* rolloff */ +#define CX24116_ROLLOFF_020 (0x00) +#define CX24116_ROLLOFF_025 (0x01) +#define CX24116_ROLLOFF_035 (0x02) + +/* pilot bit */ +#define CX24116_PILOT_OFF (0x00) +#define CX24116_PILOT_ON (0x40) + +/* signal status */ +#define CX24116_HAS_SIGNAL (0x01) +#define CX24116_HAS_CARRIER (0x02) +#define CX24116_HAS_VITERBI (0x04) +#define CX24116_HAS_SYNCLOCK (0x08) +#define CX24116_HAS_UNKNOWN1 (0x10) +#define CX24116_HAS_UNKNOWN2 (0x20) +#define CX24116_STATUS_MASK (0x0f) +#define CX24116_SIGNAL_MASK (0xc0) + +#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ +#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */ +#define CX24116_DISEQC_MESGCACHE (2) /* message cached */ + +/* arg offset for DiSEqC */ +#define CX24116_DISEQC_BURST (1) +#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ +#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ +#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ +#define CX24116_DISEQC_MSGLEN (5) +#define CX24116_DISEQC_MSGOFS (6) + +/* DiSEqC burst */ +#define CX24116_DISEQC_MINI_A (0) +#define CX24116_DISEQC_MINI_B (1) + +/* DiSEqC tone burst */ +static int toneburst = 1; +module_param(toneburst, int, 0644); +MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\ + "2=MESSAGE CACHE (default:1)"); + +/* SNR measurements */ +static int esno_snr; +module_param(esno_snr, int, 0644); +MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\ + "1=ESNO(db * 10) (default:0)"); + +enum cmds { + CMD_SET_VCO = 0x10, + CMD_TUNEREQUEST = 0x11, + CMD_MPEGCONFIG = 0x13, + CMD_TUNERINIT = 0x14, + CMD_BANDWIDTH = 0x15, + CMD_GETAGC = 0x19, + CMD_LNBCONFIG = 0x20, + CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ + CMD_LNBDCLEVEL = 0x22, + CMD_SET_TONE = 0x23, + CMD_UPDFWVERS = 0x35, + CMD_TUNERSLEEP = 0x36, + CMD_AGCCONTROL = 0x3b, /* Unknown */ +}; + +/* The Demod/Tuner can't easily provide these, we cache them */ +struct cx24116_tuning { + u32 frequency; + u32 symbol_rate; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec; + + enum fe_delivery_system delsys; + enum fe_modulation modulation; + enum fe_pilot pilot; + enum fe_rolloff rolloff; + + /* Demod values */ + u8 fec_val; + u8 fec_mask; + u8 inversion_val; + u8 pilot_val; + u8 rolloff_val; +}; + +/* Basic commands that are sent to the firmware */ +struct cx24116_cmd { + u8 len; + u8 args[CX24116_ARGLEN]; +}; + +struct cx24116_state { + struct i2c_adapter *i2c; + const struct cx24116_config *config; + + struct dvb_frontend frontend; + + struct cx24116_tuning dcur; + struct cx24116_tuning dnxt; + + u8 skip_fw_load; + u8 burst; + struct cx24116_cmd dsec_cmd; +}; + +static int cx24116_writereg(struct cx24116_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + if (debug > 1) + printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", + __func__, reg, data); + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", + __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +/* Bulk byte writes to a single I2C address, for 32k firmware load */ +static int cx24116_writeregN(struct cx24116_state *state, int reg, + const u8 *data, u16 len) +{ + int ret; + struct i2c_msg msg; + u8 *buf; + + buf = kmalloc(len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + *(buf) = reg; + memcpy(buf + 1, data, len); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = len + 1; + + if (debug > 1) + printk(KERN_INFO "cx24116: %s: write regN 0x%02x, len = %d\n", + __func__, reg, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n", + __func__, ret, reg); + ret = -EREMOTEIO; + } + + kfree(buf); + + return ret; +} + +static int cx24116_readreg(struct cx24116_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", + __func__, reg, ret); + return ret; + } + + if (debug > 1) + printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n", + reg, b1[0]); + + return b1[0]; +} + +static int cx24116_set_inversion(struct cx24116_state *state, + enum fe_spectral_inversion inversion) +{ + dprintk("%s(%d)\n", __func__, inversion); + + switch (inversion) { + case INVERSION_OFF: + state->dnxt.inversion_val = 0x00; + break; + case INVERSION_ON: + state->dnxt.inversion_val = 0x04; + break; + case INVERSION_AUTO: + state->dnxt.inversion_val = 0x0C; + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +/* + * modfec (modulation and FEC) + * =========================== + * + * MOD FEC mask/val standard + * ---- -------- ----------- -------- + * QPSK FEC_1_2 0x02 0x02+X DVB-S + * QPSK FEC_2_3 0x04 0x02+X DVB-S + * QPSK FEC_3_4 0x08 0x02+X DVB-S + * QPSK FEC_4_5 0x10 0x02+X DVB-S (?) + * QPSK FEC_5_6 0x20 0x02+X DVB-S + * QPSK FEC_6_7 0x40 0x02+X DVB-S + * QPSK FEC_7_8 0x80 0x02+X DVB-S + * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?) + * QPSK AUTO 0xff 0x02+X DVB-S + * + * For DVB-S high byte probably represents FEC + * and low byte selects the modulator. The high + * byte is search range mask. Bit 5 may turn + * on DVB-S and remaining bits represent some + * kind of calibration (how/what i do not know). + * + * Eg.(2/3) szap "Zone Horror" + * + * mask/val = 0x04, 0x20 + * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK + * + * mask/val = 0x04, 0x30 + * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK + * + * After tuning FECSTATUS contains actual FEC + * in use numbered 1 through to 8 for 1/2 .. 2/3 etc + * + * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only) + * + * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2 + * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2 + * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2 + * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2 + * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2 + * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2 + * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2 + * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2 + * + * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2 + * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2 + * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2 + * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2 + * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2 + * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2 + * + * For DVB-S2 low bytes selects both modulator + * and FEC. High byte is meaningless here. To + * set pilot, bit 6 (0x40) is set. When inspecting + * FECSTATUS bit 7 (0x80) represents the pilot + * selection whilst not tuned. When tuned, actual FEC + * in use is found in FECSTATUS as per above. Pilot + * value is reset. + */ + +/* A table of modulation, fec and configuration bytes for the demod. + * Not all S2 mmodulation schemes are support and not all rates with + * a scheme are support. Especially, no auto detect when in S2 mode. + */ +static struct cx24116_modfec { + enum fe_delivery_system delivery_system; + enum fe_modulation modulation; + enum fe_code_rate fec; + u8 mask; /* In DVBS mode this is used to autodetect */ + u8 val; /* Passed to the firmware to indicate mode selection */ +} CX24116_MODFEC_MODES[] = { + /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ + + /*mod fec mask val */ + { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, + { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ + { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ + { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ + { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ + { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ + { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ + { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ + { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ + { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, + /* NBC-QPSK */ + { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, + /* 8PSK */ + { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, + /* + * `val' can be found in the FECSTATUS register when tuning. + * FECSTATUS will give the actual FEC in use if tuning was successful. + */ +}; + +static int cx24116_lookup_fecmod(struct cx24116_state *state, + enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f) +{ + int i, ret = -EOPNOTSUPP; + + dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); + + for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { + if ((d == CX24116_MODFEC_MODES[i].delivery_system) && + (m == CX24116_MODFEC_MODES[i].modulation) && + (f == CX24116_MODFEC_MODES[i].fec)) { + ret = i; + break; + } + } + + return ret; +} + +static int cx24116_set_fec(struct cx24116_state *state, + enum fe_delivery_system delsys, + enum fe_modulation mod, + enum fe_code_rate fec) +{ + int ret = 0; + + dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); + + ret = cx24116_lookup_fecmod(state, delsys, mod, fec); + + if (ret < 0) + return ret; + + state->dnxt.fec = fec; + state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; + state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; + dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__, + state->dnxt.fec_mask, state->dnxt.fec_val); + + return 0; +} + +static int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate) +{ + dprintk("%s(%d)\n", __func__, rate); + + /* check if symbol rate is within limits */ + if ((rate > state->frontend.ops.info.symbol_rate_max) || + (rate < state->frontend.ops.info.symbol_rate_min)) { + dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate); + return -EOPNOTSUPP; + } + + state->dnxt.symbol_rate = rate; + dprintk("%s() symbol_rate = %d\n", __func__, rate); + + return 0; +} + +static int cx24116_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw); + +static int cx24116_firmware_ondemand(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret = 0; + + dprintk("%s()\n", __func__); + + if (cx24116_readreg(state, 0x20) > 0) { + + if (state->skip_fw_load) + return 0; + + /* Load firmware */ + /* request the firmware, this will block until loaded */ + printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", + __func__, CX24116_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", + __func__); + if (ret) { + printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n", + __func__); + return ret; + } + + /* Make sure we don't recurse back through here + * during loading */ + state->skip_fw_load = 1; + + ret = cx24116_load_firmware(fe, fw); + if (ret) + printk(KERN_ERR "%s: Writing firmware to device failed\n", + __func__); + + release_firmware(fw); + + printk(KERN_INFO "%s: Firmware upload %s\n", __func__, + ret == 0 ? "complete" : "failed"); + + /* Ensure firmware is always loaded if required */ + state->skip_fw_load = 0; + } + + return ret; +} + +/* Take a basic firmware command structure, format it + * and forward it for processing + */ +static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd) +{ + struct cx24116_state *state = fe->demodulator_priv; + int i, ret; + + dprintk("%s()\n", __func__); + + /* Load the firmware if required */ + ret = cx24116_firmware_ondemand(fe); + if (ret != 0) { + printk(KERN_ERR "%s(): Unable initialise the firmware\n", + __func__); + return ret; + } + + /* Write the command */ + for (i = 0; i < cmd->len ; i++) { + dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); + cx24116_writereg(state, i, cmd->args[i]); + } + + /* Start execution and wait for cmd to terminate */ + cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); + while (cx24116_readreg(state, CX24116_REG_EXECUTE)) { + msleep(10); + if (i++ > 64) { + /* Avoid looping forever if the firmware does + not respond */ + printk(KERN_WARNING "%s() Firmware not responding\n", + __func__); + return -EREMOTEIO; + } + } + return 0; +} + +static int cx24116_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int i, ret, len, max, remaining; + unsigned char vers[4]; + + dprintk("%s\n", __func__); + dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", + fw->size, + fw->data[0], + fw->data[1], + fw->data[fw->size-2], + fw->data[fw->size-1]); + + /* Toggle 88x SRST pin to reset demod */ + if (state->config->reset_device) + state->config->reset_device(fe); + + /* Begin the firmware load process */ + /* Prepare the demod, load the firmware, cleanup after load */ + + /* Init PLL */ + cx24116_writereg(state, 0xE5, 0x00); + cx24116_writereg(state, 0xF1, 0x08); + cx24116_writereg(state, 0xF2, 0x13); + + /* Start PLL */ + cx24116_writereg(state, 0xe0, 0x03); + cx24116_writereg(state, 0xe0, 0x00); + + /* Unknown */ + cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); + cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); + + /* Unknown */ + cx24116_writereg(state, 0xF0, 0x03); + cx24116_writereg(state, 0xF4, 0x81); + cx24116_writereg(state, 0xF5, 0x00); + cx24116_writereg(state, 0xF6, 0x00); + + /* Split firmware to the max I2C write len and write. + * Writes whole firmware as one write when i2c_wr_max is set to 0. */ + if (state->config->i2c_wr_max) + max = state->config->i2c_wr_max; + else + max = INT_MAX; /* enough for 32k firmware */ + + for (remaining = fw->size; remaining > 0; remaining -= max - 1) { + len = remaining; + if (len > max - 1) + len = max - 1; + + cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining], + len); + } + + cx24116_writereg(state, 0xF4, 0x10); + cx24116_writereg(state, 0xF0, 0x00); + cx24116_writereg(state, 0xF8, 0x06); + + /* Firmware CMD 10: VCO config */ + cmd.args[0x00] = CMD_SET_VCO; + cmd.args[0x01] = 0x05; + cmd.args[0x02] = 0xdc; + cmd.args[0x03] = 0xda; + cmd.args[0x04] = 0xae; + cmd.args[0x05] = 0xaa; + cmd.args[0x06] = 0x04; + cmd.args[0x07] = 0x9d; + cmd.args[0x08] = 0xfc; + cmd.args[0x09] = 0x06; + cmd.len = 0x0a; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); + + /* Firmware CMD 14: Tuner config */ + cmd.args[0x00] = CMD_TUNERINIT; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x00; + cmd.len = 0x03; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + cx24116_writereg(state, 0xe5, 0x00); + + /* Firmware CMD 13: MPEG config */ + cmd.args[0x00] = CMD_MPEGCONFIG; + cmd.args[0x01] = 0x01; + cmd.args[0x02] = 0x75; + cmd.args[0x03] = 0x00; + if (state->config->mpg_clk_pos_pol) + cmd.args[0x04] = state->config->mpg_clk_pos_pol; + else + cmd.args[0x04] = 0x02; + cmd.args[0x05] = 0x00; + cmd.len = 0x06; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Firmware CMD 35: Get firmware version */ + cmd.args[0x00] = CMD_UPDFWVERS; + cmd.len = 0x02; + for (i = 0; i < 4; i++) { + cmd.args[0x01] = i; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX); + } + printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__, + vers[0], vers[1], vers[2], vers[3]); + + return 0; +} + +static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx24116_state *state = fe->demodulator_priv; + + int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & + CX24116_STATUS_MASK; + + dprintk("%s: status = 0x%02x\n", __func__, lock); + + *status = 0; + + if (lock & CX24116_HAS_SIGNAL) + *status |= FE_HAS_SIGNAL; + if (lock & CX24116_HAS_CARRIER) + *status |= FE_HAS_CARRIER; + if (lock & CX24116_HAS_VITERBI) + *status |= FE_HAS_VITERBI; + if (lock & CX24116_HAS_SYNCLOCK) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + return 0; +} + +static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cx24116_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + *ber = (cx24116_readreg(state, CX24116_REG_BER24) << 24) | + (cx24116_readreg(state, CX24116_REG_BER16) << 16) | + (cx24116_readreg(state, CX24116_REG_BER8) << 8) | + cx24116_readreg(state, CX24116_REG_BER0); + + return 0; +} + +/* TODO Determine function and scale appropriately */ +static int cx24116_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + u16 sig_reading; + + dprintk("%s()\n", __func__); + + /* Firmware CMD 19: Get AGC */ + cmd.args[0x00] = CMD_GETAGC; + cmd.len = 0x01; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + sig_reading = + (cx24116_readreg(state, + CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) | + (cx24116_readreg(state, CX24116_REG_SIGNAL) << 6); + *signal_strength = 0 - sig_reading; + + dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", + __func__, sig_reading, *signal_strength); + + return 0; +} + +/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ +static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr) +{ + struct cx24116_state *state = fe->demodulator_priv; + u8 snr_reading; + static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ + 0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667, + 0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667, + 0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667, + 0x18000 }; + + dprintk("%s()\n", __func__); + + snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0); + + if (snr_reading >= 0xa0 /* 100% */) + *snr = 0xffff; + else + *snr = snr_tab[(snr_reading & 0xf0) >> 4] + + (snr_tab[(snr_reading & 0x0f)] >> 4); + + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + + return 0; +} + +/* The reelbox patches show the value in the registers represents + * ESNO, from 0->30db (values 0->300). We provide this value by + * default. + */ +static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr) +{ + struct cx24116_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 | + cx24116_readreg(state, CX24116_REG_QUALITY0); + + dprintk("%s: raw 0x%04x\n", __func__, *snr); + + return 0; +} + +static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + if (esno_snr == 1) + return cx24116_read_snr_esno(fe, snr); + else + return cx24116_read_snr_pct(fe, snr); +} + +static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct cx24116_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + *ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) | + cx24116_readreg(state, CX24116_REG_UCB0); + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void cx24116_clone_params(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + state->dcur = state->dnxt; +} + +/* Wait for LNB */ +static int cx24116_wait_for_lnb(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + int i; + + dprintk("%s() qstatus = 0x%02x\n", __func__, + cx24116_readreg(state, CX24116_REG_QSTATUS)); + + /* Wait for up to 300 ms */ + for (i = 0; i < 30 ; i++) { + if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) + return 0; + msleep(10); + } + + dprintk("%s(): LNB not ready\n", __func__); + + return -ETIMEDOUT; /* -EBUSY ? */ +} + +static int cx24116_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct cx24116_cmd cmd; + int ret; + + dprintk("%s: %s\n", __func__, + voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + cmd.args[0x00] = CMD_LNBDCLEVEL; + cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); + cmd.len = 0x02; + + /* Min delay time before DiSEqC send */ + msleep(15); + + return cx24116_cmd_execute(fe, &cmd); +} + +static int cx24116_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct cx24116_cmd cmd; + int ret; + + dprintk("%s(%d)\n", __func__, tone); + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); + return -EINVAL; + } + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Min delay time after DiSEqC send */ + msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ + + /* Now we set the tone */ + cmd.args[0x00] = CMD_SET_TONE; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x00; + + switch (tone) { + case SEC_TONE_ON: + dprintk("%s: setting tone on\n", __func__); + cmd.args[0x03] = 0x01; + break; + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n", __func__); + cmd.args[0x03] = 0x00; + break; + } + cmd.len = 0x04; + + /* Min delay time before DiSEqC send */ + msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ + + return cx24116_cmd_execute(fe, &cmd); +} + +/* Initialise DiSEqC */ +static int cx24116_diseqc_init(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + + /* Firmware CMD 20: LNB/DiSEqC config */ + cmd.args[0x00] = CMD_LNBCONFIG; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x10; + cmd.args[0x03] = 0x00; + cmd.args[0x04] = 0x8f; + cmd.args[0x05] = 0x28; + cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; + cmd.args[0x07] = 0x01; + cmd.len = 0x08; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Prepare a DiSEqC command */ + state->dsec_cmd.args[0x00] = CMD_LNBSEND; + + /* DiSEqC burst */ + state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; + + /* Unknown */ + state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; + state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; + /* Continuation flag? */ + state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; + + /* DiSEqC message length */ + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; + + /* Command length */ + state->dsec_cmd.len = CX24116_DISEQC_MSGOFS; + + return 0; +} + +/* Send DiSEqC message with derived burst (hack) || previous burst */ +static int cx24116_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct cx24116_state *state = fe->demodulator_priv; + int i, ret; + + /* Validate length */ + if (d->msg_len > sizeof(d->msg)) + return -EINVAL; + + /* Dump DiSEqC message */ + if (debug) { + printk(KERN_INFO "cx24116: %s(", __func__); + for (i = 0 ; i < d->msg_len ;) { + printk(KERN_INFO "0x%02x", d->msg[i]); + if (++i < d->msg_len) + printk(KERN_INFO ", "); + } + printk(") toneburst=%d\n", toneburst); + } + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; + + /* DiSEqC message length */ + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; + + /* Command length */ + state->dsec_cmd.len = CX24116_DISEQC_MSGOFS + + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; + + /* DiSEqC toneburst */ + if (toneburst == CX24116_DISEQC_MESGCACHE) + /* Message is cached */ + return 0; + + else if (toneburst == CX24116_DISEQC_TONEOFF) + /* Message is sent without burst */ + state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; + + else if (toneburst == CX24116_DISEQC_TONECACHE) { + /* + * Message is sent with derived else cached burst + * + * WRITE PORT GROUP COMMAND 38 + * + * 0/A/A: E0 10 38 F0..F3 + * 1/B/B: E0 10 38 F4..F7 + * 2/C/A: E0 10 38 F8..FB + * 3/D/B: E0 10 38 FC..FF + * + * databyte[3]= 8421:8421 + * ABCD:WXYZ + * CLR :SET + * + * WX= PORT SELECT 0..3 (X=TONEBURST) + * Y = VOLTAGE (0=13V, 1=18V) + * Z = BAND (0=LOW, 1=HIGH(22K)) + */ + if (d->msg_len >= 4 && d->msg[2] == 0x38) + state->dsec_cmd.args[CX24116_DISEQC_BURST] = + ((d->msg[3] & 4) >> 2); + if (debug) + dprintk("%s burst=%d\n", __func__, + state->dsec_cmd.args[CX24116_DISEQC_BURST]); + } + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + /* Command */ + ret = cx24116_cmd_execute(fe, &state->dsec_cmd); + if (ret != 0) + return ret; + /* + * Wait for send + * + * Eutelsat spec: + * >15ms delay + (XXX determine if FW does this, see set_tone) + * 13.5ms per byte + + * >15ms delay + + * 12.5ms burst + + * >15ms delay (XXX determine if FW does this, see set_tone) + */ + msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60)); + + return 0; +} + +/* Send DiSEqC burst */ +static int cx24116_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct cx24116_state *state = fe->demodulator_priv; + int ret; + + dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst); + + /* DiSEqC burst */ + if (burst == SEC_MINI_A) + state->dsec_cmd.args[CX24116_DISEQC_BURST] = + CX24116_DISEQC_MINI_A; + else if (burst == SEC_MINI_B) + state->dsec_cmd.args[CX24116_DISEQC_BURST] = + CX24116_DISEQC_MINI_B; + else + return -EINVAL; + + /* DiSEqC toneburst */ + if (toneburst != CX24116_DISEQC_MESGCACHE) + /* Burst is cached */ + return 0; + + /* Burst is to be sent with cached message */ + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + /* Command */ + ret = cx24116_cmd_execute(fe, &state->dsec_cmd); + if (ret != 0) + return ret; + + /* + * Wait for send + * + * Eutelsat spec: + * >15ms delay + (XXX determine if FW does this, see set_tone) + * 13.5ms per byte + + * >15ms delay + + * 12.5ms burst + + * >15ms delay (XXX determine if FW does this, see set_tone) + */ + msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60); + + return 0; +} + +static void cx24116_release(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + kfree(state); +} + +static const struct dvb_frontend_ops cx24116_ops; + +struct dvb_frontend *cx24116_attach(const struct cx24116_config *config, + struct i2c_adapter *i2c) +{ + struct cx24116_state *state; + int ret; + + dprintk("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->config = config; + state->i2c = i2c; + + /* check if the demod is present */ + ret = (cx24116_readreg(state, 0xFF) << 8) | + cx24116_readreg(state, 0xFE); + if (ret != 0x0501) { + kfree(state); + printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n"); + return NULL; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx24116_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} +EXPORT_SYMBOL_GPL(cx24116_attach); + +/* + * Initialise or wake up device + * + * Power config will reset and load initial firmware if required + */ +static int cx24116_initfe(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + + dprintk("%s()\n", __func__); + + /* Power on */ + cx24116_writereg(state, 0xe0, 0); + cx24116_writereg(state, 0xe1, 0); + cx24116_writereg(state, 0xea, 0); + + /* Firmware CMD 36: Power config */ + cmd.args[0x00] = CMD_TUNERSLEEP; + cmd.args[0x01] = 0; + cmd.len = 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + ret = cx24116_diseqc_init(fe); + if (ret != 0) + return ret; + + /* HVR-4000 needs this */ + return cx24116_set_voltage(fe, SEC_VOLTAGE_13); +} + +/* + * Put device to sleep + */ +static int cx24116_sleep(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + + dprintk("%s()\n", __func__); + + /* Firmware CMD 36: Power config */ + cmd.args[0x00] = CMD_TUNERSLEEP; + cmd.args[0x01] = 1; + cmd.len = 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Power off (Shutdown clocks) */ + cx24116_writereg(state, 0xea, 0xff); + cx24116_writereg(state, 0xe1, 1); + cx24116_writereg(state, 0xe0, 1); + + return 0; +} + +/* dvb-core told us to tune, the tv property cache will be complete, + * it's safe for is to pull values and use them for tuning purposes. + */ +static int cx24116_set_frontend(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24116_cmd cmd; + enum fe_status tunerstat; + int i, status, ret, retune = 1; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + dprintk("%s: DVB-S delivery system selected\n", __func__); + + /* Only QPSK is supported for DVB-S */ + if (c->modulation != QPSK) { + dprintk("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + + /* Pilot doesn't exist in DVB-S, turn bit off */ + state->dnxt.pilot_val = CX24116_PILOT_OFF; + + /* DVB-S only supports 0.35 */ + if (c->rolloff != ROLLOFF_35) { + dprintk("%s: unsupported rolloff selected (%d)\n", + __func__, c->rolloff); + return -EOPNOTSUPP; + } + state->dnxt.rolloff_val = CX24116_ROLLOFF_035; + break; + + case SYS_DVBS2: + dprintk("%s: DVB-S2 delivery system selected\n", __func__); + + /* + * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, + * but not hardware auto detection + */ + if (c->modulation != PSK_8 && c->modulation != QPSK) { + dprintk("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + + switch (c->pilot) { + case PILOT_AUTO: /* Not supported but emulated */ + state->dnxt.pilot_val = (c->modulation == QPSK) + ? CX24116_PILOT_OFF : CX24116_PILOT_ON; + retune++; + break; + case PILOT_OFF: + state->dnxt.pilot_val = CX24116_PILOT_OFF; + break; + case PILOT_ON: + state->dnxt.pilot_val = CX24116_PILOT_ON; + break; + default: + dprintk("%s: unsupported pilot mode selected (%d)\n", + __func__, c->pilot); + return -EOPNOTSUPP; + } + + switch (c->rolloff) { + case ROLLOFF_20: + state->dnxt.rolloff_val = CX24116_ROLLOFF_020; + break; + case ROLLOFF_25: + state->dnxt.rolloff_val = CX24116_ROLLOFF_025; + break; + case ROLLOFF_35: + state->dnxt.rolloff_val = CX24116_ROLLOFF_035; + break; + case ROLLOFF_AUTO: /* Rolloff must be explicit */ + default: + dprintk("%s: unsupported rolloff selected (%d)\n", + __func__, c->rolloff); + return -EOPNOTSUPP; + } + break; + + default: + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + state->dnxt.delsys = c->delivery_system; + state->dnxt.modulation = c->modulation; + state->dnxt.frequency = c->frequency; + state->dnxt.pilot = c->pilot; + state->dnxt.rolloff = c->rolloff; + + ret = cx24116_set_inversion(state, c->inversion); + if (ret != 0) + return ret; + + /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ + ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); + if (ret != 0) + return ret; + + ret = cx24116_set_symbolrate(state, c->symbol_rate); + if (ret != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + cx24116_clone_params(fe); + + dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); + dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); + dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); + dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, + state->dcur.pilot, state->dcur.pilot_val); + dprintk("%s: retune = %d\n", __func__, retune); + dprintk("%s: rolloff = %d (val = 0x%02x)\n", __func__, + state->dcur.rolloff, state->dcur.rolloff_val); + dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); + dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, + state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); + dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, + state->dcur.inversion, state->dcur.inversion_val); + + /* This is also done in advise/acquire on HVR4000 but not on LITE */ + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + /* Set/Reset B/W */ + cmd.args[0x00] = CMD_BANDWIDTH; + cmd.args[0x01] = 0x01; + cmd.len = 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Prepare a tune request */ + cmd.args[0x00] = CMD_TUNEREQUEST; + + /* Frequency */ + cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; + cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; + cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); + + /* Symbol Rate */ + cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; + cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); + + /* Automatic Inversion */ + cmd.args[0x06] = state->dcur.inversion_val; + + /* Modulation / FEC / Pilot */ + cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val; + + cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; + cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; + cmd.args[0x0a] = 0x00; + cmd.args[0x0b] = 0x00; + cmd.args[0x0c] = state->dcur.rolloff_val; + cmd.args[0x0d] = state->dcur.fec_mask; + + if (state->dcur.symbol_rate > 30000000) { + cmd.args[0x0e] = 0x04; + cmd.args[0x0f] = 0x00; + cmd.args[0x10] = 0x01; + cmd.args[0x11] = 0x77; + cmd.args[0x12] = 0x36; + cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44); + cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01); + } else { + cmd.args[0x0e] = 0x06; + cmd.args[0x0f] = 0x00; + cmd.args[0x10] = 0x00; + cmd.args[0x11] = 0xFA; + cmd.args[0x12] = 0x24; + cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); + cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); + } + + cmd.len = 0x13; + + /* We need to support pilot and non-pilot tuning in the + * driver automatically. This is a workaround for because + * the demod does not support autodetect. + */ + do { + /* Reset status register */ + status = cx24116_readreg(state, CX24116_REG_SSTATUS) + & CX24116_SIGNAL_MASK; + cx24116_writereg(state, CX24116_REG_SSTATUS, status); + + /* Tune */ + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + break; + + /* + * Wait for up to 500 ms before retrying + * + * If we are able to tune then generally it occurs within 100ms. + * If it takes longer, try a different toneburst setting. + */ + for (i = 0; i < 50 ; i++) { + cx24116_read_status(fe, &tunerstat); + status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); + if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { + dprintk("%s: Tuned\n", __func__); + goto tuned; + } + msleep(10); + } + + dprintk("%s: Not tuned\n", __func__); + + /* Toggle pilot bit when in auto-pilot */ + if (state->dcur.pilot == PILOT_AUTO) + cmd.args[0x07] ^= CX24116_PILOT_ON; + } while (--retune); + +tuned: /* Set/Reset B/W */ + cmd.args[0x00] = CMD_BANDWIDTH; + cmd.args[0x01] = 0x00; + cmd.len = 0x02; + return cx24116_cmd_execute(fe, &cmd); +} + +static int cx24116_tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, unsigned int *delay, enum fe_status *status) +{ + /* + * It is safe to discard "params" here, as the DVB core will sync + * fe->dtv_property_cache with fepriv->parameters_in, where the + * DVBv3 params are stored. The only practical usage for it indicate + * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is + * true. + */ + + *delay = HZ / 5; + if (re_tune) { + int ret = cx24116_set_frontend(fe); + if (ret) + return ret; + } + return cx24116_read_status(fe, status); +} + +static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static const struct dvb_frontend_ops cx24116_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Conexant CX24116/CX24118", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24116_release, + + .init = cx24116_initfe, + .sleep = cx24116_sleep, + .read_status = cx24116_read_status, + .read_ber = cx24116_read_ber, + .read_signal_strength = cx24116_read_signal_strength, + .read_snr = cx24116_read_snr, + .read_ucblocks = cx24116_read_ucblocks, + .set_tone = cx24116_set_tone, + .set_voltage = cx24116_set_voltage, + .diseqc_send_master_cmd = cx24116_send_diseqc_msg, + .diseqc_send_burst = cx24116_diseqc_send_burst, + .get_frontend_algo = cx24116_get_algo, + .tune = cx24116_tune, + + .set_frontend = cx24116_set_frontend, +}; + +MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/dvb-frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h new file mode 100644 index 0000000000..1d2fab5945 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24116.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006 Steven Toth <stoth@linuxtv.com> + +*/ + +#ifndef CX24116_H +#define CX24116_H + +#include <linux/dvb/frontend.h> + +struct cx24116_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* Need to set device param for start_dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + + /* Need to reset device during firmware loading */ + int (*reset_device)(struct dvb_frontend *fe); + + /* Need to set MPEG parameters */ + u8 mpg_clk_pos_pol:0x02; + + /* max bytes I2C provider can write at once */ + u16 i2c_wr_max; +}; + +#if IS_REACHABLE(CONFIG_DVB_CX24116) +extern struct dvb_frontend *cx24116_attach( + const struct cx24116_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *cx24116_attach( + const struct cx24116_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* CX24116_H */ diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c new file mode 100644 index 0000000000..ac6e47d81b --- /dev/null +++ b/drivers/media/dvb-frontends/cx24117.c @@ -0,0 +1,1654 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2013 Luis Alves <ljalvs@gmail.com> + July, 6th 2013 + First release based on cx24116 driver by: + Steven Toth and Georg Acher, Darron Broad, Igor Liplianin + Cards currently supported: + TBS6980 - Dual DVBS/S2 PCIe card + TBS6981 - Dual DVBS/S2 PCIe card + +*/ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/firmware.h> + +#include "tuner-i2c.h" +#include <media/dvb_frontend.h> +#include "cx24117.h" + + +#define CX24117_DEFAULT_FIRMWARE "dvb-fe-cx24117.fw" +#define CX24117_SEARCH_RANGE_KHZ 5000 + +/* known registers */ +#define CX24117_REG_COMMAND (0x00) /* command buffer */ +#define CX24117_REG_EXECUTE (0x1f) /* execute command */ + +#define CX24117_REG_FREQ3_0 (0x34) /* frequency */ +#define CX24117_REG_FREQ2_0 (0x35) +#define CX24117_REG_FREQ1_0 (0x36) +#define CX24117_REG_STATE0 (0x39) +#define CX24117_REG_SSTATUS0 (0x3a) /* demod0 signal high / status */ +#define CX24117_REG_SIGNAL0 (0x3b) +#define CX24117_REG_FREQ5_0 (0x3c) /* +-freq */ +#define CX24117_REG_FREQ6_0 (0x3d) +#define CX24117_REG_SRATE2_0 (0x3e) /* +- 1000 * srate */ +#define CX24117_REG_SRATE1_0 (0x3f) +#define CX24117_REG_QUALITY2_0 (0x40) +#define CX24117_REG_QUALITY1_0 (0x41) + +#define CX24117_REG_BER4_0 (0x47) +#define CX24117_REG_BER3_0 (0x48) +#define CX24117_REG_BER2_0 (0x49) +#define CX24117_REG_BER1_0 (0x4a) +#define CX24117_REG_DVBS_UCB2_0 (0x4b) +#define CX24117_REG_DVBS_UCB1_0 (0x4c) +#define CX24117_REG_DVBS2_UCB2_0 (0x50) +#define CX24117_REG_DVBS2_UCB1_0 (0x51) +#define CX24117_REG_QSTATUS0 (0x93) +#define CX24117_REG_CLKDIV0 (0xe6) +#define CX24117_REG_RATEDIV0 (0xf0) + + +#define CX24117_REG_FREQ3_1 (0x55) /* frequency */ +#define CX24117_REG_FREQ2_1 (0x56) +#define CX24117_REG_FREQ1_1 (0x57) +#define CX24117_REG_STATE1 (0x5a) +#define CX24117_REG_SSTATUS1 (0x5b) /* demod1 signal high / status */ +#define CX24117_REG_SIGNAL1 (0x5c) +#define CX24117_REG_FREQ5_1 (0x5d) /* +- freq */ +#define CX24117_REG_FREQ4_1 (0x5e) +#define CX24117_REG_SRATE2_1 (0x5f) +#define CX24117_REG_SRATE1_1 (0x60) +#define CX24117_REG_QUALITY2_1 (0x61) +#define CX24117_REG_QUALITY1_1 (0x62) +#define CX24117_REG_BER4_1 (0x68) +#define CX24117_REG_BER3_1 (0x69) +#define CX24117_REG_BER2_1 (0x6a) +#define CX24117_REG_BER1_1 (0x6b) +#define CX24117_REG_DVBS_UCB2_1 (0x6c) +#define CX24117_REG_DVBS_UCB1_1 (0x6d) +#define CX24117_REG_DVBS2_UCB2_1 (0x71) +#define CX24117_REG_DVBS2_UCB1_1 (0x72) +#define CX24117_REG_QSTATUS1 (0x9f) +#define CX24117_REG_CLKDIV1 (0xe7) +#define CX24117_REG_RATEDIV1 (0xf1) + + +/* arg buffer size */ +#define CX24117_ARGLEN (0x1e) + +/* rolloff */ +#define CX24117_ROLLOFF_020 (0x00) +#define CX24117_ROLLOFF_025 (0x01) +#define CX24117_ROLLOFF_035 (0x02) + +/* pilot bit */ +#define CX24117_PILOT_OFF (0x00) +#define CX24117_PILOT_ON (0x40) +#define CX24117_PILOT_AUTO (0x80) + +/* signal status */ +#define CX24117_HAS_SIGNAL (0x01) +#define CX24117_HAS_CARRIER (0x02) +#define CX24117_HAS_VITERBI (0x04) +#define CX24117_HAS_SYNCLOCK (0x08) +#define CX24117_STATUS_MASK (0x0f) +#define CX24117_SIGNAL_MASK (0xc0) + + +/* arg offset for DiSEqC */ +#define CX24117_DISEQC_DEMOD (1) +#define CX24117_DISEQC_BURST (2) +#define CX24117_DISEQC_ARG3_2 (3) /* unknown value=2 */ +#define CX24117_DISEQC_ARG4_0 (4) /* unknown value=0 */ +#define CX24117_DISEQC_ARG5_0 (5) /* unknown value=0 */ +#define CX24117_DISEQC_MSGLEN (6) +#define CX24117_DISEQC_MSGOFS (7) + +/* DiSEqC burst */ +#define CX24117_DISEQC_MINI_A (0) +#define CX24117_DISEQC_MINI_B (1) + + +#define CX24117_PNE (0) /* 0 disabled / 2 enabled */ +#define CX24117_OCC (1) /* 0 disabled / 1 enabled */ + + +enum cmds { + CMD_SET_VCOFREQ = 0x10, + CMD_TUNEREQUEST = 0x11, + CMD_GLOBAL_MPEGCFG = 0x13, + CMD_MPEGCFG = 0x14, + CMD_TUNERINIT = 0x15, + CMD_GET_SRATE = 0x18, + CMD_SET_GOLDCODE = 0x19, + CMD_GET_AGCACC = 0x1a, + CMD_DEMODINIT = 0x1b, + CMD_GETCTLACC = 0x1c, + + CMD_LNBCONFIG = 0x20, + CMD_LNBSEND = 0x21, + CMD_LNBDCLEVEL = 0x22, + CMD_LNBPCBCONFIG = 0x23, + CMD_LNBSENDTONEBST = 0x24, + CMD_LNBUPDREPLY = 0x25, + + CMD_SET_GPIOMODE = 0x30, + CMD_SET_GPIOEN = 0x31, + CMD_SET_GPIODIR = 0x32, + CMD_SET_GPIOOUT = 0x33, + CMD_ENABLERSCORR = 0x34, + CMD_FWVERSION = 0x35, + CMD_SET_SLEEPMODE = 0x36, + CMD_BERCTRL = 0x3c, + CMD_EVENTCTRL = 0x3d, +}; + +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(cx24117_list_mutex); + +/* The Demod/Tuner can't easily provide these, we cache them */ +struct cx24117_tuning { + u32 frequency; + u32 symbol_rate; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec; + + enum fe_delivery_system delsys; + enum fe_modulation modulation; + enum fe_pilot pilot; + enum fe_rolloff rolloff; + + /* Demod values */ + u8 fec_val; + u8 fec_mask; + u8 inversion_val; + u8 pilot_val; + u8 rolloff_val; +}; + +/* Basic commands that are sent to the firmware */ +struct cx24117_cmd { + u8 len; + u8 args[CX24117_ARGLEN]; +}; + +/* common to both fe's */ +struct cx24117_priv { + u8 demod_address; + struct i2c_adapter *i2c; + u8 skip_fw_load; + struct mutex fe_lock; + + /* Used for sharing this struct between demods */ + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; +}; + +/* one per each fe */ +struct cx24117_state { + struct cx24117_priv *priv; + struct dvb_frontend frontend; + + struct cx24117_tuning dcur; + struct cx24117_tuning dnxt; + struct cx24117_cmd dsec_cmd; + + int demod; +}; + +/* modfec (modulation and FEC) lookup table */ +/* Check cx24116.c for a detailed description of each field */ +static struct cx24117_modfec { + enum fe_delivery_system delivery_system; + enum fe_modulation modulation; + enum fe_code_rate fec; + u8 mask; /* In DVBS mode this is used to autodetect */ + u8 val; /* Passed to the firmware to indicate mode selection */ +} cx24117_modfec_modes[] = { + /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ + + /*mod fec mask val */ + { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, + { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ + { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ + { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ + { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ + { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ + { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ + { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ + { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ + { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, + /* NBC-QPSK */ + { SYS_DVBS2, QPSK, FEC_NONE, 0x00, 0x00 }, + { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, + { SYS_DVBS2, QPSK, FEC_AUTO, 0x00, 0x00 }, + /* 8PSK */ + { SYS_DVBS2, PSK_8, FEC_NONE, 0x00, 0x00 }, + { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, + { SYS_DVBS2, PSK_8, FEC_AUTO, 0x00, 0x00 }, + /* + * 'val' can be found in the FECSTATUS register when tuning. + * FECSTATUS will give the actual FEC in use if tuning was successful. + */ +}; + + +static int cx24117_writereg(struct cx24117_state *state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->priv->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + int ret; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d i2c wr @0x%02x=0x%02x\n", + __func__, state->demod, reg, data); + + ret = i2c_transfer(state->priv->i2c, &msg, 1); + if (ret < 0) { + dev_warn(&state->priv->i2c->dev, + "%s: demod%d i2c wr err(%i) @0x%02x=0x%02x\n", + KBUILD_MODNAME, state->demod, ret, reg, data); + return ret; + } + return 0; +} + +static int cx24117_writecmd(struct cx24117_state *state, + struct cx24117_cmd *cmd) +{ + struct i2c_msg msg; + u8 buf[CX24117_ARGLEN+1]; + int ret; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d i2c wr cmd len=%d\n", + __func__, state->demod, cmd->len); + + buf[0] = CX24117_REG_COMMAND; + memcpy(&buf[1], cmd->args, cmd->len); + + msg.addr = state->priv->demod_address; + msg.flags = 0; + msg.len = cmd->len+1; + msg.buf = buf; + ret = i2c_transfer(state->priv->i2c, &msg, 1); + if (ret < 0) { + dev_warn(&state->priv->i2c->dev, + "%s: demod%d i2c wr cmd err(%i) len=%d\n", + KBUILD_MODNAME, state->demod, ret, cmd->len); + return ret; + } + return 0; +} + +static int cx24117_readreg(struct cx24117_state *state, u8 reg) +{ + int ret; + u8 recv = 0; + struct i2c_msg msg[] = { + { .addr = state->priv->demod_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = state->priv->demod_address, .flags = I2C_M_RD, + .buf = &recv, .len = 1 } + }; + + ret = i2c_transfer(state->priv->i2c, msg, 2); + if (ret < 0) { + dev_warn(&state->priv->i2c->dev, + "%s: demod%d i2c rd err(%d) @0x%x\n", + KBUILD_MODNAME, state->demod, ret, reg); + return ret; + } + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d i2c rd @0x%02x=0x%02x\n", + __func__, state->demod, reg, recv); + + return recv; +} + +static int cx24117_readregN(struct cx24117_state *state, + u8 reg, u8 *buf, int len) +{ + int ret; + struct i2c_msg msg[] = { + { .addr = state->priv->demod_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = state->priv->demod_address, .flags = I2C_M_RD, + .buf = buf, .len = len } + }; + + ret = i2c_transfer(state->priv->i2c, msg, 2); + if (ret < 0) { + dev_warn(&state->priv->i2c->dev, + "%s: demod%d i2c rd err(%d) @0x%x\n", + KBUILD_MODNAME, state->demod, ret, reg); + return ret; + } + return 0; +} + +static int cx24117_set_inversion(struct cx24117_state *state, + enum fe_spectral_inversion inversion) +{ + dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n", + __func__, inversion, state->demod); + + switch (inversion) { + case INVERSION_OFF: + state->dnxt.inversion_val = 0x00; + break; + case INVERSION_ON: + state->dnxt.inversion_val = 0x04; + break; + case INVERSION_AUTO: + state->dnxt.inversion_val = 0x0C; + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +static int cx24117_lookup_fecmod(struct cx24117_state *state, + enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f) +{ + int i, ret = -EINVAL; + + dev_dbg(&state->priv->i2c->dev, + "%s(demod(0x%02x,0x%02x) demod%d\n", + __func__, m, f, state->demod); + + for (i = 0; i < ARRAY_SIZE(cx24117_modfec_modes); i++) { + if ((d == cx24117_modfec_modes[i].delivery_system) && + (m == cx24117_modfec_modes[i].modulation) && + (f == cx24117_modfec_modes[i].fec)) { + ret = i; + break; + } + } + + return ret; +} + +static int cx24117_set_fec(struct cx24117_state *state, + enum fe_delivery_system delsys, + enum fe_modulation mod, + enum fe_code_rate fec) +{ + int ret; + + dev_dbg(&state->priv->i2c->dev, + "%s(0x%02x,0x%02x) demod%d\n", + __func__, mod, fec, state->demod); + + ret = cx24117_lookup_fecmod(state, delsys, mod, fec); + if (ret < 0) + return ret; + + state->dnxt.fec = fec; + state->dnxt.fec_val = cx24117_modfec_modes[ret].val; + state->dnxt.fec_mask = cx24117_modfec_modes[ret].mask; + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d mask/val = 0x%02x/0x%02x\n", __func__, + state->demod, state->dnxt.fec_mask, state->dnxt.fec_val); + + return 0; +} + +static int cx24117_set_symbolrate(struct cx24117_state *state, u32 rate) +{ + dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n", + __func__, rate, state->demod); + + state->dnxt.symbol_rate = rate; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d symbol_rate = %d\n", + __func__, state->demod, rate); + + return 0; +} + +static int cx24117_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw); + +static int cx24117_firmware_ondemand(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret = 0; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d skip_fw_load=%d\n", + __func__, state->demod, state->priv->skip_fw_load); + + if (state->priv->skip_fw_load) + return 0; + + /* check if firmware is already running */ + if (cx24117_readreg(state, 0xeb) != 0xa) { + /* Load firmware */ + /* request the firmware, this will block until loaded */ + dev_dbg(&state->priv->i2c->dev, + "%s: Waiting for firmware upload (%s)...\n", + __func__, CX24117_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, CX24117_DEFAULT_FIRMWARE, + state->priv->i2c->dev.parent); + dev_dbg(&state->priv->i2c->dev, + "%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + dev_err(&state->priv->i2c->dev, + "%s: No firmware uploaded (timeout or file not found?)\n", +__func__); + return ret; + } + + /* Make sure we don't recurse back through here + * during loading */ + state->priv->skip_fw_load = 1; + + ret = cx24117_load_firmware(fe, fw); + if (ret) + dev_err(&state->priv->i2c->dev, + "%s: Writing firmware failed\n", __func__); + release_firmware(fw); + + dev_info(&state->priv->i2c->dev, + "%s: Firmware upload %s\n", __func__, + ret == 0 ? "complete" : "failed"); + + /* Ensure firmware is always loaded if required */ + state->priv->skip_fw_load = 0; + } + + return ret; +} + +/* Take a basic firmware command structure, format it + * and forward it for processing + */ +static int cx24117_cmd_execute_nolock(struct dvb_frontend *fe, + struct cx24117_cmd *cmd) +{ + struct cx24117_state *state = fe->demodulator_priv; + int i, ret; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n", + __func__, state->demod); + + /* Load the firmware if required */ + ret = cx24117_firmware_ondemand(fe); + if (ret != 0) + return ret; + + /* Write the command */ + cx24117_writecmd(state, cmd); + + /* Start execution and wait for cmd to terminate */ + cx24117_writereg(state, CX24117_REG_EXECUTE, 0x01); + i = 0; + while (cx24117_readreg(state, CX24117_REG_EXECUTE)) { + msleep(20); + if (i++ > 40) { + /* Avoid looping forever if the firmware does + not respond */ + dev_warn(&state->priv->i2c->dev, + "%s() Firmware not responding\n", __func__); + return -EIO; + } + } + return 0; +} + +static int cx24117_cmd_execute(struct dvb_frontend *fe, struct cx24117_cmd *cmd) +{ + struct cx24117_state *state = fe->demodulator_priv; + int ret; + + mutex_lock(&state->priv->fe_lock); + ret = cx24117_cmd_execute_nolock(fe, cmd); + mutex_unlock(&state->priv->fe_lock); + + return ret; +} + +static int cx24117_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + int i, ret; + unsigned char vers[4]; + + struct i2c_msg msg; + u8 *buf; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d FW is %zu bytes (%02x %02x .. %02x %02x)\n", + __func__, state->demod, fw->size, fw->data[0], fw->data[1], + fw->data[fw->size - 2], fw->data[fw->size - 1]); + + cx24117_writereg(state, 0xea, 0x00); + cx24117_writereg(state, 0xea, 0x01); + cx24117_writereg(state, 0xea, 0x00); + + cx24117_writereg(state, 0xce, 0x92); + + cx24117_writereg(state, 0xfb, 0x00); + cx24117_writereg(state, 0xfc, 0x00); + + cx24117_writereg(state, 0xc3, 0x04); + cx24117_writereg(state, 0xc4, 0x04); + + cx24117_writereg(state, 0xce, 0x00); + cx24117_writereg(state, 0xcf, 0x00); + + cx24117_writereg(state, 0xea, 0x00); + cx24117_writereg(state, 0xeb, 0x0c); + cx24117_writereg(state, 0xec, 0x06); + cx24117_writereg(state, 0xed, 0x05); + cx24117_writereg(state, 0xee, 0x03); + cx24117_writereg(state, 0xef, 0x05); + + cx24117_writereg(state, 0xf3, 0x03); + cx24117_writereg(state, 0xf4, 0x44); + + cx24117_writereg(state, CX24117_REG_RATEDIV0, 0x04); + cx24117_writereg(state, CX24117_REG_CLKDIV0, 0x02); + + cx24117_writereg(state, CX24117_REG_RATEDIV1, 0x04); + cx24117_writereg(state, CX24117_REG_CLKDIV1, 0x02); + + cx24117_writereg(state, 0xf2, 0x04); + cx24117_writereg(state, 0xe8, 0x02); + cx24117_writereg(state, 0xea, 0x01); + cx24117_writereg(state, 0xc8, 0x00); + cx24117_writereg(state, 0xc9, 0x00); + cx24117_writereg(state, 0xca, 0x00); + cx24117_writereg(state, 0xcb, 0x00); + cx24117_writereg(state, 0xcc, 0x00); + cx24117_writereg(state, 0xcd, 0x00); + cx24117_writereg(state, 0xe4, 0x03); + cx24117_writereg(state, 0xeb, 0x0a); + + cx24117_writereg(state, 0xfb, 0x00); + cx24117_writereg(state, 0xe0, 0x76); + cx24117_writereg(state, 0xf7, 0x81); + cx24117_writereg(state, 0xf8, 0x00); + cx24117_writereg(state, 0xf9, 0x00); + + buf = kmalloc(fw->size + 1, GFP_KERNEL); + if (buf == NULL) { + state->priv->skip_fw_load = 0; + return -ENOMEM; + } + + /* fw upload reg */ + buf[0] = 0xfa; + memcpy(&buf[1], fw->data, fw->size); + + /* prepare i2c message to send */ + msg.addr = state->priv->demod_address; + msg.flags = 0; + msg.len = fw->size + 1; + msg.buf = buf; + + /* send fw */ + ret = i2c_transfer(state->priv->i2c, &msg, 1); + if (ret < 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + cx24117_writereg(state, 0xf7, 0x0c); + cx24117_writereg(state, 0xe0, 0x00); + + /* Init demodulator */ + cmd.args[0] = CMD_DEMODINIT; + cmd.args[1] = 0x00; + cmd.args[2] = 0x01; + cmd.args[3] = 0x00; + cmd.len = 4; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto error; + + /* Set VCO frequency */ + cmd.args[0] = CMD_SET_VCOFREQ; + cmd.args[1] = 0x06; + cmd.args[2] = 0x2b; + cmd.args[3] = 0xd8; + cmd.args[4] = 0xa5; + cmd.args[5] = 0xee; + cmd.args[6] = 0x03; + cmd.args[7] = 0x9d; + cmd.args[8] = 0xfc; + cmd.args[9] = 0x06; + cmd.args[10] = 0x02; + cmd.args[11] = 0x9d; + cmd.args[12] = 0xfc; + cmd.len = 13; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto error; + + /* Tuner init */ + cmd.args[0] = CMD_TUNERINIT; + cmd.args[1] = 0x00; + cmd.args[2] = 0x01; + cmd.args[3] = 0x00; + cmd.args[4] = 0x00; + cmd.args[5] = 0x01; + cmd.args[6] = 0x01; + cmd.args[7] = 0x01; + cmd.args[8] = 0x00; + cmd.args[9] = 0x05; + cmd.args[10] = 0x02; + cmd.args[11] = 0x02; + cmd.args[12] = 0x00; + cmd.len = 13; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto error; + + /* Global MPEG config */ + cmd.args[0] = CMD_GLOBAL_MPEGCFG; + cmd.args[1] = 0x00; + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.args[4] = 0x01; + cmd.args[5] = 0x00; + cmd.len = 6; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto error; + + /* MPEG config for each demod */ + for (i = 0; i < 2; i++) { + cmd.args[0] = CMD_MPEGCFG; + cmd.args[1] = (u8) i; + cmd.args[2] = 0x00; + cmd.args[3] = 0x05; + cmd.args[4] = 0x00; + cmd.args[5] = 0x00; + cmd.args[6] = 0x55; + cmd.args[7] = 0x00; + cmd.len = 8; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto error; + } + + cx24117_writereg(state, 0xce, 0xc0); + cx24117_writereg(state, 0xcf, 0x00); + cx24117_writereg(state, 0xe5, 0x04); + + /* Get firmware version */ + cmd.args[0] = CMD_FWVERSION; + cmd.len = 2; + for (i = 0; i < 4; i++) { + cmd.args[1] = i; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto error; + vers[i] = cx24117_readreg(state, 0x33); + } + dev_info(&state->priv->i2c->dev, + "%s: FW version %i.%i.%i.%i\n", __func__, + vers[0], vers[1], vers[2], vers[3]); + return 0; +error: + state->priv->skip_fw_load = 0; + dev_err(&state->priv->i2c->dev, "%s() Error running FW.\n", __func__); + return ret; +} + +static int cx24117_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx24117_state *state = fe->demodulator_priv; + int lock; + + lock = cx24117_readreg(state, + (state->demod == 0) ? CX24117_REG_SSTATUS0 : + CX24117_REG_SSTATUS1) & + CX24117_STATUS_MASK; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d status = 0x%02x\n", + __func__, state->demod, lock); + + *status = 0; + + if (lock & CX24117_HAS_SIGNAL) + *status |= FE_HAS_SIGNAL; + if (lock & CX24117_HAS_CARRIER) + *status |= FE_HAS_CARRIER; + if (lock & CX24117_HAS_VITERBI) + *status |= FE_HAS_VITERBI; + if (lock & CX24117_HAS_SYNCLOCK) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + return 0; +} + +static int cx24117_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cx24117_state *state = fe->demodulator_priv; + int ret; + u8 buf[4]; + u8 base_reg = (state->demod == 0) ? + CX24117_REG_BER4_0 : + CX24117_REG_BER4_1; + + ret = cx24117_readregN(state, base_reg, buf, 4); + if (ret != 0) + return ret; + + *ber = (buf[0] << 24) | (buf[1] << 16) | + (buf[1] << 8) | buf[0]; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d ber=0x%04x\n", + __func__, state->demod, *ber); + + return 0; +} + +static int cx24117_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + int ret; + u16 sig_reading; + u8 buf[2]; + u8 reg = (state->demod == 0) ? + CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1; + + /* Read AGC accumulator register */ + cmd.args[0] = CMD_GET_AGCACC; + cmd.args[1] = (u8) state->demod; + cmd.len = 2; + ret = cx24117_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + ret = cx24117_readregN(state, reg, buf, 2); + if (ret != 0) + return ret; + sig_reading = ((buf[0] & CX24117_SIGNAL_MASK) << 2) | buf[1]; + + *signal_strength = -100 * sig_reading + 94324; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d raw / cooked = 0x%04x / 0x%04x\n", + __func__, state->demod, sig_reading, *signal_strength); + + return 0; +} + +static int cx24117_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct cx24117_state *state = fe->demodulator_priv; + int ret; + u8 buf[2]; + u8 reg = (state->demod == 0) ? + CX24117_REG_QUALITY2_0 : CX24117_REG_QUALITY2_1; + + ret = cx24117_readregN(state, reg, buf, 2); + if (ret != 0) + return ret; + + *snr = (buf[0] << 8) | buf[1]; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d snr = 0x%04x\n", + __func__, state->demod, *snr); + + return ret; +} + +static int cx24117_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct cx24117_state *state = fe->demodulator_priv; + enum fe_delivery_system delsys = fe->dtv_property_cache.delivery_system; + int ret; + u8 buf[2]; + u8 reg = (state->demod == 0) ? + CX24117_REG_DVBS_UCB2_0 : + CX24117_REG_DVBS_UCB2_1; + + switch (delsys) { + case SYS_DVBS: + break; + case SYS_DVBS2: + reg += (CX24117_REG_DVBS2_UCB2_0 - CX24117_REG_DVBS_UCB2_0); + break; + default: + return -EINVAL; + } + + ret = cx24117_readregN(state, reg, buf, 2); + if (ret != 0) + return ret; + *ucblocks = (buf[0] << 8) | buf[1]; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d ucb=0x%04x\n", + __func__, state->demod, *ucblocks); + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void cx24117_clone_params(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + state->dcur = state->dnxt; +} + +/* Wait for LNB */ +static int cx24117_wait_for_lnb(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + int i; + u8 val, reg = (state->demod == 0) ? CX24117_REG_QSTATUS0 : + CX24117_REG_QSTATUS1; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d qstatus = 0x%02x\n", + __func__, state->demod, cx24117_readreg(state, reg)); + + /* Wait for up to 300 ms */ + for (i = 0; i < 10; i++) { + val = cx24117_readreg(state, reg) & 0x01; + if (val != 0) + return 0; + msleep(30); + } + + dev_warn(&state->priv->i2c->dev, "%s: demod%d LNB not ready\n", + KBUILD_MODNAME, state->demod); + + return -ETIMEDOUT; /* -EBUSY ? */ +} + +static int cx24117_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + int ret; + u8 reg = (state->demod == 0) ? 0x10 : 0x20; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d %s\n", + __func__, state->demod, + voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : + "SEC_VOLTAGE_OFF"); + + /* Prepare a set GPIO logic level CMD */ + cmd.args[0] = CMD_SET_GPIOOUT; + cmd.args[2] = reg; /* mask */ + cmd.len = 3; + + if ((voltage == SEC_VOLTAGE_13) || + (voltage == SEC_VOLTAGE_18)) { + /* power on LNB */ + cmd.args[1] = reg; + ret = cx24117_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + ret = cx24117_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + /* Set 13V/18V select pin */ + cmd.args[0] = CMD_LNBDCLEVEL; + cmd.args[1] = state->demod ? 0 : 1; + cmd.args[2] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); + cmd.len = 3; + ret = cx24117_cmd_execute(fe, &cmd); + + /* Min delay time before DiSEqC send */ + msleep(20); + } else { + /* power off LNB */ + cmd.args[1] = 0x00; + ret = cx24117_cmd_execute(fe, &cmd); + } + + return ret; +} + +static int cx24117_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + int ret; + + dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n", + __func__, state->demod, tone); + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + dev_warn(&state->priv->i2c->dev, "%s: demod%d invalid tone=%d\n", + KBUILD_MODNAME, state->demod, tone); + return -EINVAL; + } + + /* Wait for LNB ready */ + ret = cx24117_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Min delay time after DiSEqC send */ + msleep(20); + + /* Set the tone */ + cmd.args[0] = CMD_LNBPCBCONFIG; + cmd.args[1] = (state->demod ? 0 : 1); + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.len = 5; + switch (tone) { + case SEC_TONE_ON: + cmd.args[4] = 0x01; + break; + case SEC_TONE_OFF: + cmd.args[4] = 0x00; + break; + } + + msleep(20); + + return cx24117_cmd_execute(fe, &cmd); +} + +/* Initialise DiSEqC */ +static int cx24117_diseqc_init(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + + /* Prepare a DiSEqC command */ + state->dsec_cmd.args[0] = CMD_LNBSEND; + + /* demod */ + state->dsec_cmd.args[CX24117_DISEQC_DEMOD] = state->demod ? 0 : 1; + + /* DiSEqC burst */ + state->dsec_cmd.args[CX24117_DISEQC_BURST] = CX24117_DISEQC_MINI_A; + + /* Unknown */ + state->dsec_cmd.args[CX24117_DISEQC_ARG3_2] = 0x02; + state->dsec_cmd.args[CX24117_DISEQC_ARG4_0] = 0x00; + + /* Continuation flag? */ + state->dsec_cmd.args[CX24117_DISEQC_ARG5_0] = 0x00; + + /* DiSEqC message length */ + state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = 0x00; + + /* Command length */ + state->dsec_cmd.len = 7; + + return 0; +} + +/* Send DiSEqC message */ +static int cx24117_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct cx24117_state *state = fe->demodulator_priv; + int i, ret; + + /* Dump DiSEqC message */ + dev_dbg(&state->priv->i2c->dev, "%s: demod %d (", + __func__, state->demod); + for (i = 0; i < d->msg_len; i++) + dev_dbg(&state->priv->i2c->dev, "0x%02x ", d->msg[i]); + dev_dbg(&state->priv->i2c->dev, ")\n"); + + /* Validate length */ + if (d->msg_len > sizeof(d->msg)) + return -EINVAL; + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + state->dsec_cmd.args[CX24117_DISEQC_MSGOFS + i] = d->msg[i]; + + /* DiSEqC message length */ + state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = d->msg_len; + + /* Command length */ + state->dsec_cmd.len = CX24117_DISEQC_MSGOFS + + state->dsec_cmd.args[CX24117_DISEQC_MSGLEN]; + + /* + * Message is sent with derived else cached burst + * + * WRITE PORT GROUP COMMAND 38 + * + * 0/A/A: E0 10 38 F0..F3 + * 1/B/B: E0 10 38 F4..F7 + * 2/C/A: E0 10 38 F8..FB + * 3/D/B: E0 10 38 FC..FF + * + * databyte[3]= 8421:8421 + * ABCD:WXYZ + * CLR :SET + * + * WX= PORT SELECT 0..3 (X=TONEBURST) + * Y = VOLTAGE (0=13V, 1=18V) + * Z = BAND (0=LOW, 1=HIGH(22K)) + */ + if (d->msg_len >= 4 && d->msg[2] == 0x38) + state->dsec_cmd.args[CX24117_DISEQC_BURST] = + ((d->msg[3] & 4) >> 2); + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d burst=%d\n", + __func__, state->demod, + state->dsec_cmd.args[CX24117_DISEQC_BURST]); + + /* Wait for LNB ready */ + ret = cx24117_wait_for_lnb(fe); + if (ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + /* Command */ + ret = cx24117_cmd_execute(fe, &state->dsec_cmd); + if (ret != 0) + return ret; + /* + * Wait for send + * + * Eutelsat spec: + * >15ms delay + (XXX determine if FW does this, see set_tone) + * 13.5ms per byte + + * >15ms delay + + * 12.5ms burst + + * >15ms delay (XXX determine if FW does this, see set_tone) + */ + msleep((state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] << 4) + 60); + + return 0; +} + +/* Send DiSEqC burst */ +static int cx24117_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct cx24117_state *state = fe->demodulator_priv; + + dev_dbg(&state->priv->i2c->dev, "%s(%d) demod=%d\n", + __func__, burst, state->demod); + + /* DiSEqC burst */ + if (burst == SEC_MINI_A) + state->dsec_cmd.args[CX24117_DISEQC_BURST] = + CX24117_DISEQC_MINI_A; + else if (burst == SEC_MINI_B) + state->dsec_cmd.args[CX24117_DISEQC_BURST] = + CX24117_DISEQC_MINI_B; + else + return -EINVAL; + + return 0; +} + +static int cx24117_get_priv(struct cx24117_priv **priv, + struct i2c_adapter *i2c, u8 client_address) +{ + int ret; + + mutex_lock(&cx24117_list_mutex); + ret = hybrid_tuner_request_state(struct cx24117_priv, (*priv), + hybrid_tuner_instance_list, i2c, client_address, "cx24117"); + mutex_unlock(&cx24117_list_mutex); + + return ret; +} + +static void cx24117_release_priv(struct cx24117_priv *priv) +{ + mutex_lock(&cx24117_list_mutex); + if (priv != NULL) + hybrid_tuner_release_state(priv); + mutex_unlock(&cx24117_list_mutex); +} + +static void cx24117_release(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + dev_dbg(&state->priv->i2c->dev, "%s demod%d\n", + __func__, state->demod); + cx24117_release_priv(state->priv); + kfree(state); +} + +static const struct dvb_frontend_ops cx24117_ops; + +struct dvb_frontend *cx24117_attach(const struct cx24117_config *config, + struct i2c_adapter *i2c) +{ + struct cx24117_state *state = NULL; + struct cx24117_priv *priv = NULL; + int demod = 0; + + /* get the common data struct for both demods */ + demod = cx24117_get_priv(&priv, i2c, config->demod_address); + + switch (demod) { + case 0: + dev_err(&i2c->dev, + "%s: Error attaching frontend %d\n", + KBUILD_MODNAME, demod); + goto error1; + case 1: + /* new priv instance */ + priv->i2c = i2c; + priv->demod_address = config->demod_address; + mutex_init(&priv->fe_lock); + break; + default: + /* existing priv instance */ + break; + } + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct cx24117_state), GFP_KERNEL); + if (state == NULL) + goto error2; + + state->demod = demod - 1; + state->priv = priv; + + dev_info(&state->priv->i2c->dev, + "%s: Attaching frontend %d\n", + KBUILD_MODNAME, state->demod); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx24117_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error2: + cx24117_release_priv(priv); +error1: + return NULL; +} +EXPORT_SYMBOL_GPL(cx24117_attach); + +/* + * Initialise or wake up device + * + * Power config will reset and load initial firmware if required + */ +static int cx24117_initfe(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + int ret; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n", + __func__, state->demod); + + mutex_lock(&state->priv->fe_lock); + + /* Set sleep mode off */ + cmd.args[0] = CMD_SET_SLEEPMODE; + cmd.args[1] = (state->demod ? 1 : 0); + cmd.args[2] = 0; + cmd.len = 3; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto exit; + + ret = cx24117_diseqc_init(fe); + if (ret != 0) + goto exit; + + /* Set BER control */ + cmd.args[0] = CMD_BERCTRL; + cmd.args[1] = (state->demod ? 1 : 0); + cmd.args[2] = 0x10; + cmd.args[3] = 0x10; + cmd.len = 4; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto exit; + + /* Set RS correction (enable/disable) */ + cmd.args[0] = CMD_ENABLERSCORR; + cmd.args[1] = (state->demod ? 1 : 0); + cmd.args[2] = CX24117_OCC; + cmd.len = 3; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + if (ret != 0) + goto exit; + + /* Set GPIO direction */ + /* Set as output - controls LNB power on/off */ + cmd.args[0] = CMD_SET_GPIODIR; + cmd.args[1] = 0x30; + cmd.args[2] = 0x30; + cmd.len = 3; + ret = cx24117_cmd_execute_nolock(fe, &cmd); + +exit: + mutex_unlock(&state->priv->fe_lock); + + return ret; +} + +/* + * Put device to sleep + */ +static int cx24117_sleep(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n", + __func__, state->demod); + + /* Set sleep mode on */ + cmd.args[0] = CMD_SET_SLEEPMODE; + cmd.args[1] = (state->demod ? 1 : 0); + cmd.args[2] = 1; + cmd.len = 3; + return cx24117_cmd_execute(fe, &cmd); +} + +/* dvb-core told us to tune, the tv property cache will be complete, + * it's safe for is to pull values and use them for tuning purposes. + */ +static int cx24117_set_frontend(struct dvb_frontend *fe) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24117_cmd cmd; + enum fe_status tunerstat; + int i, status, ret, retune = 1; + u8 reg_clkdiv, reg_ratediv; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n", + __func__, state->demod); + + switch (c->delivery_system) { + case SYS_DVBS: + dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S\n", + __func__, state->demod); + + /* Only QPSK is supported for DVB-S */ + if (c->modulation != QPSK) { + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d unsupported modulation (%d)\n", + __func__, state->demod, c->modulation); + return -EINVAL; + } + + /* Pilot doesn't exist in DVB-S, turn bit off */ + state->dnxt.pilot_val = CX24117_PILOT_OFF; + + /* DVB-S only supports 0.35 */ + state->dnxt.rolloff_val = CX24117_ROLLOFF_035; + break; + + case SYS_DVBS2: + dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S2\n", + __func__, state->demod); + + /* + * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, + * but not hardware auto detection + */ + if (c->modulation != PSK_8 && c->modulation != QPSK) { + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d unsupported modulation (%d)\n", + __func__, state->demod, c->modulation); + return -EOPNOTSUPP; + } + + switch (c->pilot) { + case PILOT_AUTO: + state->dnxt.pilot_val = CX24117_PILOT_AUTO; + break; + case PILOT_OFF: + state->dnxt.pilot_val = CX24117_PILOT_OFF; + break; + case PILOT_ON: + state->dnxt.pilot_val = CX24117_PILOT_ON; + break; + default: + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d unsupported pilot mode (%d)\n", + __func__, state->demod, c->pilot); + return -EOPNOTSUPP; + } + + switch (c->rolloff) { + case ROLLOFF_20: + state->dnxt.rolloff_val = CX24117_ROLLOFF_020; + break; + case ROLLOFF_25: + state->dnxt.rolloff_val = CX24117_ROLLOFF_025; + break; + case ROLLOFF_35: + state->dnxt.rolloff_val = CX24117_ROLLOFF_035; + break; + case ROLLOFF_AUTO: + state->dnxt.rolloff_val = CX24117_ROLLOFF_035; + /* soft-auto rolloff */ + retune = 3; + break; + default: + dev_warn(&state->priv->i2c->dev, + "%s: demod%d unsupported rolloff (%d)\n", + KBUILD_MODNAME, state->demod, c->rolloff); + return -EOPNOTSUPP; + } + break; + + default: + dev_warn(&state->priv->i2c->dev, + "%s: demod %d unsupported delivery system (%d)\n", + KBUILD_MODNAME, state->demod, c->delivery_system); + return -EINVAL; + } + + state->dnxt.delsys = c->delivery_system; + state->dnxt.modulation = c->modulation; + state->dnxt.frequency = c->frequency; + state->dnxt.pilot = c->pilot; + state->dnxt.rolloff = c->rolloff; + + ret = cx24117_set_inversion(state, c->inversion); + if (ret != 0) + return ret; + + ret = cx24117_set_fec(state, + c->delivery_system, c->modulation, c->fec_inner); + if (ret != 0) + return ret; + + ret = cx24117_set_symbolrate(state, c->symbol_rate); + if (ret != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + cx24117_clone_params(fe); + + dev_dbg(&state->priv->i2c->dev, + "%s: delsys = %d\n", __func__, state->dcur.delsys); + dev_dbg(&state->priv->i2c->dev, + "%s: modulation = %d\n", __func__, state->dcur.modulation); + dev_dbg(&state->priv->i2c->dev, + "%s: frequency = %d\n", __func__, state->dcur.frequency); + dev_dbg(&state->priv->i2c->dev, + "%s: pilot = %d (val = 0x%02x)\n", __func__, + state->dcur.pilot, state->dcur.pilot_val); + dev_dbg(&state->priv->i2c->dev, + "%s: retune = %d\n", __func__, retune); + dev_dbg(&state->priv->i2c->dev, + "%s: rolloff = %d (val = 0x%02x)\n", __func__, + state->dcur.rolloff, state->dcur.rolloff_val); + dev_dbg(&state->priv->i2c->dev, + "%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); + dev_dbg(&state->priv->i2c->dev, + "%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, + state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); + dev_dbg(&state->priv->i2c->dev, + "%s: Inversion = %d (val = 0x%02x)\n", __func__, + state->dcur.inversion, state->dcur.inversion_val); + + /* Prepare a tune request */ + cmd.args[0] = CMD_TUNEREQUEST; + + /* demod */ + cmd.args[1] = state->demod; + + /* Frequency */ + cmd.args[2] = (state->dcur.frequency & 0xff0000) >> 16; + cmd.args[3] = (state->dcur.frequency & 0x00ff00) >> 8; + cmd.args[4] = (state->dcur.frequency & 0x0000ff); + + /* Symbol Rate */ + cmd.args[5] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; + cmd.args[6] = ((state->dcur.symbol_rate / 1000) & 0x00ff); + + /* Automatic Inversion */ + cmd.args[7] = state->dcur.inversion_val; + + /* Modulation / FEC / Pilot */ + cmd.args[8] = state->dcur.fec_val | state->dcur.pilot_val; + + cmd.args[9] = CX24117_SEARCH_RANGE_KHZ >> 8; + cmd.args[10] = CX24117_SEARCH_RANGE_KHZ & 0xff; + + cmd.args[11] = state->dcur.rolloff_val; + cmd.args[12] = state->dcur.fec_mask; + + if (state->dcur.symbol_rate > 30000000) { + reg_ratediv = 0x04; + reg_clkdiv = 0x02; + } else if (state->dcur.symbol_rate > 10000000) { + reg_ratediv = 0x06; + reg_clkdiv = 0x03; + } else { + reg_ratediv = 0x0a; + reg_clkdiv = 0x05; + } + + cmd.args[13] = reg_ratediv; + cmd.args[14] = reg_clkdiv; + + cx24117_writereg(state, (state->demod == 0) ? + CX24117_REG_CLKDIV0 : CX24117_REG_CLKDIV1, reg_clkdiv); + cx24117_writereg(state, (state->demod == 0) ? + CX24117_REG_RATEDIV0 : CX24117_REG_RATEDIV1, reg_ratediv); + + cmd.args[15] = CX24117_PNE; + cmd.len = 16; + + do { + /* Reset status register */ + status = cx24117_readreg(state, (state->demod == 0) ? + CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1) & + CX24117_SIGNAL_MASK; + + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d status_setfe = %02x\n", + __func__, state->demod, status); + + cx24117_writereg(state, (state->demod == 0) ? + CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1, status); + + /* Tune */ + ret = cx24117_cmd_execute(fe, &cmd); + if (ret != 0) + break; + + /* + * Wait for up to 500 ms before retrying + * + * If we are able to tune then generally it occurs within 100ms. + * If it takes longer, try a different rolloff setting. + */ + for (i = 0; i < 50; i++) { + cx24117_read_status(fe, &tunerstat); + status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); + if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { + dev_dbg(&state->priv->i2c->dev, + "%s() demod%d tuned\n", + __func__, state->demod); + return 0; + } + msleep(20); + } + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d not tuned\n", + __func__, state->demod); + + /* try next rolloff value */ + if (state->dcur.rolloff == 3) + cmd.args[11]--; + + } while (--retune); + return -EINVAL; +} + +static int cx24117_tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, unsigned int *delay, enum fe_status *status) +{ + struct cx24117_state *state = fe->demodulator_priv; + + dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n", + __func__, state->demod); + + *delay = HZ / 5; + if (re_tune) { + int ret = cx24117_set_frontend(fe); + if (ret) + return ret; + } + return cx24117_read_status(fe, status); +} + +static enum dvbfe_algo cx24117_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int cx24117_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cx24117_state *state = fe->demodulator_priv; + struct cx24117_cmd cmd; + u8 reg, st, inv; + int ret, idx; + unsigned int freq; + short srate_os, freq_os; + + u8 buf[0x1f-4]; + + /* Read current tune parameters */ + cmd.args[0] = CMD_GETCTLACC; + cmd.args[1] = (u8) state->demod; + cmd.len = 2; + ret = cx24117_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* read all required regs at once */ + reg = (state->demod == 0) ? CX24117_REG_FREQ3_0 : CX24117_REG_FREQ3_1; + ret = cx24117_readregN(state, reg, buf, 0x1f-4); + if (ret != 0) + return ret; + + st = buf[5]; + + /* get spectral inversion */ + inv = (((state->demod == 0) ? ~st : st) >> 6) & 1; + if (inv == 0) + c->inversion = INVERSION_OFF; + else + c->inversion = INVERSION_ON; + + /* modulation and fec */ + idx = st & 0x3f; + if (c->delivery_system == SYS_DVBS2) { + if (idx > 11) + idx += 9; + else + idx += 7; + } + + c->modulation = cx24117_modfec_modes[idx].modulation; + c->fec_inner = cx24117_modfec_modes[idx].fec; + + /* frequency */ + freq = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + freq_os = (buf[8] << 8) | buf[9]; + c->frequency = freq + freq_os; + + /* symbol rate */ + srate_os = (buf[10] << 8) | buf[11]; + c->symbol_rate = -1000 * srate_os + state->dcur.symbol_rate; + return 0; +} + +static const struct dvb_frontend_ops cx24117_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Conexant CX24117/CX24132", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24117_release, + + .init = cx24117_initfe, + .sleep = cx24117_sleep, + .read_status = cx24117_read_status, + .read_ber = cx24117_read_ber, + .read_signal_strength = cx24117_read_signal_strength, + .read_snr = cx24117_read_snr, + .read_ucblocks = cx24117_read_ucblocks, + .set_tone = cx24117_set_tone, + .set_voltage = cx24117_set_voltage, + .diseqc_send_master_cmd = cx24117_send_diseqc_msg, + .diseqc_send_burst = cx24117_diseqc_send_burst, + .get_frontend_algo = cx24117_get_algo, + .tune = cx24117_tune, + + .set_frontend = cx24117_set_frontend, + .get_frontend = cx24117_get_frontend, +}; + + +MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24117/cx24132 hardware"); +MODULE_AUTHOR("Luis Alves (ljalvs@gmail.com)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.1"); +MODULE_FIRMWARE(CX24117_DEFAULT_FIRMWARE); + diff --git a/drivers/media/dvb-frontends/cx24117.h b/drivers/media/dvb-frontends/cx24117.h new file mode 100644 index 0000000000..a613a33dbb --- /dev/null +++ b/drivers/media/dvb-frontends/cx24117.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2013 Luis Alves <ljalvs@gmail.com> + (based on cx24116.h by Steven Toth) + +*/ + +#ifndef CX24117_H +#define CX24117_H + +#include <linux/dvb/frontend.h> + +struct cx24117_config { + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_CX24117) +extern struct dvb_frontend *cx24117_attach( + const struct cx24117_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *cx24117_attach( + const struct cx24117_config *config, + struct i2c_adapter *i2c) +{ + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* CX24117_H */ diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c new file mode 100644 index 0000000000..44515fdbe9 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24120.c @@ -0,0 +1,1591 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org> + Copyright (C) 2009 Sergey Tyurin <forum.free-x.de> + Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de> + Copyright (C) 2015 Jemma Denson <jdenson@gmail.com> + April 2015 + Refactored & simplified driver + Updated to work with delivery system supplied by DVBv5 + Add frequency, fec & pilot to get_frontend + + Cards supported: Technisat Skystar S2 + +*/ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/firmware.h> +#include <media/dvb_frontend.h> +#include "cx24120.h" + +#define CX24120_SEARCH_RANGE_KHZ 5000 +#define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw" + +/* cx24120 i2c registers */ +#define CX24120_REG_CMD_START 0x00 /* write cmd_id */ +#define CX24120_REG_CMD_ARGS 0x01 /* write command arguments */ +#define CX24120_REG_CMD_END 0x1f /* write 0x01 for end */ + +#define CX24120_REG_MAILBOX 0x33 +#define CX24120_REG_FREQ3 0x34 /* frequency */ +#define CX24120_REG_FREQ2 0x35 +#define CX24120_REG_FREQ1 0x36 + +#define CX24120_REG_FECMODE 0x39 /* FEC status */ +#define CX24120_REG_STATUS 0x3a /* Tuner status */ +#define CX24120_REG_SIGSTR_H 0x3a /* Signal strength high */ +#define CX24120_REG_SIGSTR_L 0x3b /* Signal strength low byte */ +#define CX24120_REG_QUALITY_H 0x40 /* SNR high byte */ +#define CX24120_REG_QUALITY_L 0x41 /* SNR low byte */ + +#define CX24120_REG_BER_HH 0x47 /* BER high byte of high word */ +#define CX24120_REG_BER_HL 0x48 /* BER low byte of high word */ +#define CX24120_REG_BER_LH 0x49 /* BER high byte of low word */ +#define CX24120_REG_BER_LL 0x4a /* BER low byte of low word */ + +#define CX24120_REG_UCB_H 0x50 /* UCB high byte */ +#define CX24120_REG_UCB_L 0x51 /* UCB low byte */ + +#define CX24120_REG_CLKDIV 0xe6 +#define CX24120_REG_RATEDIV 0xf0 + +#define CX24120_REG_REVISION 0xff /* Chip revision (ro) */ + +/* Command messages */ +enum command_message_id { + CMD_VCO_SET = 0x10, /* cmd.len = 12; */ + CMD_TUNEREQUEST = 0x11, /* cmd.len = 15; */ + + CMD_MPEG_ONOFF = 0x13, /* cmd.len = 4; */ + CMD_MPEG_INIT = 0x14, /* cmd.len = 7; */ + CMD_BANDWIDTH = 0x15, /* cmd.len = 12; */ + CMD_CLOCK_READ = 0x16, /* read clock */ + CMD_CLOCK_SET = 0x17, /* cmd.len = 10; */ + + CMD_DISEQC_MSG1 = 0x20, /* cmd.len = 11; */ + CMD_DISEQC_MSG2 = 0x21, /* cmd.len = d->msg_len + 6; */ + CMD_SETVOLTAGE = 0x22, /* cmd.len = 2; */ + CMD_SETTONE = 0x23, /* cmd.len = 4; */ + CMD_DISEQC_BURST = 0x24, /* cmd.len not used !!! */ + + CMD_READ_SNR = 0x1a, /* Read signal strength */ + CMD_START_TUNER = 0x1b, /* ??? */ + + CMD_FWVERSION = 0x35, + + CMD_BER_CTRL = 0x3c, /* cmd.len = 0x03; */ +}; + +#define CX24120_MAX_CMD_LEN 30 + +/* pilot mask */ +#define CX24120_PILOT_OFF 0x00 +#define CX24120_PILOT_ON 0x40 +#define CX24120_PILOT_AUTO 0x80 + +/* signal status */ +#define CX24120_HAS_SIGNAL 0x01 +#define CX24120_HAS_CARRIER 0x02 +#define CX24120_HAS_VITERBI 0x04 +#define CX24120_HAS_LOCK 0x08 +#define CX24120_HAS_UNK1 0x10 +#define CX24120_HAS_UNK2 0x20 +#define CX24120_STATUS_MASK 0x0f +#define CX24120_SIGNAL_MASK 0xc0 + +/* ber window */ +#define CX24120_BER_WINDOW 16 +#define CX24120_BER_WSIZE ((1 << CX24120_BER_WINDOW) * 208 * 8) + +#define info(args...) pr_info("cx24120: " args) +#define err(args...) pr_err("cx24120: ### ERROR: " args) + +/* The Demod/Tuner can't easily provide these, we cache them */ +struct cx24120_tuning { + u32 frequency; + u32 symbol_rate; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec; + + enum fe_delivery_system delsys; + enum fe_modulation modulation; + enum fe_pilot pilot; + + /* Demod values */ + u8 fec_val; + u8 fec_mask; + u8 clkdiv; + u8 ratediv; + u8 inversion_val; + u8 pilot_val; +}; + +/* Private state */ +struct cx24120_state { + struct i2c_adapter *i2c; + const struct cx24120_config *config; + struct dvb_frontend frontend; + + u8 cold_init; + u8 mpeg_enabled; + u8 need_clock_set; + + /* current and next tuning parameters */ + struct cx24120_tuning dcur; + struct cx24120_tuning dnxt; + + enum fe_status fe_status; + + /* dvbv5 stats calculations */ + u32 bitrate; + u32 berw_usecs; + u32 ber_prev; + u32 ucb_offset; + unsigned long ber_jiffies_stats; + unsigned long per_jiffies_stats; +}; + +/* Command message to firmware */ +struct cx24120_cmd { + u8 id; + u8 len; + u8 arg[CX24120_MAX_CMD_LEN]; +}; + +/* Read single register */ +static int cx24120_readreg(struct cx24120_state *state, u8 reg) +{ + int ret; + u8 buf = 0; + struct i2c_msg msg[] = { + { + .addr = state->config->i2c_addr, + .flags = 0, + .len = 1, + .buf = ® + }, { + .addr = state->config->i2c_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &buf + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + err("Read error: reg=0x%02x, ret=%i)\n", reg, ret); + return ret; + } + + dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, buf); + + return buf; +} + +/* Write single register */ +static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->i2c_addr, + .flags = 0, + .buf = buf, + .len = 2 + }; + int ret; + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n", + ret, reg, data); + return ret; + } + + dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, data); + + return 0; +} + +/* Write multiple registers in chunks of i2c_wr_max-sized buffers */ +static int cx24120_writeregs(struct cx24120_state *state, + u8 reg, const u8 *values, u16 len, u8 incr) +{ + int ret; + u16 max = state->config->i2c_wr_max > 0 ? + state->config->i2c_wr_max : + len; + + struct i2c_msg msg = { + .addr = state->config->i2c_addr, + .flags = 0, + }; + + msg.buf = kmalloc(max + 1, GFP_KERNEL); + if (!msg.buf) + return -ENOMEM; + + while (len) { + msg.buf[0] = reg; + msg.len = len > max ? max : len; + memcpy(&msg.buf[1], values, msg.len); + + len -= msg.len; /* data length revers counter */ + values += msg.len; /* incr data pointer */ + + if (incr) + reg += msg.len; + msg.len++; /* don't forget the addr byte */ + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + err("i2c_write error(err == %i, 0x%02x)\n", ret, reg); + goto out; + } + + dev_dbg(&state->i2c->dev, "reg=0x%02x; data=%*ph\n", + reg, msg.len - 1, msg.buf + 1); + } + + ret = 0; + +out: + kfree(msg.buf); + return ret; +} + +static const struct dvb_frontend_ops cx24120_ops; + +struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, + struct i2c_adapter *i2c) +{ + struct cx24120_state *state; + int demod_rev; + + info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n"); + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + err("Unable to allocate memory for cx24120_state\n"); + goto error; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is present and has proper type */ + demod_rev = cx24120_readreg(state, CX24120_REG_REVISION); + switch (demod_rev) { + case 0x07: + info("Demod cx24120 rev. 0x07 detected.\n"); + break; + case 0x05: + info("Demod cx24120 rev. 0x05 detected.\n"); + break; + default: + err("Unsupported demod revision: 0x%x detected.\n", demod_rev); + goto error; + } + + /* create dvb_frontend */ + state->cold_init = 0; + memcpy(&state->frontend.ops, &cx24120_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + info("Conexant cx24120/cx24118 attached.\n"); + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(cx24120_attach); + +static int cx24120_test_rom(struct cx24120_state *state) +{ + int err, ret; + + err = cx24120_readreg(state, 0xfd); + if (err & 4) { + ret = cx24120_readreg(state, 0xdf) & 0xfe; + err = cx24120_writereg(state, 0xdf, ret); + } + return err; +} + +static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->cnr.stat[0].scale != FE_SCALE_DECIBEL) + *snr = 0; + else + *snr = div_s64(c->cnr.stat[0].svalue, 100); + + return 0; +} + +static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) { + *ber = 0; + return 0; + } + + *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev; + state->ber_prev = c->post_bit_error.stat[0].uvalue; + + return 0; +} + +static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, + u8 flag); + +/* Check if we're running a command that needs to disable mpeg out */ +static void cx24120_check_cmd(struct cx24120_state *state, u8 id) +{ + switch (id) { + case CMD_TUNEREQUEST: + case CMD_CLOCK_READ: + case CMD_DISEQC_MSG1: + case CMD_DISEQC_MSG2: + case CMD_SETVOLTAGE: + case CMD_SETTONE: + case CMD_DISEQC_BURST: + cx24120_msg_mpeg_output_global_config(state, 0); + /* Old driver would do a msleep(100) here */ + return; + default: + return; + } +} + +/* Send a message to the firmware */ +static int cx24120_message_send(struct cx24120_state *state, + struct cx24120_cmd *cmd) +{ + int ficus; + + if (state->mpeg_enabled) { + /* Disable mpeg out on certain commands */ + cx24120_check_cmd(state, cmd->id); + } + + cx24120_writereg(state, CX24120_REG_CMD_START, cmd->id); + cx24120_writeregs(state, CX24120_REG_CMD_ARGS, &cmd->arg[0], + cmd->len, 1); + cx24120_writereg(state, CX24120_REG_CMD_END, 0x01); + + ficus = 1000; + while (cx24120_readreg(state, CX24120_REG_CMD_END)) { + msleep(20); + ficus -= 20; + if (ficus == 0) { + err("Error sending message to firmware\n"); + return -EREMOTEIO; + } + } + dev_dbg(&state->i2c->dev, "sent message 0x%02x\n", cmd->id); + + return 0; +} + +/* Send a message and fill arg[] with the results */ +static int cx24120_message_sendrcv(struct cx24120_state *state, + struct cx24120_cmd *cmd, u8 numreg) +{ + int ret, i; + + if (numreg > CX24120_MAX_CMD_LEN) { + err("Too many registers to read. cmd->reg = %d", numreg); + return -EREMOTEIO; + } + + ret = cx24120_message_send(state, cmd); + if (ret != 0) + return ret; + + if (!numreg) + return 0; + + /* Read numreg registers starting from register cmd->len */ + for (i = 0; i < numreg; i++) + cmd->arg[i] = cx24120_readreg(state, (cmd->len + i + 1)); + + return 0; +} + +static int cx24120_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->strength.stat[0].scale != FE_SCALE_RELATIVE) + *signal_strength = 0; + else + *signal_strength = c->strength.stat[0].uvalue; + + return 0; +} + +static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, + u8 enable) +{ + struct cx24120_cmd cmd; + int ret; + + cmd.id = CMD_MPEG_ONOFF; + cmd.len = 4; + cmd.arg[0] = 0x01; + cmd.arg[1] = 0x00; + cmd.arg[2] = enable ? 0 : (u8)(-1); + cmd.arg[3] = 0x01; + + ret = cx24120_message_send(state, &cmd); + if (ret != 0) { + dev_dbg(&state->i2c->dev, "failed to %s MPEG output\n", + enable ? "enable" : "disable"); + return ret; + } + + state->mpeg_enabled = enable; + dev_dbg(&state->i2c->dev, "MPEG output %s\n", + enable ? "enabled" : "disabled"); + + return 0; +} + +static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 seq) +{ + struct cx24120_cmd cmd; + struct cx24120_initial_mpeg_config i = + state->config->initial_mpeg_config; + + cmd.id = CMD_MPEG_INIT; + cmd.len = 7; + cmd.arg[0] = seq; /* sequental number - can be 0,1,2 */ + cmd.arg[1] = ((i.x1 & 0x01) << 1) | ((i.x1 >> 1) & 0x01); + cmd.arg[2] = 0x05; + cmd.arg[3] = 0x02; + cmd.arg[4] = ((i.x2 >> 1) & 0x01); + cmd.arg[5] = (i.x2 & 0xf0) | (i.x3 & 0x0f); + cmd.arg[6] = 0x10; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + + dev_dbg(&state->i2c->dev, "\n"); + + /* + * Yes, cmd.len is set to zero. The old driver + * didn't specify any len, but also had a + * memset 0 before every use of the cmd struct + * which would have set it to zero. + * This quite probably needs looking into. + */ + cmd.id = CMD_DISEQC_BURST; + cmd.len = 0; + cmd.arg[0] = 0x00; + cmd.arg[1] = (burst == SEC_MINI_B) ? 0x01 : 0x00; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + + dev_dbg(&state->i2c->dev, "(%d)\n", tone); + + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + err("Invalid tone=%d\n", tone); + return -EINVAL; + } + + cmd.id = CMD_SETTONE; + cmd.len = 4; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x00; + cmd.arg[3] = (tone == SEC_TONE_ON) ? 0x01 : 0x00; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + + dev_dbg(&state->i2c->dev, "(%d)\n", voltage); + + cmd.id = CMD_SETVOLTAGE; + cmd.len = 2; + cmd.arg[0] = 0x00; + cmd.arg[1] = (voltage == SEC_VOLTAGE_18) ? 0x01 : 0x00; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + int back_count; + + dev_dbg(&state->i2c->dev, "\n"); + + cmd.id = CMD_DISEQC_MSG1; + cmd.len = 11; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x03; + cmd.arg[3] = 0x16; + cmd.arg[4] = 0x28; + cmd.arg[5] = 0x01; + cmd.arg[6] = 0x01; + cmd.arg[7] = 0x14; + cmd.arg[8] = 0x19; + cmd.arg[9] = 0x14; + cmd.arg[10] = 0x1e; + + if (cx24120_message_send(state, &cmd)) { + err("send 1st message(0x%x) failed\n", cmd.id); + return -EREMOTEIO; + } + + cmd.id = CMD_DISEQC_MSG2; + cmd.len = d->msg_len + 6; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x01; + cmd.arg[2] = 0x02; + cmd.arg[3] = 0x00; + cmd.arg[4] = 0x00; + cmd.arg[5] = d->msg_len; + + memcpy(&cmd.arg[6], &d->msg, d->msg_len); + + if (cx24120_message_send(state, &cmd)) { + err("send 2nd message(0x%x) failed\n", cmd.id); + return -EREMOTEIO; + } + + back_count = 500; + do { + if (!(cx24120_readreg(state, 0x93) & 0x01)) { + dev_dbg(&state->i2c->dev, "diseqc sequence sent\n"); + return 0; + } + msleep(20); + back_count -= 20; + } while (back_count); + + err("Too long waiting for diseqc.\n"); + return -ETIMEDOUT; +} + +static void cx24120_get_stats(struct cx24120_state *state) +{ + struct dvb_frontend *fe = &state->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_cmd cmd; + int ret, cnr, msecs; + u16 sig, ucb; + u32 ber; + + dev_dbg(&state->i2c->dev, "\n"); + + /* signal strength */ + if (state->fe_status & FE_HAS_SIGNAL) { + cmd.id = CMD_READ_SNR; + cmd.len = 1; + cmd.arg[0] = 0x00; + + ret = cx24120_message_send(state, &cmd); + if (ret != 0) { + err("error reading signal strength\n"); + return; + } + + /* raw */ + sig = cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6; + sig = sig << 8; + sig |= cx24120_readreg(state, CX24120_REG_SIGSTR_L); + dev_dbg(&state->i2c->dev, + "signal strength from firmware = 0x%x\n", sig); + + /* cooked */ + sig = -100 * sig + 94324; + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = sig; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (state->fe_status & FE_HAS_VITERBI) { + cnr = cx24120_readreg(state, CX24120_REG_QUALITY_H) << 8; + cnr |= cx24120_readreg(state, CX24120_REG_QUALITY_L); + dev_dbg(&state->i2c->dev, "read SNR index = %d\n", cnr); + + /* guessed - seems about right */ + cnr = cnr * 100; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER & UCB require lock */ + if (!(state->fe_status & FE_HAS_LOCK)) { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + /* BER */ + if (time_after(jiffies, state->ber_jiffies_stats)) { + msecs = (state->berw_usecs + 500) / 1000; + state->ber_jiffies_stats = jiffies + msecs_to_jiffies(msecs); + + ber = cx24120_readreg(state, CX24120_REG_BER_HH) << 24; + ber |= cx24120_readreg(state, CX24120_REG_BER_HL) << 16; + ber |= cx24120_readreg(state, CX24120_REG_BER_LH) << 8; + ber |= cx24120_readreg(state, CX24120_REG_BER_LL); + dev_dbg(&state->i2c->dev, "read BER index = %d\n", ber); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += ber; + + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += CX24120_BER_WSIZE; + } + + /* UCB */ + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); + + ucb = cx24120_readreg(state, CX24120_REG_UCB_H) << 8; + ucb |= cx24120_readreg(state, CX24120_REG_UCB_L); + dev_dbg(&state->i2c->dev, "ucblocks = %d\n", ucb); + + /* handle reset */ + if (ucb < state->ucb_offset) + state->ucb_offset = c->block_error.stat[0].uvalue; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = ucb + state->ucb_offset; + + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += state->bitrate / 8 / 208; + } +} + +static void cx24120_set_clock_ratios(struct dvb_frontend *fe); + +/* Read current tuning status */ +static int cx24120_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx24120_state *state = fe->demodulator_priv; + int lock; + + lock = cx24120_readreg(state, CX24120_REG_STATUS); + + dev_dbg(&state->i2c->dev, "status = 0x%02x\n", lock); + + *status = 0; + + if (lock & CX24120_HAS_SIGNAL) + *status = FE_HAS_SIGNAL; + if (lock & CX24120_HAS_CARRIER) + *status |= FE_HAS_CARRIER; + if (lock & CX24120_HAS_VITERBI) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC; + if (lock & CX24120_HAS_LOCK) + *status |= FE_HAS_LOCK; + + /* + * TODO: is FE_HAS_SYNC in the right place? + * Other cx241xx drivers have this slightly + * different + */ + + state->fe_status = *status; + cx24120_get_stats(state); + + /* Set the clock once tuned in */ + if (state->need_clock_set && *status & FE_HAS_LOCK) { + /* Set clock ratios */ + cx24120_set_clock_ratios(fe); + + /* Old driver would do a msleep(200) here */ + + /* Renable mpeg output */ + if (!state->mpeg_enabled) + cx24120_msg_mpeg_output_global_config(state, 1); + + state->need_clock_set = 0; + } + + return 0; +} + +/* + * FEC & modulation lookup table + * Used for decoding the REG_FECMODE register + * once tuned in. + */ +struct cx24120_modfec { + enum fe_delivery_system delsys; + enum fe_modulation mod; + enum fe_code_rate fec; + u8 val; +}; + +static const struct cx24120_modfec modfec_lookup_table[] = { + /*delsys mod fec val */ + { SYS_DVBS, QPSK, FEC_1_2, 0x01 }, + { SYS_DVBS, QPSK, FEC_2_3, 0x02 }, + { SYS_DVBS, QPSK, FEC_3_4, 0x03 }, + { SYS_DVBS, QPSK, FEC_4_5, 0x04 }, + { SYS_DVBS, QPSK, FEC_5_6, 0x05 }, + { SYS_DVBS, QPSK, FEC_6_7, 0x06 }, + { SYS_DVBS, QPSK, FEC_7_8, 0x07 }, + + { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, + + { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, +}; + +/* Retrieve current fec, modulation & pilot values */ +static int cx24120_get_fec(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + int idx; + int ret; + int fec; + + ret = cx24120_readreg(state, CX24120_REG_FECMODE); + fec = ret & 0x3f; /* Lower 6 bits */ + + dev_dbg(&state->i2c->dev, "raw fec = %d\n", fec); + + for (idx = 0; idx < ARRAY_SIZE(modfec_lookup_table); idx++) { + if (modfec_lookup_table[idx].delsys != state->dcur.delsys) + continue; + if (modfec_lookup_table[idx].val != fec) + continue; + + break; /* found */ + } + + if (idx >= ARRAY_SIZE(modfec_lookup_table)) { + dev_dbg(&state->i2c->dev, "couldn't find fec!\n"); + return -EINVAL; + } + + /* save values back to cache */ + c->modulation = modfec_lookup_table[idx].mod; + c->fec_inner = modfec_lookup_table[idx].fec; + c->pilot = (ret & 0x80) ? PILOT_ON : PILOT_OFF; + + dev_dbg(&state->i2c->dev, "mod(%d), fec(%d), pilot(%d)\n", + c->modulation, c->fec_inner, c->pilot); + + return 0; +} + +/* Calculate ber window time */ +static void cx24120_calculate_ber_window(struct cx24120_state *state, u32 rate) +{ + struct dvb_frontend *fe = &state->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u64 tmp; + + /* + * Calculate bitrate from rate in the clock ratios table. + * This isn't *exactly* right but close enough. + */ + tmp = (u64)c->symbol_rate * rate; + do_div(tmp, 256); + state->bitrate = tmp; + + /* usecs per ber window */ + tmp = 1000000ULL * CX24120_BER_WSIZE; + do_div(tmp, state->bitrate); + state->berw_usecs = tmp; + + dev_dbg(&state->i2c->dev, "bitrate: %u, berw_usecs: %u\n", + state->bitrate, state->berw_usecs); +} + +/* + * Clock ratios lookup table + * + * Values obtained from much larger table in old driver + * which had numerous entries which would never match. + * + * There's probably some way of calculating these but I + * can't determine the pattern + */ +struct cx24120_clock_ratios_table { + enum fe_delivery_system delsys; + enum fe_pilot pilot; + enum fe_modulation mod; + enum fe_code_rate fec; + u32 m_rat; + u32 n_rat; + u32 rate; +}; + +static const struct cx24120_clock_ratios_table clock_ratios_table[] = { + /*delsys pilot mod fec m_rat n_rat rate */ + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_1_2, 273088, 254505, 274 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_5, 17272, 13395, 330 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_2_3, 24344, 16967, 367 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_4, 410788, 254505, 413 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_4_5, 438328, 254505, 440 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_5_6, 30464, 16967, 459 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_8_9, 487832, 254505, 490 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_9_10, 493952, 254505, 496 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_5, 328168, 169905, 494 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_2_3, 24344, 11327, 550 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_4, 410788, 169905, 618 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_5_6, 30464, 11327, 688 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_8_9, 487832, 169905, 735 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_9_10, 493952, 169905, 744 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_1_2, 273088, 260709, 268 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_5, 328168, 260709, 322 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_2_3, 121720, 86903, 358 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_4, 410788, 260709, 403 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_4_5, 438328, 260709, 430 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_5_6, 152320, 86903, 448 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_8_9, 487832, 260709, 479 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_9_10, 493952, 260709, 485 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_5, 328168, 173853, 483 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_2_3, 121720, 57951, 537 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_4, 410788, 173853, 604 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_5_6, 152320, 57951, 672 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_8_9, 487832, 173853, 718 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_9_10, 493952, 173853, 727 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_1_2, 152592, 152592, 256 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_2_3, 305184, 228888, 341 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_3_4, 457776, 305184, 384 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_5_6, 762960, 457776, 427 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_7_8, 1068144, 610368, 448 }, +}; + +/* Set clock ratio from lookup table */ +static void cx24120_set_clock_ratios(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + int ret, idx; + + /* Find fec, modulation, pilot */ + ret = cx24120_get_fec(fe); + if (ret != 0) + return; + + /* Find the clock ratios in the lookup table */ + for (idx = 0; idx < ARRAY_SIZE(clock_ratios_table); idx++) { + if (clock_ratios_table[idx].delsys != state->dcur.delsys) + continue; + if (clock_ratios_table[idx].mod != c->modulation) + continue; + if (clock_ratios_table[idx].fec != c->fec_inner) + continue; + if (clock_ratios_table[idx].pilot != c->pilot) + continue; + + break; /* found */ + } + + if (idx >= ARRAY_SIZE(clock_ratios_table)) { + info("Clock ratio not found - data reception in danger\n"); + return; + } + + /* Read current values? */ + cmd.id = CMD_CLOCK_READ; + cmd.len = 1; + cmd.arg[0] = 0x00; + ret = cx24120_message_sendrcv(state, &cmd, 6); + if (ret != 0) + return; + /* in cmd[0]-[5] - result */ + + dev_dbg(&state->i2c->dev, "m=%d, n=%d; idx: %d m=%d, n=%d, rate=%d\n", + cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16), + cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16), + idx, + clock_ratios_table[idx].m_rat, + clock_ratios_table[idx].n_rat, + clock_ratios_table[idx].rate); + + /* Set the clock */ + cmd.id = CMD_CLOCK_SET; + cmd.len = 10; + cmd.arg[0] = 0; + cmd.arg[1] = 0x10; + cmd.arg[2] = (clock_ratios_table[idx].m_rat >> 16) & 0xff; + cmd.arg[3] = (clock_ratios_table[idx].m_rat >> 8) & 0xff; + cmd.arg[4] = (clock_ratios_table[idx].m_rat >> 0) & 0xff; + cmd.arg[5] = (clock_ratios_table[idx].n_rat >> 16) & 0xff; + cmd.arg[6] = (clock_ratios_table[idx].n_rat >> 8) & 0xff; + cmd.arg[7] = (clock_ratios_table[idx].n_rat >> 0) & 0xff; + cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff; + cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff; + + ret = cx24120_message_send(state, &cmd); + if (ret != 0) + return; + + /* Calculate ber window rates for stat work */ + cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate); +} + +/* Set inversion value */ +static int cx24120_set_inversion(struct cx24120_state *state, + enum fe_spectral_inversion inversion) +{ + dev_dbg(&state->i2c->dev, "(%d)\n", inversion); + + switch (inversion) { + case INVERSION_OFF: + state->dnxt.inversion_val = 0x00; + break; + case INVERSION_ON: + state->dnxt.inversion_val = 0x04; + break; + case INVERSION_AUTO: + state->dnxt.inversion_val = 0x0c; + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +/* FEC lookup table for tuning */ +struct cx24120_modfec_table { + enum fe_delivery_system delsys; + enum fe_modulation mod; + enum fe_code_rate fec; + u8 val; +}; + +static const struct cx24120_modfec_table modfec_table[] = { + /*delsys mod fec val */ + { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, + { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, + { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, + { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, + { SYS_DVBS, QPSK, FEC_6_7, 0x32 }, + { SYS_DVBS, QPSK, FEC_7_8, 0x33 }, + + { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, + + { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, +}; + +/* Set fec_val & fec_mask values from delsys, modulation & fec */ +static int cx24120_set_fec(struct cx24120_state *state, enum fe_modulation mod, + enum fe_code_rate fec) +{ + int idx; + + dev_dbg(&state->i2c->dev, "(0x%02x,0x%02x)\n", mod, fec); + + state->dnxt.fec = fec; + + /* Lookup fec_val from modfec table */ + for (idx = 0; idx < ARRAY_SIZE(modfec_table); idx++) { + if (modfec_table[idx].delsys != state->dnxt.delsys) + continue; + if (modfec_table[idx].mod != mod) + continue; + if (modfec_table[idx].fec != fec) + continue; + + /* found */ + state->dnxt.fec_mask = 0x00; + state->dnxt.fec_val = modfec_table[idx].val; + return 0; + } + + if (state->dnxt.delsys == SYS_DVBS2) { + /* DVBS2 auto is 0x00/0x00 */ + state->dnxt.fec_mask = 0x00; + state->dnxt.fec_val = 0x00; + } else { + /* Set DVB-S to auto */ + state->dnxt.fec_val = 0x2e; + state->dnxt.fec_mask = 0xac; + } + + return 0; +} + +/* Set pilot */ +static int cx24120_set_pilot(struct cx24120_state *state, enum fe_pilot pilot) +{ + dev_dbg(&state->i2c->dev, "(%d)\n", pilot); + + /* Pilot only valid in DVBS2 */ + if (state->dnxt.delsys != SYS_DVBS2) { + state->dnxt.pilot_val = CX24120_PILOT_OFF; + return 0; + } + + switch (pilot) { + case PILOT_OFF: + state->dnxt.pilot_val = CX24120_PILOT_OFF; + break; + case PILOT_ON: + state->dnxt.pilot_val = CX24120_PILOT_ON; + break; + case PILOT_AUTO: + default: + state->dnxt.pilot_val = CX24120_PILOT_AUTO; + } + + return 0; +} + +/* Set symbol rate */ +static int cx24120_set_symbolrate(struct cx24120_state *state, u32 rate) +{ + dev_dbg(&state->i2c->dev, "(%d)\n", rate); + + state->dnxt.symbol_rate = rate; + + /* Check symbol rate */ + if (rate > 31000000) { + state->dnxt.clkdiv = (-(rate < 31000001) & 3) + 2; + state->dnxt.ratediv = (-(rate < 31000001) & 6) + 4; + } else { + state->dnxt.clkdiv = 3; + state->dnxt.ratediv = 6; + } + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void cx24120_clone_params(struct dvb_frontend *fe) +{ + struct cx24120_state *state = fe->demodulator_priv; + + state->dcur = state->dnxt; +} + +static int cx24120_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + int ret; + + switch (c->delivery_system) { + case SYS_DVBS2: + dev_dbg(&state->i2c->dev, "DVB-S2\n"); + break; + case SYS_DVBS: + dev_dbg(&state->i2c->dev, "DVB-S\n"); + break; + default: + dev_dbg(&state->i2c->dev, + "delivery system(%d) not supported\n", + c->delivery_system); + return -EINVAL; + } + + state->dnxt.delsys = c->delivery_system; + state->dnxt.modulation = c->modulation; + state->dnxt.frequency = c->frequency; + state->dnxt.pilot = c->pilot; + + ret = cx24120_set_inversion(state, c->inversion); + if (ret != 0) + return ret; + + ret = cx24120_set_fec(state, c->modulation, c->fec_inner); + if (ret != 0) + return ret; + + ret = cx24120_set_pilot(state, c->pilot); + if (ret != 0) + return ret; + + ret = cx24120_set_symbolrate(state, c->symbol_rate); + if (ret != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + cx24120_clone_params(fe); + + dev_dbg(&state->i2c->dev, + "delsys = %d\n", state->dcur.delsys); + dev_dbg(&state->i2c->dev, + "modulation = %d\n", state->dcur.modulation); + dev_dbg(&state->i2c->dev, + "frequency = %d\n", state->dcur.frequency); + dev_dbg(&state->i2c->dev, + "pilot = %d (val = 0x%02x)\n", + state->dcur.pilot, state->dcur.pilot_val); + dev_dbg(&state->i2c->dev, + "symbol_rate = %d (clkdiv/ratediv = 0x%02x/0x%02x)\n", + state->dcur.symbol_rate, + state->dcur.clkdiv, state->dcur.ratediv); + dev_dbg(&state->i2c->dev, + "FEC = %d (mask/val = 0x%02x/0x%02x)\n", + state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); + dev_dbg(&state->i2c->dev, + "Inversion = %d (val = 0x%02x)\n", + state->dcur.inversion, state->dcur.inversion_val); + + /* Flag that clock needs to be set after tune */ + state->need_clock_set = 1; + + /* Tune in */ + cmd.id = CMD_TUNEREQUEST; + cmd.len = 15; + cmd.arg[0] = 0; + cmd.arg[1] = (state->dcur.frequency & 0xff0000) >> 16; + cmd.arg[2] = (state->dcur.frequency & 0x00ff00) >> 8; + cmd.arg[3] = (state->dcur.frequency & 0x0000ff); + cmd.arg[4] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; + cmd.arg[5] = ((state->dcur.symbol_rate / 1000) & 0x00ff); + cmd.arg[6] = state->dcur.inversion; + cmd.arg[7] = state->dcur.fec_val | state->dcur.pilot_val; + cmd.arg[8] = CX24120_SEARCH_RANGE_KHZ >> 8; + cmd.arg[9] = CX24120_SEARCH_RANGE_KHZ & 0xff; + cmd.arg[10] = 0; /* maybe rolloff? */ + cmd.arg[11] = state->dcur.fec_mask; + cmd.arg[12] = state->dcur.ratediv; + cmd.arg[13] = state->dcur.clkdiv; + cmd.arg[14] = 0; + + /* Send tune command */ + ret = cx24120_message_send(state, &cmd); + if (ret != 0) + return ret; + + /* Write symbol rate values */ + ret = cx24120_writereg(state, CX24120_REG_CLKDIV, state->dcur.clkdiv); + ret = cx24120_readreg(state, CX24120_REG_RATEDIV); + ret &= 0xfffffff0; + ret |= state->dcur.ratediv; + ret = cx24120_writereg(state, CX24120_REG_RATEDIV, ret); + + return 0; +} + +/* Set vco from config */ +static int cx24120_set_vco(struct cx24120_state *state) +{ + struct cx24120_cmd cmd; + u32 nxtal_khz, vco; + u64 inv_vco; + u32 xtal_khz = state->config->xtal_khz; + + nxtal_khz = xtal_khz * 4; + vco = nxtal_khz * 10; + inv_vco = DIV_ROUND_CLOSEST_ULL(0x400000000ULL, vco); + + dev_dbg(&state->i2c->dev, "xtal=%d, vco=%d, inv_vco=%lld\n", + xtal_khz, vco, inv_vco); + + cmd.id = CMD_VCO_SET; + cmd.len = 12; + cmd.arg[0] = (vco >> 16) & 0xff; + cmd.arg[1] = (vco >> 8) & 0xff; + cmd.arg[2] = vco & 0xff; + cmd.arg[3] = (inv_vco >> 8) & 0xff; + cmd.arg[4] = (inv_vco) & 0xff; + cmd.arg[5] = 0x03; + cmd.arg[6] = (nxtal_khz >> 8) & 0xff; + cmd.arg[7] = nxtal_khz & 0xff; + cmd.arg[8] = 0x06; + cmd.arg[9] = 0x03; + cmd.arg[10] = (xtal_khz >> 16) & 0xff; + cmd.arg[11] = xtal_khz & 0xff; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_init(struct dvb_frontend *fe) +{ + const struct firmware *fw; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + u8 reg; + int ret, i; + unsigned char vers[4]; + + if (state->cold_init) + return 0; + + /* ???? */ + cx24120_writereg(state, 0xea, 0x00); + cx24120_test_rom(state); + reg = cx24120_readreg(state, 0xfb) & 0xfe; + cx24120_writereg(state, 0xfb, reg); + reg = cx24120_readreg(state, 0xfc) & 0xfe; + cx24120_writereg(state, 0xfc, reg); + cx24120_writereg(state, 0xc3, 0x04); + cx24120_writereg(state, 0xc4, 0x04); + cx24120_writereg(state, 0xce, 0x00); + cx24120_writereg(state, 0xcf, 0x00); + reg = cx24120_readreg(state, 0xea) & 0xfe; + cx24120_writereg(state, 0xea, reg); + cx24120_writereg(state, 0xeb, 0x0c); + cx24120_writereg(state, 0xec, 0x06); + cx24120_writereg(state, 0xed, 0x05); + cx24120_writereg(state, 0xee, 0x03); + cx24120_writereg(state, 0xef, 0x05); + cx24120_writereg(state, 0xf3, 0x03); + cx24120_writereg(state, 0xf4, 0x44); + + for (i = 0; i < 3; i++) { + cx24120_writereg(state, 0xf0 + i, 0x04); + cx24120_writereg(state, 0xe6 + i, 0x02); + } + + cx24120_writereg(state, 0xea, (reg | 0x01)); + for (i = 0; i < 6; i += 2) { + cx24120_writereg(state, 0xc5 + i, 0x00); + cx24120_writereg(state, 0xc6 + i, 0x00); + } + + cx24120_writereg(state, 0xe4, 0x03); + cx24120_writereg(state, 0xeb, 0x0a); + + dev_dbg(&state->i2c->dev, "requesting firmware (%s) to download...\n", + CX24120_FIRMWARE); + + ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE); + if (ret) { + err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE, + ret); + return ret; + } + + dev_dbg(&state->i2c->dev, + "Firmware found, size %d bytes (%02x %02x .. %02x %02x)\n", + (int)fw->size, /* firmware_size in bytes */ + fw->data[0], /* fw 1st byte */ + fw->data[1], /* fw 2d byte */ + fw->data[fw->size - 2], /* fw before last byte */ + fw->data[fw->size - 1]); /* fw last byte */ + + cx24120_test_rom(state); + reg = cx24120_readreg(state, 0xfb) & 0xfe; + cx24120_writereg(state, 0xfb, reg); + cx24120_writereg(state, 0xe0, 0x76); + cx24120_writereg(state, 0xf7, 0x81); + cx24120_writereg(state, 0xf8, 0x00); + cx24120_writereg(state, 0xf9, 0x00); + cx24120_writeregs(state, 0xfa, fw->data, (fw->size - 1), 0x00); + cx24120_writereg(state, 0xf7, 0xc0); + cx24120_writereg(state, 0xe0, 0x00); + reg = (fw->size - 2) & 0x00ff; + cx24120_writereg(state, 0xf8, reg); + reg = ((fw->size - 2) >> 8) & 0x00ff; + cx24120_writereg(state, 0xf9, reg); + cx24120_writereg(state, 0xf7, 0x00); + cx24120_writereg(state, 0xdc, 0x00); + cx24120_writereg(state, 0xdc, 0x07); + msleep(500); + + /* Check final byte matches final byte of firmware */ + reg = cx24120_readreg(state, 0xe1); + if (reg == fw->data[fw->size - 1]) { + dev_dbg(&state->i2c->dev, "Firmware uploaded successfully\n"); + ret = 0; + } else { + err("Firmware upload failed. Last byte returned=0x%x\n", ret); + ret = -EREMOTEIO; + } + cx24120_writereg(state, 0xdc, 0x00); + release_firmware(fw); + if (ret != 0) + return ret; + + /* Start tuner */ + cmd.id = CMD_START_TUNER; + cmd.len = 3; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x00; + + if (cx24120_message_send(state, &cmd) != 0) { + err("Error tuner start! :(\n"); + return -EREMOTEIO; + } + + /* Set VCO */ + ret = cx24120_set_vco(state); + if (ret != 0) { + err("Error set VCO! :(\n"); + return ret; + } + + /* set bandwidth */ + cmd.id = CMD_BANDWIDTH; + cmd.len = 12; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x00; + cmd.arg[3] = 0x00; + cmd.arg[4] = 0x05; + cmd.arg[5] = 0x02; + cmd.arg[6] = 0x02; + cmd.arg[7] = 0x00; + cmd.arg[8] = 0x05; + cmd.arg[9] = 0x02; + cmd.arg[10] = 0x02; + cmd.arg[11] = 0x00; + + if (cx24120_message_send(state, &cmd)) { + err("Error set bandwidth!\n"); + return -EREMOTEIO; + } + + reg = cx24120_readreg(state, 0xba); + if (reg > 3) { + dev_dbg(&state->i2c->dev, "Reset-readreg 0xba: %x\n", ret); + err("Error initialising tuner!\n"); + return -EREMOTEIO; + } + + dev_dbg(&state->i2c->dev, "Tuner initialised correctly.\n"); + + /* Initialise mpeg outputs */ + cx24120_writereg(state, 0xeb, 0x0a); + if (cx24120_msg_mpeg_output_global_config(state, 0) || + cx24120_msg_mpeg_output_config(state, 0) || + cx24120_msg_mpeg_output_config(state, 1) || + cx24120_msg_mpeg_output_config(state, 2)) { + err("Error initialising mpeg output. :(\n"); + return -EREMOTEIO; + } + + /* Set size of BER window */ + cmd.id = CMD_BER_CTRL; + cmd.len = 3; + cmd.arg[0] = 0x00; + cmd.arg[1] = CX24120_BER_WINDOW; + cmd.arg[2] = CX24120_BER_WINDOW; + if (cx24120_message_send(state, &cmd)) { + err("Error setting ber window\n"); + return -EREMOTEIO; + } + + /* Firmware CMD 35: Get firmware version */ + cmd.id = CMD_FWVERSION; + cmd.len = 1; + for (i = 0; i < 4; i++) { + cmd.arg[0] = i; + ret = cx24120_message_send(state, &cmd); + if (ret != 0) + return ret; + vers[i] = cx24120_readreg(state, CX24120_REG_MAILBOX); + } + info("FW version %i.%i.%i.%i\n", vers[0], vers[1], vers[2], vers[3]); + + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + state->cold_init = 1; + + return 0; +} + +static int cx24120_tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, unsigned int *delay, + enum fe_status *status) +{ + struct cx24120_state *state = fe->demodulator_priv; + int ret; + + dev_dbg(&state->i2c->dev, "(%d)\n", re_tune); + + /* TODO: Do we need to set delay? */ + + if (re_tune) { + ret = cx24120_set_frontend(fe); + if (ret) + return ret; + } + + return cx24120_read_status(fe, status); +} + +static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int cx24120_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int cx24120_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cx24120_state *state = fe->demodulator_priv; + u8 freq1, freq2, freq3; + int status; + + dev_dbg(&state->i2c->dev, "\n"); + + /* don't return empty data if we're not tuned in */ + status = cx24120_readreg(state, CX24120_REG_STATUS); + if (!(status & CX24120_HAS_LOCK)) + return 0; + + /* Get frequency */ + freq1 = cx24120_readreg(state, CX24120_REG_FREQ1); + freq2 = cx24120_readreg(state, CX24120_REG_FREQ2); + freq3 = cx24120_readreg(state, CX24120_REG_FREQ3); + c->frequency = (freq3 << 16) | (freq2 << 8) | freq1; + dev_dbg(&state->i2c->dev, "frequency = %d\n", c->frequency); + + /* Get modulation, fec, pilot */ + cx24120_get_fec(fe); + + return 0; +} + +static void cx24120_release(struct dvb_frontend *fe) +{ + struct cx24120_state *state = fe->demodulator_priv; + + dev_dbg(&state->i2c->dev, "Clear state structure\n"); + kfree(state); +} + +static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->block_error.stat[0].scale != FE_SCALE_COUNTER) { + *ucblocks = 0; + return 0; + } + + *ucblocks = c->block_error.stat[0].uvalue - state->ucb_offset; + + return 0; +} + +static const struct dvb_frontend_ops cx24120_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Conexant CX24120/CX24118", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + .release = cx24120_release, + + .init = cx24120_init, + .sleep = cx24120_sleep, + + .tune = cx24120_tune, + .get_frontend_algo = cx24120_get_algo, + .set_frontend = cx24120_set_frontend, + + .get_frontend = cx24120_get_frontend, + .read_status = cx24120_read_status, + .read_ber = cx24120_read_ber, + .read_signal_strength = cx24120_read_signal_strength, + .read_snr = cx24120_read_snr, + .read_ucblocks = cx24120_read_ucblocks, + + .diseqc_send_master_cmd = cx24120_send_diseqc_msg, + + .diseqc_send_burst = cx24120_diseqc_send_burst, + .set_tone = cx24120_set_tone, + .set_voltage = cx24120_set_voltage, +}; + +MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware"); +MODULE_AUTHOR("Jemma Denson"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cx24120.h b/drivers/media/dvb-frontends/cx24120.h new file mode 100644 index 0000000000..371b1d3d55 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24120.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Conexant CX24120/CX24118 - DVB-S/S2 demod/tuner driver + * + * Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org> + * Copyright (C) 2009 Sergey Tyurin <forum.free-x.de> + * Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de> + * Copyright (C) 2015 Jemma Denson <jdenson@gmail.com> + */ + +#ifndef CX24120_H +#define CX24120_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct cx24120_initial_mpeg_config { + u8 x1; + u8 x2; + u8 x3; +}; + +struct cx24120_config { + u8 i2c_addr; + u32 xtal_khz; + struct cx24120_initial_mpeg_config initial_mpeg_config; + + int (*request_firmware)(struct dvb_frontend *fe, + const struct firmware **fw, char *name); + + /* max bytes I2C provider can write at once */ + u16 i2c_wr_max; +}; + +#if IS_REACHABLE(CONFIG_DVB_CX24120) +struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* CX24120_H */ diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c new file mode 100644 index 0000000000..539889e638 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24123.c @@ -0,0 +1,1139 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver + * + * Copyright (C) 2005 Steven Toth <stoth@linuxtv.org> + * + * Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc> + * + * Support for CX24123/CX24113-NIM by Patrick Boettcher <pb@linuxtv.org> + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "cx24123.h" + +#define XTAL 10111000 + +static int force_band; +module_param(force_band, int, 0644); +MODULE_PARM_DESC(force_band, "Force a specific band select "\ + "(1-9, default:off)."); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +#define info(args...) do { printk(KERN_INFO "CX24123: " args); } while (0) +#define err(args...) do { printk(KERN_ERR "CX24123: " args); } while (0) + +#define dprintk(args...) \ + do { \ + if (debug) { \ + printk(KERN_DEBUG "CX24123: %s: ", __func__); \ + printk(args); \ + } \ + } while (0) + +struct cx24123_state { + struct i2c_adapter *i2c; + const struct cx24123_config *config; + + struct dvb_frontend frontend; + + /* Some PLL specifics for tuning */ + u32 VCAarg; + u32 VGAarg; + u32 bandselectarg; + u32 pllarg; + u32 FILTune; + + struct i2c_adapter tuner_i2c_adapter; + + u8 demod_rev; + + /* The Demod/Tuner can't easily provide these, we cache them */ + u32 currentfreq; + u32 currentsymbolrate; +}; + +/* Various tuner defaults need to be established for a given symbol rate Sps */ +static struct cx24123_AGC_val { + u32 symbolrate_low; + u32 symbolrate_high; + u32 VCAprogdata; + u32 VGAprogdata; + u32 FILTune; +} cx24123_AGC_vals[] = +{ + { + .symbolrate_low = 1000000, + .symbolrate_high = 4999999, + /* the specs recommend other values for VGA offsets, + but tests show they are wrong */ + .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, + .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x07, + .FILTune = 0x27f /* 0.41 V */ + }, + { + .symbolrate_low = 5000000, + .symbolrate_high = 14999999, + .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, + .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x1f, + .FILTune = 0x317 /* 0.90 V */ + }, + { + .symbolrate_low = 15000000, + .symbolrate_high = 45000000, + .VGAprogdata = (1 << 19) | (0x100 << 9) | 0x180, + .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x3f, + .FILTune = 0x145 /* 2.70 V */ + }, +}; + +/* + * Various tuner defaults need to be established for a given frequency kHz. + * fixme: The bounds on the bands do not match the doc in real life. + * fixme: Some of them have been moved, other might need adjustment. + */ +static struct cx24123_bandselect_val { + u32 freq_low; + u32 freq_high; + u32 VCOdivider; + u32 progdata; +} cx24123_bandselect_vals[] = +{ + /* band 1 */ + { + .freq_low = 950000, + .freq_high = 1074999, + .VCOdivider = 4, + .progdata = (0 << 19) | (0 << 9) | 0x40, + }, + + /* band 2 */ + { + .freq_low = 1075000, + .freq_high = 1177999, + .VCOdivider = 4, + .progdata = (0 << 19) | (0 << 9) | 0x80, + }, + + /* band 3 */ + { + .freq_low = 1178000, + .freq_high = 1295999, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x01, + }, + + /* band 4 */ + { + .freq_low = 1296000, + .freq_high = 1431999, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x02, + }, + + /* band 5 */ + { + .freq_low = 1432000, + .freq_high = 1575999, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x04, + }, + + /* band 6 */ + { + .freq_low = 1576000, + .freq_high = 1717999, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x08, + }, + + /* band 7 */ + { + .freq_low = 1718000, + .freq_high = 1855999, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x10, + }, + + /* band 8 */ + { + .freq_low = 1856000, + .freq_high = 2035999, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x20, + }, + + /* band 9 */ + { + .freq_low = 2036000, + .freq_high = 2150000, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x40, + }, +}; + +static struct { + u8 reg; + u8 data; +} cx24123_regdata[] = +{ + {0x00, 0x03}, /* Reset system */ + {0x00, 0x00}, /* Clear reset */ + {0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */ + {0x04, 0x10}, /* MPEG */ + {0x05, 0x04}, /* MPEG */ + {0x06, 0x31}, /* MPEG (default) */ + {0x0b, 0x00}, /* Freq search start point (default) */ + {0x0c, 0x00}, /* Demodulator sample gain (default) */ + {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */ + {0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */ + {0x0f, 0xfe}, /* FEC search mask (all supported codes) */ + {0x10, 0x01}, /* Default search inversion, no repeat (default) */ + {0x16, 0x00}, /* Enable reading of frequency */ + {0x17, 0x01}, /* Enable EsNO Ready Counter */ + {0x1c, 0x80}, /* Enable error counter */ + {0x20, 0x00}, /* Tuner burst clock rate = 500KHz */ + {0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */ + {0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */ + {0x29, 0x00}, /* DiSEqC LNB_DC off */ + {0x2a, 0xb0}, /* DiSEqC Parameters (default) */ + {0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */ + {0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ + {0x2d, 0x00}, + {0x2e, 0x00}, + {0x2f, 0x00}, + {0x30, 0x00}, + {0x31, 0x00}, + {0x32, 0x8c}, /* DiSEqC Parameters (default) */ + {0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */ + {0x34, 0x00}, + {0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ + {0x36, 0x02}, /* DiSEqC Parameters (default) */ + {0x37, 0x3a}, /* DiSEqC Parameters (default) */ + {0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */ + {0x44, 0x00}, /* Constellation (default) */ + {0x45, 0x00}, /* Symbol count (default) */ + {0x46, 0x0d}, /* Symbol rate estimator on (default) */ + {0x56, 0xc1}, /* Error Counter = Viterbi BER */ + {0x57, 0xff}, /* Error Counter Window (default) */ + {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */ + {0x67, 0x83}, /* Non-DCII symbol clock */ +}; + +static int cx24123_i2c_writereg(struct cx24123_state *state, + u8 i2c_addr, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 + }; + int err; + + /* printk(KERN_DEBUG "wr(%02x): %02x %02x\n", i2c_addr, reg, data); */ + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, err, reg, data); + return err; + } + + return 0; +} + +static int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg) +{ + int ret; + u8 b = 0; + struct i2c_msg msg[] = { + { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &b, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + err("%s: reg=0x%x (error=%d)\n", __func__, reg, ret); + return ret; + } + + /* printk(KERN_DEBUG "rd(%02x): %02x %02x\n", i2c_addr, reg, b); */ + + return b; +} + +#define cx24123_readreg(state, reg) \ + cx24123_i2c_readreg(state, state->config->demod_address, reg) +#define cx24123_writereg(state, reg, val) \ + cx24123_i2c_writereg(state, state->config->demod_address, reg, val) + +static int cx24123_set_inversion(struct cx24123_state *state, + enum fe_spectral_inversion inversion) +{ + u8 nom_reg = cx24123_readreg(state, 0x0e); + u8 auto_reg = cx24123_readreg(state, 0x10); + + switch (inversion) { + case INVERSION_OFF: + dprintk("inversion off\n"); + cx24123_writereg(state, 0x0e, nom_reg & ~0x80); + cx24123_writereg(state, 0x10, auto_reg | 0x80); + break; + case INVERSION_ON: + dprintk("inversion on\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x80); + cx24123_writereg(state, 0x10, auto_reg | 0x80); + break; + case INVERSION_AUTO: + dprintk("inversion auto\n"); + cx24123_writereg(state, 0x10, auto_reg & ~0x80); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cx24123_get_inversion(struct cx24123_state *state, + enum fe_spectral_inversion *inversion) +{ + u8 val; + + val = cx24123_readreg(state, 0x1b) >> 7; + + if (val == 0) { + dprintk("read inversion off\n"); + *inversion = INVERSION_OFF; + } else { + dprintk("read inversion on\n"); + *inversion = INVERSION_ON; + } + + return 0; +} + +static int cx24123_set_fec(struct cx24123_state *state, enum fe_code_rate fec) +{ + u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07; + + if (((int)fec < FEC_NONE) || (fec > FEC_AUTO)) + fec = FEC_AUTO; + + /* Set the soft decision threshold */ + if (fec == FEC_1_2) + cx24123_writereg(state, 0x43, + cx24123_readreg(state, 0x43) | 0x01); + else + cx24123_writereg(state, 0x43, + cx24123_readreg(state, 0x43) & ~0x01); + + switch (fec) { + case FEC_1_2: + dprintk("set FEC to 1/2\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x01); + cx24123_writereg(state, 0x0f, 0x02); + break; + case FEC_2_3: + dprintk("set FEC to 2/3\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x02); + cx24123_writereg(state, 0x0f, 0x04); + break; + case FEC_3_4: + dprintk("set FEC to 3/4\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x03); + cx24123_writereg(state, 0x0f, 0x08); + break; + case FEC_4_5: + dprintk("set FEC to 4/5\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x04); + cx24123_writereg(state, 0x0f, 0x10); + break; + case FEC_5_6: + dprintk("set FEC to 5/6\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x05); + cx24123_writereg(state, 0x0f, 0x20); + break; + case FEC_6_7: + dprintk("set FEC to 6/7\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x06); + cx24123_writereg(state, 0x0f, 0x40); + break; + case FEC_7_8: + dprintk("set FEC to 7/8\n"); + cx24123_writereg(state, 0x0e, nom_reg | 0x07); + cx24123_writereg(state, 0x0f, 0x80); + break; + case FEC_AUTO: + dprintk("set FEC to auto\n"); + cx24123_writereg(state, 0x0f, 0xfe); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int cx24123_get_fec(struct cx24123_state *state, enum fe_code_rate *fec) +{ + int ret; + + ret = cx24123_readreg(state, 0x1b); + if (ret < 0) + return ret; + ret = ret & 0x07; + + switch (ret) { + case 1: + *fec = FEC_1_2; + break; + case 2: + *fec = FEC_2_3; + break; + case 3: + *fec = FEC_3_4; + break; + case 4: + *fec = FEC_4_5; + break; + case 5: + *fec = FEC_5_6; + break; + case 6: + *fec = FEC_6_7; + break; + case 7: + *fec = FEC_7_8; + break; + default: + /* this can happen when there's no lock */ + *fec = FEC_NONE; + } + + return 0; +} + +/* Approximation of closest integer of log2(a/b). It actually gives the + lowest integer i such that 2^i >= round(a/b) */ +static u32 cx24123_int_log2(u32 a, u32 b) +{ + u32 exp, nearest = 0; + u32 div = a / b; + if (a % b >= b / 2) + ++div; + if (div < (1UL << 31)) { + for (exp = 1; div > exp; nearest++) + exp += exp; + } + return nearest; +} + +static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) +{ + u64 tmp; + u32 sample_rate, ratio, sample_gain; + u8 pll_mult; + + /* check if symbol rate is within limits */ + if ((srate > state->frontend.ops.info.symbol_rate_max) || + (srate < state->frontend.ops.info.symbol_rate_min)) + return -EOPNOTSUPP; + + /* choose the sampling rate high enough for the required operation, + while optimizing the power consumed by the demodulator */ + if (srate < (XTAL*2)/2) + pll_mult = 2; + else if (srate < (XTAL*3)/2) + pll_mult = 3; + else if (srate < (XTAL*4)/2) + pll_mult = 4; + else if (srate < (XTAL*5)/2) + pll_mult = 5; + else if (srate < (XTAL*6)/2) + pll_mult = 6; + else if (srate < (XTAL*7)/2) + pll_mult = 7; + else if (srate < (XTAL*8)/2) + pll_mult = 8; + else + pll_mult = 9; + + + sample_rate = pll_mult * XTAL; + + /* SYSSymbolRate[21:0] = (srate << 23) / sample_rate */ + + tmp = ((u64)srate) << 23; + do_div(tmp, sample_rate); + ratio = (u32) tmp; + + cx24123_writereg(state, 0x01, pll_mult * 6); + + cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f); + cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff); + cx24123_writereg(state, 0x0a, ratio & 0xff); + + /* also set the demodulator sample gain */ + sample_gain = cx24123_int_log2(sample_rate, srate); + tmp = cx24123_readreg(state, 0x0c) & ~0xe0; + cx24123_writereg(state, 0x0c, tmp | sample_gain << 5); + + dprintk("srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", + srate, ratio, sample_rate, sample_gain); + + return 0; +} + +/* + * Based on the required frequency and symbolrate, the tuner AGC has + * to be configured and the correct band selected. + * Calculate those values. + */ +static int cx24123_pll_calculate(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cx24123_state *state = fe->demodulator_priv; + u32 ndiv = 0, adiv = 0, vco_div = 0; + int i = 0; + int pump = 2; + int band = 0; + int num_bands = ARRAY_SIZE(cx24123_bandselect_vals); + struct cx24123_bandselect_val *bsv = NULL; + struct cx24123_AGC_val *agcv = NULL; + + /* Defaults for low freq, low rate */ + state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; + state->VGAarg = cx24123_AGC_vals[0].VGAprogdata; + state->bandselectarg = cx24123_bandselect_vals[0].progdata; + vco_div = cx24123_bandselect_vals[0].VCOdivider; + + /* For the given symbol rate, determine the VCA, VGA and + * FILTUNE programming bits */ + for (i = 0; i < ARRAY_SIZE(cx24123_AGC_vals); i++) { + agcv = &cx24123_AGC_vals[i]; + if ((agcv->symbolrate_low <= p->symbol_rate) && + (agcv->symbolrate_high >= p->symbol_rate)) { + state->VCAarg = agcv->VCAprogdata; + state->VGAarg = agcv->VGAprogdata; + state->FILTune = agcv->FILTune; + } + } + + /* determine the band to use */ + if (force_band < 1 || force_band > num_bands) { + for (i = 0; i < num_bands; i++) { + bsv = &cx24123_bandselect_vals[i]; + if ((bsv->freq_low <= p->frequency) && + (bsv->freq_high >= p->frequency)) + band = i; + } + } else + band = force_band - 1; + + state->bandselectarg = cx24123_bandselect_vals[band].progdata; + vco_div = cx24123_bandselect_vals[band].VCOdivider; + + /* determine the charge pump current */ + if (p->frequency < (cx24123_bandselect_vals[band].freq_low + + cx24123_bandselect_vals[band].freq_high) / 2) + pump = 0x01; + else + pump = 0x02; + + /* Determine the N/A dividers for the requested lband freq (in kHz). */ + /* Note: the reference divider R=10, frequency is in KHz, + * XTAL is in Hz */ + ndiv = (((p->frequency * vco_div * 10) / + (2 * XTAL / 1000)) / 32) & 0x1ff; + adiv = (((p->frequency * vco_div * 10) / + (2 * XTAL / 1000)) % 32) & 0x1f; + + if (adiv == 0 && ndiv > 0) + ndiv--; + + /* control bits 11, refdiv 11, charge pump polarity 1, + * charge pump current, ndiv, adiv */ + state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | + (pump << 14) | (ndiv << 5) | adiv; + + return 0; +} + +/* + * Tuner data is 21 bits long, must be left-aligned in data. + * Tuner cx24109 is written through a dedicated 3wire interface + * on the demod chip. + */ +static int cx24123_pll_writereg(struct dvb_frontend *fe, u32 data) +{ + struct cx24123_state *state = fe->demodulator_priv; + unsigned long timeout; + + dprintk("pll writereg called, data=0x%08x\n", data); + + /* align the 21 bytes into to bit23 boundary */ + data = data << 3; + + /* Reset the demod pll word length to 0x15 bits */ + cx24123_writereg(state, 0x21, 0x15); + + /* write the msb 8 bits, wait for the send to be completed */ + timeout = jiffies + msecs_to_jiffies(40); + cx24123_writereg(state, 0x22, (data >> 16) & 0xff); + while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { + if (time_after(jiffies, timeout)) { + err("%s: demodulator is not responding, "\ + "possibly hung, aborting.\n", __func__); + return -EREMOTEIO; + } + msleep(10); + } + + /* send another 8 bytes, wait for the send to be completed */ + timeout = jiffies + msecs_to_jiffies(40); + cx24123_writereg(state, 0x22, (data >> 8) & 0xff); + while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { + if (time_after(jiffies, timeout)) { + err("%s: demodulator is not responding, "\ + "possibly hung, aborting.\n", __func__); + return -EREMOTEIO; + } + msleep(10); + } + + /* send the lower 5 bits of this byte, padded with 3 LBB, + * wait for the send to be completed */ + timeout = jiffies + msecs_to_jiffies(40); + cx24123_writereg(state, 0x22, (data) & 0xff); + while ((cx24123_readreg(state, 0x20) & 0x80)) { + if (time_after(jiffies, timeout)) { + err("%s: demodulator is not responding," \ + "possibly hung, aborting.\n", __func__); + return -EREMOTEIO; + } + msleep(10); + } + + /* Trigger the demod to configure the tuner */ + cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) | 2); + cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) & 0xfd); + + return 0; +} + +static int cx24123_pll_tune(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cx24123_state *state = fe->demodulator_priv; + u8 val; + + dprintk("frequency=%i\n", p->frequency); + + if (cx24123_pll_calculate(fe) != 0) { + err("%s: cx24123_pll_calculate failed\n", __func__); + return -EINVAL; + } + + /* Write the new VCO/VGA */ + cx24123_pll_writereg(fe, state->VCAarg); + cx24123_pll_writereg(fe, state->VGAarg); + + /* Write the new bandselect and pll args */ + cx24123_pll_writereg(fe, state->bandselectarg); + cx24123_pll_writereg(fe, state->pllarg); + + /* set the FILTUNE voltage */ + val = cx24123_readreg(state, 0x28) & ~0x3; + cx24123_writereg(state, 0x27, state->FILTune >> 2); + cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3)); + + dprintk("pll tune VCA=%d, band=%d, pll=%d\n", state->VCAarg, + state->bandselectarg, state->pllarg); + + return 0; +} + + +/* + * 0x23: + * [7:7] = BTI enabled + * [6:6] = I2C repeater enabled + * [5:5] = I2C repeater start + * [0:0] = BTI start + */ + +/* mode == 1 -> i2c-repeater, 0 -> bti */ +static int cx24123_repeater_mode(struct cx24123_state *state, u8 mode, u8 start) +{ + u8 r = cx24123_readreg(state, 0x23) & 0x1e; + if (mode) + r |= (1 << 6) | (start << 5); + else + r |= (1 << 7) | (start); + return cx24123_writereg(state, 0x23, r); +} + +static int cx24123_initfe(struct dvb_frontend *fe) +{ + struct cx24123_state *state = fe->demodulator_priv; + int i; + + dprintk("init frontend\n"); + + /* Configure the demod to a good set of defaults */ + for (i = 0; i < ARRAY_SIZE(cx24123_regdata); i++) + cx24123_writereg(state, cx24123_regdata[i].reg, + cx24123_regdata[i].data); + + /* Set the LNB polarity */ + if (state->config->lnb_polarity) + cx24123_writereg(state, 0x32, + cx24123_readreg(state, 0x32) | 0x02); + + if (state->config->dont_use_pll) + cx24123_repeater_mode(state, 1, 0); + + return 0; +} + +static int cx24123_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct cx24123_state *state = fe->demodulator_priv; + u8 val; + + val = cx24123_readreg(state, 0x29) & ~0x40; + + switch (voltage) { + case SEC_VOLTAGE_13: + dprintk("setting voltage 13V\n"); + return cx24123_writereg(state, 0x29, val & 0x7f); + case SEC_VOLTAGE_18: + dprintk("setting voltage 18V\n"); + return cx24123_writereg(state, 0x29, val | 0x80); + case SEC_VOLTAGE_OFF: + /* already handled in cx88-dvb */ + return 0; + default: + return -EINVAL; + } + + return 0; +} + +/* wait for diseqc queue to become ready (or timeout) */ +static void cx24123_wait_for_diseqc(struct cx24123_state *state) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(200); + while (!(cx24123_readreg(state, 0x29) & 0x40)) { + if (time_after(jiffies, timeout)) { + err("%s: diseqc queue not ready, " \ + "command may be lost.\n", __func__); + break; + } + msleep(10); + } +} + +static int cx24123_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct cx24123_state *state = fe->demodulator_priv; + int i, val, tone; + + dprintk("\n"); + + /* stop continuous tone if enabled */ + tone = cx24123_readreg(state, 0x29); + if (tone & 0x10) + cx24123_writereg(state, 0x29, tone & ~0x50); + + /* wait for diseqc queue ready */ + cx24123_wait_for_diseqc(state); + + /* select tone mode */ + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); + + for (i = 0; i < cmd->msg_len; i++) + cx24123_writereg(state, 0x2C + i, cmd->msg[i]); + + val = cx24123_readreg(state, 0x29); + cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | + ((cmd->msg_len-3) & 3)); + + /* wait for diseqc message to finish sending */ + cx24123_wait_for_diseqc(state); + + /* restart continuous tone if enabled */ + if (tone & 0x10) + cx24123_writereg(state, 0x29, tone & ~0x40); + + return 0; +} + +static int cx24123_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct cx24123_state *state = fe->demodulator_priv; + int val, tone; + + dprintk("\n"); + + /* stop continuous tone if enabled */ + tone = cx24123_readreg(state, 0x29); + if (tone & 0x10) + cx24123_writereg(state, 0x29, tone & ~0x50); + + /* wait for diseqc queue ready */ + cx24123_wait_for_diseqc(state); + + /* select tone mode */ + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) | 0x4); + msleep(30); + val = cx24123_readreg(state, 0x29); + if (burst == SEC_MINI_A) + cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00)); + else if (burst == SEC_MINI_B) + cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08)); + else + return -EINVAL; + + cx24123_wait_for_diseqc(state); + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); + + /* restart continuous tone if enabled */ + if (tone & 0x10) + cx24123_writereg(state, 0x29, tone & ~0x40); + + return 0; +} + +static int cx24123_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx24123_state *state = fe->demodulator_priv; + int sync = cx24123_readreg(state, 0x14); + + *status = 0; + if (state->config->dont_use_pll) { + u32 tun_status = 0; + if (fe->ops.tuner_ops.get_status) + fe->ops.tuner_ops.get_status(fe, &tun_status); + if (tun_status & TUNER_STATUS_LOCKED) + *status |= FE_HAS_SIGNAL; + } else { + int lock = cx24123_readreg(state, 0x20); + if (lock & 0x01) + *status |= FE_HAS_SIGNAL; + } + + if (sync & 0x02) + *status |= FE_HAS_CARRIER; /* Phase locked */ + if (sync & 0x04) + *status |= FE_HAS_VITERBI; + + /* Reed-Solomon Status */ + if (sync & 0x08) + *status |= FE_HAS_SYNC; + if (sync & 0x80) + *status |= FE_HAS_LOCK; /*Full Sync */ + + return 0; +} + +/* + * Configured to return the measurement of errors in blocks, + * because no UCBLOCKS value is available, so this value doubles up + * to satisfy both measurements. + */ +static int cx24123_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cx24123_state *state = fe->demodulator_priv; + + /* The true bit error rate is this value divided by + the window size (set as 256 * 255) */ + *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | + (cx24123_readreg(state, 0x1d) << 8 | + cx24123_readreg(state, 0x1e)); + + dprintk("BER = %d\n", *ber); + + return 0; +} + +static int cx24123_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct cx24123_state *state = fe->demodulator_priv; + + /* larger = better */ + *signal_strength = cx24123_readreg(state, 0x3b) << 8; + + dprintk("Signal strength = %d\n", *signal_strength); + + return 0; +} + +static int cx24123_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct cx24123_state *state = fe->demodulator_priv; + + /* Inverted raw Es/N0 count, totally bogus but better than the + BER threshold. */ + *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) | + (u16)cx24123_readreg(state, 0x19)); + + dprintk("read S/N index = %d\n", *snr); + + return 0; +} + +static int cx24123_set_frontend(struct dvb_frontend *fe) +{ + struct cx24123_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dprintk("\n"); + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + state->currentfreq = p->frequency; + state->currentsymbolrate = p->symbol_rate; + + cx24123_set_inversion(state, p->inversion); + cx24123_set_fec(state, p->fec_inner); + cx24123_set_symbolrate(state, p->symbol_rate); + + if (!state->config->dont_use_pll) + cx24123_pll_tune(fe); + else if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + else + err("it seems I don't have a tuner..."); + + /* Enable automatic acquisition and reset cycle */ + cx24123_writereg(state, 0x03, (cx24123_readreg(state, 0x03) | 0x07)); + cx24123_writereg(state, 0x00, 0x10); + cx24123_writereg(state, 0x00, 0); + + if (state->config->agc_callback) + state->config->agc_callback(fe); + + return 0; +} + +static int cx24123_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct cx24123_state *state = fe->demodulator_priv; + + dprintk("\n"); + + if (cx24123_get_inversion(state, &p->inversion) != 0) { + err("%s: Failed to get inversion status\n", __func__); + return -EREMOTEIO; + } + if (cx24123_get_fec(state, &p->fec_inner) != 0) { + err("%s: Failed to get fec status\n", __func__); + return -EREMOTEIO; + } + p->frequency = state->currentfreq; + p->symbol_rate = state->currentsymbolrate; + + return 0; +} + +static int cx24123_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct cx24123_state *state = fe->demodulator_priv; + u8 val; + + /* wait for diseqc queue ready */ + cx24123_wait_for_diseqc(state); + + val = cx24123_readreg(state, 0x29) & ~0x40; + + switch (tone) { + case SEC_TONE_ON: + dprintk("setting tone on\n"); + return cx24123_writereg(state, 0x29, val | 0x10); + case SEC_TONE_OFF: + dprintk("setting tone off\n"); + return cx24123_writereg(state, 0x29, val & 0xef); + default: + err("CASE reached default with tone=%d\n", tone); + return -EINVAL; + } + + return 0; +} + +static int cx24123_tune(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int retval = 0; + + if (re_tune) + retval = cx24123_set_frontend(fe); + + if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) + cx24123_read_status(fe, status); + *delay = HZ/10; + + return retval; +} + +static enum dvbfe_algo cx24123_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static void cx24123_release(struct dvb_frontend *fe) +{ + struct cx24123_state *state = fe->demodulator_priv; + dprintk("\n"); + i2c_del_adapter(&state->tuner_i2c_adapter); + kfree(state); +} + +static int cx24123_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct cx24123_state *state = i2c_get_adapdata(i2c_adap); + /* this repeater closes after the first stop */ + cx24123_repeater_mode(state, 1, 1); + return i2c_transfer(state->i2c, msg, num); +} + +static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm cx24123_tuner_i2c_algo = { + .master_xfer = cx24123_tuner_i2c_tuner_xfer, + .functionality = cx24123_tuner_i2c_func, +}; + +struct i2c_adapter * + cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + struct cx24123_state *state = fe->demodulator_priv; + return &state->tuner_i2c_adapter; +} +EXPORT_SYMBOL(cx24123_get_tuner_i2c_adapter); + +static const struct dvb_frontend_ops cx24123_ops; + +struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, + struct i2c_adapter *i2c) +{ + /* allocate memory for the internal state */ + struct cx24123_state *state = + kzalloc(sizeof(struct cx24123_state), GFP_KERNEL); + + dprintk("\n"); + if (state == NULL) { + err("Unable to kzalloc\n"); + goto error; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + state->demod_rev = cx24123_readreg(state, 0x00); + switch (state->demod_rev) { + case 0xe1: + info("detected CX24123C\n"); + break; + case 0xd1: + info("detected CX24123\n"); + break; + default: + err("wrong demod revision: %x\n", state->demod_rev); + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx24123_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* create tuner i2c adapter */ + if (config->dont_use_pll) + cx24123_repeater_mode(state, 1, 0); + + strscpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus", + sizeof(state->tuner_i2c_adapter.name)); + state->tuner_i2c_adapter.algo = &cx24123_tuner_i2c_algo; + state->tuner_i2c_adapter.algo_data = NULL; + state->tuner_i2c_adapter.dev.parent = i2c->dev.parent; + i2c_set_adapdata(&state->tuner_i2c_adapter, state); + if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { + err("tuner i2c bus could not be initialized\n"); + goto error; + } + + return &state->frontend; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL_GPL(cx24123_attach); + +static const struct dvb_frontend_ops cx24123_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Conexant CX24123/CX24109", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24123_release, + + .init = cx24123_initfe, + .set_frontend = cx24123_set_frontend, + .get_frontend = cx24123_get_frontend, + .read_status = cx24123_read_status, + .read_ber = cx24123_read_ber, + .read_signal_strength = cx24123_read_signal_strength, + .read_snr = cx24123_read_snr, + .diseqc_send_master_cmd = cx24123_send_diseqc_msg, + .diseqc_send_burst = cx24123_diseqc_send_burst, + .set_tone = cx24123_set_tone, + .set_voltage = cx24123_set_voltage, + .tune = cx24123_tune, + .get_frontend_algo = cx24123_get_algo, +}; + +MODULE_DESCRIPTION("DVB Frontend module for Conexant " \ + "CX24123/CX24109/CX24113 hardware"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/dvb-frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h new file mode 100644 index 0000000000..dfda44efa4 --- /dev/null +++ b/drivers/media/dvb-frontends/cx24123.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver + + Copyright (C) 2005 Steven Toth <stoth@linuxtv.org> + +*/ + +#ifndef CX24123_H +#define CX24123_H + +#include <linux/dvb/frontend.h> + +struct cx24123_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* Need to set device param for start_dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + + /* 0 = LNB voltage normal, 1 = LNB voltage inverted */ + int lnb_polarity; + + /* this device has another tuner */ + u8 dont_use_pll; + void (*agc_callback) (struct dvb_frontend *); +}; + +#if IS_REACHABLE(CONFIG_DVB_CX24123) +extern struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, + struct i2c_adapter *i2c); +extern struct i2c_adapter *cx24123_get_tuner_i2c_adapter(struct dvb_frontend *); +#else +static inline struct dvb_frontend *cx24123_attach( + const struct cx24123_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct i2c_adapter * + cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* CX24123_H */ diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c new file mode 100644 index 0000000000..3f3b857436 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2099.c: Driver for the Sony CXD2099AR Common Interface Controller + * + * Copyright (C) 2010-2013 Digital Devices GmbH + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/io.h> + +#include "cxd2099.h" + +static int buffermode; +module_param(buffermode, int, 0444); +MODULE_PARM_DESC(buffermode, "Enable CXD2099AR buffer mode (default: disabled)"); + +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount); + +struct cxd { + struct dvb_ca_en50221 en; + + struct cxd2099_cfg cfg; + struct i2c_client *client; + struct regmap *regmap; + + u8 regs[0x23]; + u8 lastaddress; + u8 clk_reg_f; + u8 clk_reg_b; + int mode; + int ready; + int dr; + int write_busy; + int slot_stat; + + u8 amem[1024]; + int amem_read; + + int cammode; + struct mutex lock; /* device access lock */ + + u8 rbuf[1028]; + u8 wbuf[1028]; +}; + +static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n) +{ + int status = 0; + + if (ci->lastaddress != adr) + status = regmap_write(ci->regmap, 0, adr); + if (!status) { + ci->lastaddress = adr; + + while (n) { + int len = n; + + if (ci->cfg.max_i2c && len > ci->cfg.max_i2c) + len = ci->cfg.max_i2c; + status = regmap_raw_read(ci->regmap, 1, data, len); + if (status) + return status; + data += len; + n -= len; + } + } + return status; +} + +static int read_reg(struct cxd *ci, u8 reg, u8 *val) +{ + return read_block(ci, reg, val, 1); +} + +static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) + status = regmap_raw_read(ci->regmap, 3, data, n); + return status; +} + +static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) { + u8 buf[256]; + + memcpy(buf, data, n); + status = regmap_raw_write(ci->regmap, 3, buf, n); + } + return status; +} + +static int read_io(struct cxd *ci, u16 address, unsigned int *val) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) + status = regmap_read(ci->regmap, 3, val); + return status; +} + +static int write_io(struct cxd *ci, u16 address, u8 val) +{ + int status; + u8 addr[2] = {address & 0xff, address >> 8}; + + status = regmap_raw_write(ci->regmap, 2, addr, 2); + if (!status) + status = regmap_write(ci->regmap, 3, val); + return status; +} + +static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) +{ + int status = 0; + unsigned int regval; + + if (ci->lastaddress != reg) + status = regmap_write(ci->regmap, 0, reg); + if (!status && reg >= 6 && reg <= 8 && mask != 0xff) { + status = regmap_read(ci->regmap, 1, ®val); + ci->regs[reg] = regval; + } + ci->lastaddress = reg; + ci->regs[reg] = (ci->regs[reg] & (~mask)) | val; + if (!status) + status = regmap_write(ci->regmap, 1, ci->regs[reg]); + if (reg == 0x20) + ci->regs[reg] &= 0x7f; + return status; +} + +static int write_reg(struct cxd *ci, u8 reg, u8 val) +{ + return write_regm(ci, reg, val, 0xff); +} + +static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n) +{ + int status = 0; + u8 *buf = ci->wbuf; + + if (ci->lastaddress != adr) + status = regmap_write(ci->regmap, 0, adr); + if (status) + return status; + + ci->lastaddress = adr; + while (n) { + int len = n; + + if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c)) + len = ci->cfg.max_i2c - 1; + memcpy(buf, data, len); + status = regmap_raw_write(ci->regmap, 1, buf, len); + if (status) + return status; + n -= len; + data += len; + } + return status; +} + +static void set_mode(struct cxd *ci, int mode) +{ + if (mode == ci->mode) + return; + + switch (mode) { + case 0x00: /* IO mem */ + write_regm(ci, 0x06, 0x00, 0x07); + break; + case 0x01: /* ATT mem */ + write_regm(ci, 0x06, 0x02, 0x07); + break; + default: + break; + } + ci->mode = mode; +} + +static void cam_mode(struct cxd *ci, int mode) +{ + u8 dummy; + + if (mode == ci->cammode) + return; + + switch (mode) { + case 0x00: + write_regm(ci, 0x20, 0x80, 0x80); + break; + case 0x01: + if (!ci->en.read_data) + return; + ci->write_busy = 0; + dev_info(&ci->client->dev, "enable cam buffer mode\n"); + write_reg(ci, 0x0d, 0x00); + write_reg(ci, 0x0e, 0x01); + write_regm(ci, 0x08, 0x40, 0x40); + read_reg(ci, 0x12, &dummy); + write_regm(ci, 0x08, 0x80, 0x80); + break; + default: + break; + } + ci->cammode = mode; +} + +static int init(struct cxd *ci) +{ + int status; + + mutex_lock(&ci->lock); + ci->mode = -1; + do { + status = write_reg(ci, 0x00, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x01, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x02, 0x10); + if (status < 0) + break; + status = write_reg(ci, 0x03, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x05, 0xFF); + if (status < 0) + break; + status = write_reg(ci, 0x06, 0x1F); + if (status < 0) + break; + status = write_reg(ci, 0x07, 0x1F); + if (status < 0) + break; + status = write_reg(ci, 0x08, 0x28); + if (status < 0) + break; + status = write_reg(ci, 0x14, 0x20); + if (status < 0) + break; + + /* TOSTRT = 8, Mode B (gated clock), falling Edge, + * Serial, POL=HIGH, MSB + */ + status = write_reg(ci, 0x0A, 0xA7); + if (status < 0) + break; + + status = write_reg(ci, 0x0B, 0x33); + if (status < 0) + break; + status = write_reg(ci, 0x0C, 0x33); + if (status < 0) + break; + + status = write_regm(ci, 0x14, 0x00, 0x0F); + if (status < 0) + break; + status = write_reg(ci, 0x15, ci->clk_reg_b); + if (status < 0) + break; + status = write_regm(ci, 0x16, 0x00, 0x0F); + if (status < 0) + break; + status = write_reg(ci, 0x17, ci->clk_reg_f); + if (status < 0) + break; + + if (ci->cfg.clock_mode == 2) { + /* bitrate*2^13/ 72000 */ + u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000; + + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x6f); + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x6d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x08); + if (status < 0) + break; + status = write_reg(ci, 0x21, (reg >> 8) & 0xff); + if (status < 0) + break; + status = write_reg(ci, 0x22, reg & 0xff); + if (status < 0) + break; + } else if (ci->cfg.clock_mode == 1) { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x6f); /* D */ + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x6d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x68); + if (status < 0) + break; + status = write_reg(ci, 0x21, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x22, 0x02); + if (status < 0) + break; + } else { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x4f); /* C */ + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x4d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x28); + if (status < 0) + break; + status = write_reg(ci, 0x21, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x22, 0x07); + if (status < 0) + break; + } + + status = write_regm(ci, 0x20, 0x80, 0x80); + if (status < 0) + break; + status = write_regm(ci, 0x03, 0x02, 0x02); + if (status < 0) + break; + status = write_reg(ci, 0x01, 0x04); + if (status < 0) + break; + status = write_reg(ci, 0x00, 0x31); + if (status < 0) + break; + + /* Put TS in bypass */ + status = write_regm(ci, 0x09, 0x08, 0x08); + if (status < 0) + break; + ci->cammode = -1; + cam_mode(ci, 0); + } while (0); + mutex_unlock(&ci->lock); + + return 0; +} + +static int read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + struct cxd *ci = ca->data; + u8 val; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + read_pccard(ci, address, &val, 1); + mutex_unlock(&ci->lock); + return val; +} + +static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, + int address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + write_pccard(ci, address, &value, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int read_cam_control(struct dvb_ca_en50221 *ca, + int slot, u8 address) +{ + struct cxd *ci = ca->data; + unsigned int val; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + read_io(ci, address, &val); + mutex_unlock(&ci->lock); + return val; +} + +static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, + u8 address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + write_io(ci, address, value); + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + if (ci->cammode) + read_data(ca, slot, ci->rbuf, 0); + + mutex_lock(&ci->lock); + cam_mode(ci, 0); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); + write_regm(ci, 0x20, 0x80, 0x80); + write_reg(ci, 0x03, 0x02); + ci->ready = 0; + ci->mode = -1; + { + int i; + + for (i = 0; i < 100; i++) { + usleep_range(10000, 11000); + if (ci->ready) + break; + } + } + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + dev_dbg(&ci->client->dev, "%s\n", __func__); + if (ci->cammode) + read_data(ca, slot, ci->rbuf, 0); + mutex_lock(&ci->lock); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + msleep(300); + + write_regm(ci, 0x09, 0x08, 0x08); + write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ + write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */ + + ci->mode = -1; + ci->write_busy = 0; + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x00, 0x08); + set_mode(ci, 0); + cam_mode(ci, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int campoll(struct cxd *ci) +{ + u8 istat; + + read_reg(ci, 0x04, &istat); + if (!istat) + return 0; + write_reg(ci, 0x05, istat); + + if (istat & 0x40) + ci->dr = 1; + if (istat & 0x20) + ci->write_busy = 0; + + if (istat & 2) { + u8 slotstat; + + read_reg(ci, 0x01, &slotstat); + if (!(2 & slotstat)) { + if (!ci->slot_stat) { + ci->slot_stat |= + DVB_CA_EN50221_POLL_CAM_PRESENT; + write_regm(ci, 0x03, 0x08, 0x08); + } + + } else { + if (ci->slot_stat) { + ci->slot_stat = 0; + write_regm(ci, 0x03, 0x00, 0x08); + dev_info(&ci->client->dev, "NO CAM\n"); + ci->ready = 0; + } + } + if ((istat & 8) && + ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + ci->ready = 1; + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; + } + } + return 0; +} + +static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct cxd *ci = ca->data; + u8 slotstat; + + mutex_lock(&ci->lock); + campoll(ci); + read_reg(ci, 0x01, &slotstat); + mutex_unlock(&ci->lock); + + return ci->slot_stat; +} + +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + u8 msb, lsb; + u16 len; + + mutex_lock(&ci->lock); + campoll(ci); + mutex_unlock(&ci->lock); + + if (!ci->dr) + return 0; + + mutex_lock(&ci->lock); + read_reg(ci, 0x0f, &msb); + read_reg(ci, 0x10, &lsb); + len = ((u16)msb << 8) | lsb; + if (len > ecount || len < 2) { + /* read it anyway or cxd may hang */ + read_block(ci, 0x12, ci->rbuf, len); + mutex_unlock(&ci->lock); + return -EIO; + } + read_block(ci, 0x12, ebuf, len); + ci->dr = 0; + mutex_unlock(&ci->lock); + return len; +} + +static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + + if (ci->write_busy) + return -EAGAIN; + mutex_lock(&ci->lock); + write_reg(ci, 0x0d, ecount >> 8); + write_reg(ci, 0x0e, ecount & 0xff); + write_block(ci, 0x11, ebuf, ecount); + ci->write_busy = 1; + mutex_unlock(&ci->lock); + return ecount; +} + +static const struct dvb_ca_en50221 en_templ = { + .read_attribute_mem = read_attribute_mem, + .write_attribute_mem = write_attribute_mem, + .read_cam_control = read_cam_control, + .write_cam_control = write_cam_control, + .slot_reset = slot_reset, + .slot_shutdown = slot_shutdown, + .slot_ts_enable = slot_ts_enable, + .poll_slot_status = poll_slot_status, + .read_data = read_data, + .write_data = write_data, +}; + +static int cxd2099_probe(struct i2c_client *client) +{ + struct cxd *ci; + struct cxd2099_cfg *cfg = client->dev.platform_data; + static const struct regmap_config rm_cfg = { + .reg_bits = 8, + .val_bits = 8, + }; + unsigned int val; + int ret; + + ci = kzalloc(sizeof(*ci), GFP_KERNEL); + if (!ci) { + ret = -ENOMEM; + goto err; + } + + ci->client = client; + memcpy(&ci->cfg, cfg, sizeof(ci->cfg)); + + ci->regmap = regmap_init_i2c(client, &rm_cfg); + if (IS_ERR(ci->regmap)) { + ret = PTR_ERR(ci->regmap); + goto err_kfree; + } + + ret = regmap_read(ci->regmap, 0x00, &val); + if (ret < 0) { + dev_info(&client->dev, "No CXD2099AR detected at 0x%02x\n", + client->addr); + goto err_rmexit; + } + + mutex_init(&ci->lock); + ci->lastaddress = 0xff; + ci->clk_reg_b = 0x4a; + ci->clk_reg_f = 0x1b; + + ci->en = en_templ; + ci->en.data = ci; + init(ci); + dev_info(&client->dev, "Attached CXD2099AR at 0x%02x\n", client->addr); + + *cfg->en = &ci->en; + + if (!buffermode) { + ci->en.read_data = NULL; + ci->en.write_data = NULL; + } else { + dev_info(&client->dev, "Using CXD2099AR buffer mode"); + } + + i2c_set_clientdata(client, ci); + + return 0; + +err_rmexit: + regmap_exit(ci->regmap); +err_kfree: + kfree(ci); +err: + + return ret; +} + +static void cxd2099_remove(struct i2c_client *client) +{ + struct cxd *ci = i2c_get_clientdata(client); + + regmap_exit(ci->regmap); + kfree(ci); +} + +static const struct i2c_device_id cxd2099_id[] = { + {"cxd2099", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cxd2099_id); + +static struct i2c_driver cxd2099_driver = { + .driver = { + .name = "cxd2099", + }, + .probe = cxd2099_probe, + .remove = cxd2099_remove, + .id_table = cxd2099_id, +}; + +module_i2c_driver(cxd2099_driver); + +MODULE_DESCRIPTION("Sony CXD2099AR Common Interface controller driver"); +MODULE_AUTHOR("Ralph Metzler"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/cxd2099.h b/drivers/media/dvb-frontends/cxd2099.h new file mode 100644 index 0000000000..5d4060007c --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2099.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2099.h: Driver for the Sony CXD2099AR Common Interface Controller + * + * Copyright (C) 2010-2011 Digital Devices GmbH + */ + +#ifndef _CXD2099_H_ +#define _CXD2099_H_ + +#include <media/dvb_ca_en50221.h> + +struct cxd2099_cfg { + u32 bitrate; + u8 polarity; + u8 clock_mode; + + u32 max_i2c; + + /* ptr to DVB CA struct */ + struct dvb_ca_en50221 **en; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h new file mode 100644 index 0000000000..4aa6cf4fb9 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2820r.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Sony CXD2820R demodulator driver + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + */ + + +#ifndef CXD2820R_H +#define CXD2820R_H + +#include <linux/dvb/frontend.h> + +#define CXD2820R_GPIO_D (0 << 0) /* disable */ +#define CXD2820R_GPIO_E (1 << 0) /* enable */ +#define CXD2820R_GPIO_O (0 << 1) /* output */ +#define CXD2820R_GPIO_I (1 << 1) /* input */ +#define CXD2820R_GPIO_L (0 << 2) /* output low */ +#define CXD2820R_GPIO_H (1 << 2) /* output high */ + +#define CXD2820R_TS_SERIAL 0x08 +#define CXD2820R_TS_SERIAL_MSB 0x28 +#define CXD2820R_TS_PARALLEL 0x30 +#define CXD2820R_TS_PARALLEL_MSB 0x70 + +/* + * I2C address: 0x6c, 0x6d + */ + +/** + * struct cxd2820r_platform_data - Platform data for the cxd2820r driver + * @ts_mode: TS mode. + * @ts_clk_inv: TS clock inverted. + * @if_agc_polarity: IF AGC polarity. + * @spec_inv: Input spectrum inverted. + * @gpio_chip_base: GPIO. + * @get_dvb_frontend: Get DVB frontend. + */ +struct cxd2820r_platform_data { + u8 ts_mode; + bool ts_clk_inv; + bool if_agc_polarity; + bool spec_inv; + int **gpio_chip_base; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); +/* private: For legacy media attach wrapper. Do not set value. */ + bool attach_in_use; +}; + +/** + * struct cxd2820r_config - configuration for cxd2020r demod + * + * @i2c_address: Demodulator I2C address. Driver determines DVB-C slave I2C + * address automatically from master address. + * Default: none, must set. Values: 0x6c, 0x6d. + * @ts_mode: TS output mode. Default: none, must set. Values: FIXME? + * @ts_clock_inv: TS clock inverted. Default: 0. Values: 0, 1. + * @if_agc_polarity: Default: 0. Values: 0, 1 + * @spec_inv: Spectrum inversion. Default: 0. Values: 0, 1. + */ +struct cxd2820r_config { + /* Demodulator I2C address. + * Driver determines DVB-C slave I2C address automatically from master + * address. + * Default: none, must set + * Values: 0x6c, 0x6d + */ + u8 i2c_address; + + /* TS output mode. + * Default: none, must set. + * Values: + */ + u8 ts_mode; + + /* TS clock inverted. + * Default: 0 + * Values: 0, 1 + */ + bool ts_clock_inv; + + /* IF AGC polarity. + * Default: 0 + * Values: 0, 1 + */ + bool if_agc_polarity; + + /* Spectrum inversion. + * Default: 0 + * Values: 0, 1 + */ + bool spec_inv; +}; + + +#if IS_REACHABLE(CONFIG_DVB_CXD2820R) +/** + * cxd2820r_attach - Attach a cxd2820r demod + * + * @config: pointer to &struct cxd2820r_config with demod configuration. + * @i2c: i2c adapter to use. + * @gpio_chip_base: if zero, disables GPIO setting. Otherwise, if + * CONFIG_GPIOLIB is set dynamically allocate + * gpio base; if is not set, use its value to + * setup the GPIO pins. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *cxd2820r_attach( + const struct cxd2820r_config *config, + struct i2c_adapter *i2c, + int *gpio_chip_base +); +#else +static inline struct dvb_frontend *cxd2820r_attach( + const struct cxd2820r_config *config, + struct i2c_adapter *i2c, + int *gpio_chip_base +) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif + +#endif /* CXD2820R_H */ diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c new file mode 100644 index 0000000000..0ba382948c --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2820r_c.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sony CXD2820R demodulator driver + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + */ + + +#include "cxd2820r_priv.h" + +int cxd2820r_set_frontend_c(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + u8 buf[2]; + u32 if_frequency; + struct reg_val_mask tab[] = { + { 0x00080, 0x01, 0xff }, + { 0x00081, 0x05, 0xff }, + { 0x00085, 0x07, 0xff }, + { 0x00088, 0x01, 0xff }, + + { 0x00082, 0x20, 0x60 }, + { 0x1016a, 0x48, 0xff }, + { 0x100a5, 0x00, 0x01 }, + { 0x10020, 0x06, 0x07 }, + { 0x10059, 0x50, 0xff }, + { 0x10087, 0x0c, 0x3c }, + { 0x1008b, 0x07, 0xff }, + { 0x1001f, priv->if_agc_polarity << 7, 0x80 }, + { 0x10070, priv->ts_mode, 0xff }, + { 0x10071, !priv->ts_clk_inv << 4, 0x10 }, + }; + + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%u inversion=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->symbol_rate, c->inversion); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + if (priv->delivery_system != SYS_DVBC_ANNEX_A) { + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; + } + + priv->delivery_system = SYS_DVBC_ANNEX_A; + priv->ber_running = false; /* tune stops BER counter */ + + /* program IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto error; + dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto error; + } + + utmp = 0x4000 - DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x4000, CXD2820R_CLK); + buf[0] = (utmp >> 8) & 0xff; + buf[1] = (utmp >> 0) & 0xff; + ret = regmap_bulk_write(priv->regmap[1], 0x0042, buf, 2); + if (ret) + goto error; + + ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); + if (ret) + goto error; + + ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_get_frontend_c(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + unsigned int utmp; + u8 buf[2]; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(priv->regmap[1], 0x001a, buf, 2); + if (ret) + goto error; + + c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]); + + ret = regmap_read(priv->regmap[1], 0x0019, &utmp); + if (ret) + goto error; + + switch ((utmp >> 0) & 0x07) { + case 0: + c->modulation = QAM_16; + break; + case 1: + c->modulation = QAM_32; + break; + case 2: + c->modulation = QAM_64; + break; + case 3: + c->modulation = QAM_128; + break; + case 4: + c->modulation = QAM_256; + break; + } + + switch ((utmp >> 7) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp, utmp1, utmp2; + u8 buf[3]; + + /* Lock detection */ + ret = regmap_bulk_read(priv->regmap[1], 0x0088, &buf[0], 1); + if (ret) + goto error; + ret = regmap_bulk_read(priv->regmap[1], 0x0073, &buf[1], 1); + if (ret) + goto error; + + utmp1 = (buf[0] >> 0) & 0x01; + utmp2 = (buf[1] >> 3) & 0x01; + + if (utmp1 == 1 && utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (utmp1 == 1 || utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC; + } else { + *status = 0; + } + + dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", + *status, 2, buf, utmp1, utmp2); + + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + unsigned int strength; + + ret = regmap_bulk_read(priv->regmap[1], 0x0049, buf, 2); + if (ret) + goto error; + + utmp = buf[0] << 8 | buf[1] << 0; + utmp = 511 - sign_extend32(utmp, 9); + /* Scale value to 0x0000-0xffff */ + strength = utmp << 6 | utmp >> 4; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (*status & FE_HAS_VITERBI) { + unsigned int cnr, const_a, const_b; + + ret = regmap_read(priv->regmap[1], 0x0019, &utmp); + if (ret) + goto error; + + if (((utmp >> 0) & 0x03) % 2) { + const_a = 8750; + const_b = 650; + } else { + const_a = 9500; + const_b = 760; + } + + ret = regmap_read(priv->regmap[1], 0x004d, &utmp); + if (ret) + goto error; + + #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */ + if (utmp) + cnr = div_u64((u64)(intlog2(const_b) - intlog2(utmp)) + * const_a, CXD2820R_LOG2_E_24); + else + cnr = 0; + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (*status & FE_HAS_SYNC) { + unsigned int post_bit_error; + bool start_ber; + + if (priv->ber_running) { + ret = regmap_bulk_read(priv->regmap[1], 0x0076, buf, 3); + if (ret) + goto error; + + if ((buf[2] >> 7) & 0x01) { + post_bit_error = buf[2] << 16 | buf[1] << 8 | + buf[0] << 0; + post_bit_error &= 0x0fffff; + start_ber = true; + } else { + post_bit_error = 0; + start_ber = false; + } + } else { + post_bit_error = 0; + start_ber = true; + } + + if (start_ber) { + ret = regmap_write(priv->regmap[1], 0x0079, 0x01); + if (ret) + goto error; + priv->ber_running = true; + } + + priv->post_bit_error += post_bit_error; + + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = priv->post_bit_error; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_init_c(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_write(priv->regmap[0], 0x0085, 0x07); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_sleep_c(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + static const struct reg_val_mask tab[] = { + { 0x000ff, 0x1f, 0xff }, + { 0x00085, 0x00, 0xff }, + { 0x00088, 0x01, 0xff }, + { 0x00081, 0x00, 0xff }, + { 0x00080, 0x00, 0xff }, + }; + + dev_dbg(&client->dev, "\n"); + + priv->delivery_system = SYS_UNDEFINED; + + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 500; + s->step_size = 0; /* no zigzag */ + s->max_drift = 0; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c new file mode 100644 index 0000000000..7feb08dccf --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -0,0 +1,745 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sony CXD2820R demodulator driver + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + */ + + +#include "cxd2820r_priv.h" + +/* Write register table */ +int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv, + const struct reg_val_mask *tab, int tab_len) +{ + struct i2c_client *client = priv->client[0]; + int ret; + unsigned int i, reg, mask, val; + struct regmap *regmap; + + dev_dbg(&client->dev, "tab_len=%d\n", tab_len); + + for (i = 0; i < tab_len; i++) { + if ((tab[i].reg >> 16) & 0x1) + regmap = priv->regmap[1]; + else + regmap = priv->regmap[0]; + + reg = (tab[i].reg >> 0) & 0xffff; + val = tab[i].val; + mask = tab[i].mask; + + if (mask == 0xff) + ret = regmap_write(regmap, reg, val); + else + ret = regmap_write_bits(regmap, reg, mask, val); + if (ret) + goto error; + } + + return 0; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u8 tmp0, tmp1; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + /* update GPIOs only when needed */ + if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio))) + return 0; + + tmp0 = 0x00; + tmp1 = 0x00; + for (i = 0; i < sizeof(priv->gpio); i++) { + /* enable / disable */ + if (gpio[i] & CXD2820R_GPIO_E) + tmp0 |= (2 << 6) >> (2 * i); + else + tmp0 |= (1 << 6) >> (2 * i); + + /* input / output */ + if (gpio[i] & CXD2820R_GPIO_I) + tmp1 |= (1 << (3 + i)); + else + tmp1 |= (0 << (3 + i)); + + /* high / low */ + if (gpio[i] & CXD2820R_GPIO_H) + tmp1 |= (1 << (0 + i)); + else + tmp1 |= (0 << (0 + i)); + + dev_dbg(&client->dev, "gpio i=%d %02x %02x\n", i, tmp0, tmp1); + } + + dev_dbg(&client->dev, "wr gpio=%02x %02x\n", tmp0, tmp1); + + /* write bits [7:2] */ + ret = regmap_update_bits(priv->regmap[0], 0x0089, 0xfc, tmp0); + if (ret) + goto error; + + /* write bits [5:0] */ + ret = regmap_update_bits(priv->regmap[0], 0x008e, 0x3f, tmp1); + if (ret) + goto error; + + memcpy(priv->gpio, gpio, sizeof(priv->gpio)); + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int cxd2820r_set_frontend(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + switch (c->delivery_system) { + case SYS_DVBT: + ret = cxd2820r_init_t(fe); + if (ret < 0) + goto err; + ret = cxd2820r_set_frontend_t(fe); + if (ret < 0) + goto err; + break; + case SYS_DVBT2: + ret = cxd2820r_init_t(fe); + if (ret < 0) + goto err; + ret = cxd2820r_set_frontend_t2(fe); + if (ret < 0) + goto err; + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2820r_init_c(fe); + if (ret < 0) + goto err; + ret = cxd2820r_set_frontend_c(fe); + if (ret < 0) + goto err; + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + break; + } +err: + return ret; +} + +static int cxd2820r_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + switch (c->delivery_system) { + case SYS_DVBT: + ret = cxd2820r_read_status_t(fe, status); + break; + case SYS_DVBT2: + ret = cxd2820r_read_status_t2(fe, status); + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2820r_read_status_c(fe, status); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int cxd2820r_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + if (priv->delivery_system == SYS_UNDEFINED) + return 0; + + switch (c->delivery_system) { + case SYS_DVBT: + ret = cxd2820r_get_frontend_t(fe, p); + break; + case SYS_DVBT2: + ret = cxd2820r_get_frontend_t2(fe, p); + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2820r_get_frontend_c(fe, p); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + *ber = (priv->post_bit_error - priv->post_bit_error_prev_dvbv3); + priv->post_bit_error_prev_dvbv3 = priv->post_bit_error; + + return 0; +} + +static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + if (c->strength.stat[0].scale == FE_SCALE_RELATIVE) + *strength = c->strength.stat[0].uvalue; + else + *strength = 0; + + return 0; +} + +static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + + return 0; +} + +static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + *ucblocks = 0; + + return 0; +} + +static int cxd2820r_init(struct dvb_frontend *fe) +{ + return 0; +} + +static int cxd2820r_sleep(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + switch (c->delivery_system) { + case SYS_DVBT: + ret = cxd2820r_sleep_t(fe); + break; + case SYS_DVBT2: + ret = cxd2820r_sleep_t2(fe); + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2820r_sleep_c(fe); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int cxd2820r_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + switch (c->delivery_system) { + case SYS_DVBT: + ret = cxd2820r_get_tune_settings_t(fe, s); + break; + case SYS_DVBT2: + ret = cxd2820r_get_tune_settings_t2(fe, s); + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2820r_get_tune_settings_c(fe, s); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + enum fe_status status = 0; + + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); + + /* switch between DVB-T and DVB-T2 when tune fails */ + if (priv->last_tune_failed) { + if (priv->delivery_system == SYS_DVBT) { + ret = cxd2820r_sleep_t(fe); + if (ret) + goto error; + + c->delivery_system = SYS_DVBT2; + } else if (priv->delivery_system == SYS_DVBT2) { + ret = cxd2820r_sleep_t2(fe); + if (ret) + goto error; + + c->delivery_system = SYS_DVBT; + } + } + + /* set frontend */ + ret = cxd2820r_set_frontend(fe); + if (ret) + goto error; + + /* frontend lock wait loop count */ + switch (priv->delivery_system) { + case SYS_DVBT: + case SYS_DVBC_ANNEX_A: + i = 20; + break; + case SYS_DVBT2: + i = 40; + break; + case SYS_UNDEFINED: + default: + i = 0; + break; + } + + /* wait frontend lock */ + for (; i > 0; i--) { + dev_dbg(&client->dev, "loop=%d\n", i); + msleep(50); + ret = cxd2820r_read_status(fe, &status); + if (ret) + goto error; + + if (status & FE_HAS_LOCK) + break; + } + + /* check if we have a valid signal */ + if (status & FE_HAS_LOCK) { + priv->last_tune_failed = false; + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + priv->last_tune_failed = true; + return DVBFE_ALGO_SEARCH_AGAIN; + } + +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return DVBFE_ALGO_SEARCH_ERROR; +} + +static enum dvbfe_algo cxd2820r_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_CUSTOM; +} + +static void cxd2820r_release(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + + dev_dbg(&client->dev, "\n"); + + i2c_unregister_device(client); + + return; +} + +static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + + dev_dbg_ratelimited(&client->dev, "enable=%d\n", enable); + + return regmap_update_bits(priv->regmap[0], 0x00db, 0x01, enable ? 1 : 0); +} + +#ifdef CONFIG_GPIOLIB +static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr, + int val) +{ + struct cxd2820r_priv *priv = gpiochip_get_data(chip); + struct i2c_client *client = priv->client[0]; + u8 gpio[GPIO_COUNT]; + + dev_dbg(&client->dev, "nr=%u val=%d\n", nr, val); + + memcpy(gpio, priv->gpio, sizeof(gpio)); + gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); + + return cxd2820r_gpio(&priv->fe, gpio); +} + +static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val) +{ + struct cxd2820r_priv *priv = gpiochip_get_data(chip); + struct i2c_client *client = priv->client[0]; + u8 gpio[GPIO_COUNT]; + + dev_dbg(&client->dev, "nr=%u val=%d\n", nr, val); + + memcpy(gpio, priv->gpio, sizeof(gpio)); + gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); + + (void) cxd2820r_gpio(&priv->fe, gpio); + + return; +} + +static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr) +{ + struct cxd2820r_priv *priv = gpiochip_get_data(chip); + struct i2c_client *client = priv->client[0]; + + dev_dbg(&client->dev, "nr=%u\n", nr); + + return (priv->gpio[nr] >> 2) & 0x01; +} +#endif + +static const struct dvb_frontend_ops cxd2820r_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, + /* default: DVB-T/T2 */ + .info = { + .name = "Sony CXD2820R", + + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM + }, + + .release = cxd2820r_release, + .init = cxd2820r_init, + .sleep = cxd2820r_sleep, + + .get_tune_settings = cxd2820r_get_tune_settings, + .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl, + + .get_frontend = cxd2820r_get_frontend, + + .get_frontend_algo = cxd2820r_get_frontend_algo, + .search = cxd2820r_search, + + .read_status = cxd2820r_read_status, + .read_snr = cxd2820r_read_snr, + .read_ber = cxd2820r_read_ber, + .read_ucblocks = cxd2820r_read_ucblocks, + .read_signal_strength = cxd2820r_read_signal_strength, +}; + +/* + * XXX: That is wrapper to cxd2820r_probe() via driver core in order to provide + * proper I2C client for legacy media attach binding. + * New users must use I2C client binding directly! + */ +struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *config, + struct i2c_adapter *adapter, + int *gpio_chip_base) +{ + struct i2c_client *client; + struct i2c_board_info board_info; + struct cxd2820r_platform_data pdata; + + pdata.ts_mode = config->ts_mode; + pdata.ts_clk_inv = config->ts_clock_inv; + pdata.if_agc_polarity = config->if_agc_polarity; + pdata.spec_inv = config->spec_inv; + pdata.gpio_chip_base = &gpio_chip_base; + pdata.attach_in_use = true; + + memset(&board_info, 0, sizeof(board_info)); + strscpy(board_info.type, "cxd2820r", I2C_NAME_SIZE); + board_info.addr = config->i2c_address; + board_info.platform_data = &pdata; + client = i2c_new_client_device(adapter, &board_info); + if (!i2c_client_has_driver(client)) + return NULL; + + return pdata.get_dvb_frontend(client); +} +EXPORT_SYMBOL_GPL(cxd2820r_attach); + +static struct dvb_frontend *cxd2820r_get_dvb_frontend(struct i2c_client *client) +{ + struct cxd2820r_priv *priv = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &priv->fe; +} + +static int cxd2820r_probe(struct i2c_client *client) +{ + struct cxd2820r_platform_data *pdata = client->dev.platform_data; + struct cxd2820r_priv *priv; + int ret, *gpio_chip_base; + unsigned int utmp; + static const struct regmap_range_cfg regmap_range_cfg0[] = { + { + .range_min = 0x0000, + .range_max = 0x3fff, + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x00, + .window_len = 0x100, + }, + }; + static const struct regmap_range_cfg regmap_range_cfg1[] = { + { + .range_min = 0x0000, + .range_max = 0x01ff, + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x00, + .window_len = 0x100, + }, + }; + static const struct regmap_config regmap_config0 = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x3fff, + .ranges = regmap_range_cfg0, + .num_ranges = ARRAY_SIZE(regmap_range_cfg0), + .cache_type = REGCACHE_NONE, + }; + static const struct regmap_config regmap_config1 = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x01ff, + .ranges = regmap_range_cfg1, + .num_ranges = ARRAY_SIZE(regmap_range_cfg1), + .cache_type = REGCACHE_NONE, + }; + + dev_dbg(&client->dev, "\n"); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err; + } + + priv->client[0] = client; + priv->fe.demodulator_priv = priv; + priv->i2c = client->adapter; + priv->ts_mode = pdata->ts_mode; + priv->ts_clk_inv = pdata->ts_clk_inv; + priv->if_agc_polarity = pdata->if_agc_polarity; + priv->spec_inv = pdata->spec_inv; + gpio_chip_base = *pdata->gpio_chip_base; + priv->regmap[0] = regmap_init_i2c(priv->client[0], ®map_config0); + if (IS_ERR(priv->regmap[0])) { + ret = PTR_ERR(priv->regmap[0]); + goto err_kfree; + } + + /* Check demod answers with correct chip id */ + ret = regmap_read(priv->regmap[0], 0x00fd, &utmp); + if (ret) + goto err_regmap_0_regmap_exit; + + dev_dbg(&client->dev, "chip_id=%02x\n", utmp); + + if (utmp != 0xe1) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + + /* + * Chip has two I2C addresses for different register banks. We register + * one dummy I2C client in order to get own I2C client for each + * register bank. + */ + priv->client[1] = i2c_new_dummy_device(client->adapter, client->addr | (1 << 1)); + if (IS_ERR(priv->client[1])) { + ret = PTR_ERR(priv->client[1]); + dev_err(&client->dev, "I2C registration failed\n"); + goto err_regmap_0_regmap_exit; + } + + priv->regmap[1] = regmap_init_i2c(priv->client[1], ®map_config1); + if (IS_ERR(priv->regmap[1])) { + ret = PTR_ERR(priv->regmap[1]); + goto err_client_1_i2c_unregister_device; + } + + if (gpio_chip_base) { +#ifdef CONFIG_GPIOLIB + /* Add GPIOs */ + priv->gpio_chip.label = KBUILD_MODNAME; + priv->gpio_chip.parent = &client->dev; + priv->gpio_chip.owner = THIS_MODULE; + priv->gpio_chip.direction_output = cxd2820r_gpio_direction_output; + priv->gpio_chip.set = cxd2820r_gpio_set; + priv->gpio_chip.get = cxd2820r_gpio_get; + priv->gpio_chip.base = -1; /* Dynamic allocation */ + priv->gpio_chip.ngpio = GPIO_COUNT; + priv->gpio_chip.can_sleep = 1; + ret = gpiochip_add_data(&priv->gpio_chip, priv); + if (ret) + goto err_regmap_1_regmap_exit; + + dev_dbg(&client->dev, "gpio_chip.base=%d\n", + priv->gpio_chip.base); + + *gpio_chip_base = priv->gpio_chip.base; +#else + /* + * Use static GPIO configuration if GPIOLIB is undefined. + * This is fallback condition. + */ + u8 gpio[GPIO_COUNT]; + gpio[0] = (*gpio_chip_base >> 0) & 0x07; + gpio[1] = (*gpio_chip_base >> 3) & 0x07; + gpio[2] = 0; + ret = cxd2820r_gpio(&priv->fe, gpio); + if (ret) + goto err_regmap_1_regmap_exit; +#endif + } + + /* Create dvb frontend */ + memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(priv->fe.ops)); + if (!pdata->attach_in_use) + priv->fe.ops.release = NULL; + i2c_set_clientdata(client, priv); + + /* Setup callbacks */ + pdata->get_dvb_frontend = cxd2820r_get_dvb_frontend; + + dev_info(&client->dev, "Sony CXD2820R successfully identified\n"); + + return 0; +err_regmap_1_regmap_exit: + regmap_exit(priv->regmap[1]); +err_client_1_i2c_unregister_device: + i2c_unregister_device(priv->client[1]); +err_regmap_0_regmap_exit: + regmap_exit(priv->regmap[0]); +err_kfree: + kfree(priv); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void cxd2820r_remove(struct i2c_client *client) +{ + struct cxd2820r_priv *priv = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + +#ifdef CONFIG_GPIOLIB + if (priv->gpio_chip.label) + gpiochip_remove(&priv->gpio_chip); +#endif + regmap_exit(priv->regmap[1]); + i2c_unregister_device(priv->client[1]); + + regmap_exit(priv->regmap[0]); + + kfree(priv); +} + +static const struct i2c_device_id cxd2820r_id_table[] = { + {"cxd2820r", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table); + +static struct i2c_driver cxd2820r_driver = { + .driver = { + .name = "cxd2820r", + .suppress_bind_attrs = true, + }, + .probe = cxd2820r_probe, + .remove = cxd2820r_remove, + .id_table = cxd2820r_id_table, +}; + +module_i2c_driver(cxd2820r_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Sony CXD2820R demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h new file mode 100644 index 0000000000..605320bbc1 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2820r_priv.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Sony CXD2820R demodulator driver + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + */ + + +#ifndef CXD2820R_PRIV_H +#define CXD2820R_PRIV_H + +#include <linux/dvb/version.h> +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "cxd2820r.h" +#include <linux/gpio/driver.h> /* For gpio_chip */ +#include <linux/math64.h> +#include <linux/regmap.h> + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +#define CXD2820R_CLK 41000000 + +struct cxd2820r_priv { + struct i2c_client *client[2]; + struct regmap *regmap[2]; + struct i2c_adapter *i2c; + struct dvb_frontend fe; + u8 ts_mode; + bool ts_clk_inv; + bool if_agc_polarity; + bool spec_inv; + + u64 post_bit_error_prev_dvbv3; + u64 post_bit_error; + + bool ber_running; + +#define GPIO_COUNT 3 + u8 gpio[GPIO_COUNT]; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif + + enum fe_delivery_system delivery_system; + bool last_tune_failed; /* for switch between T and T2 tune */ +}; + +/* cxd2820r_core.c */ + +int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio); + +int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv, + const struct reg_val_mask *tab, int tab_len); + +int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, + u8 mask); + +int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, + int len); + +int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, + int len); + +int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, + int len); + +int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val); + +int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val); + +/* cxd2820r_c.c */ + +int cxd2820r_get_frontend_c(struct dvb_frontend *fe, + struct dtv_frontend_properties *p); + +int cxd2820r_set_frontend_c(struct dvb_frontend *fe); + +int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status); + +int cxd2820r_init_c(struct dvb_frontend *fe); + +int cxd2820r_sleep_c(struct dvb_frontend *fe); + +int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s); + +/* cxd2820r_t.c */ + +int cxd2820r_get_frontend_t(struct dvb_frontend *fe, + struct dtv_frontend_properties *p); + +int cxd2820r_set_frontend_t(struct dvb_frontend *fe); + +int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status); + +int cxd2820r_init_t(struct dvb_frontend *fe); + +int cxd2820r_sleep_t(struct dvb_frontend *fe); + +int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s); + +/* cxd2820r_t2.c */ + +int cxd2820r_get_frontend_t2(struct dvb_frontend *fe, + struct dtv_frontend_properties *p); + +int cxd2820r_set_frontend_t2(struct dvb_frontend *fe); + +int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status); + +int cxd2820r_init_t2(struct dvb_frontend *fe); + +int cxd2820r_sleep_t2(struct dvb_frontend *fe); + +int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s); + +#endif /* CXD2820R_PRIV_H */ diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c new file mode 100644 index 0000000000..fbdfa6bf38 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sony CXD2820R demodulator driver + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + */ + + +#include "cxd2820r_priv.h" + +int cxd2820r_set_frontend_t(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, bw_i; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], bw_param; + u8 bw_params1[][5] = { + { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ + { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ + { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ + }; + u8 bw_params2[][2] = { + { 0x1f, 0xdc }, /* 6 MHz */ + { 0x12, 0xf8 }, /* 7 MHz */ + { 0x01, 0xe0 }, /* 8 MHz */ + }; + struct reg_val_mask tab[] = { + { 0x00080, 0x00, 0xff }, + { 0x00081, 0x03, 0xff }, + { 0x00085, 0x07, 0xff }, + { 0x00088, 0x01, 0xff }, + + { 0x00070, priv->ts_mode, 0xff }, + { 0x00071, !priv->ts_clk_inv << 4, 0x10 }, + { 0x000cb, priv->if_agc_polarity << 6, 0x40 }, + { 0x000a5, 0x00, 0x01 }, + { 0x00082, 0x20, 0x60 }, + { 0x000c2, 0xc3, 0xff }, + { 0x0016a, 0x50, 0xff }, + { 0x00427, 0x41, 0xff }, + }; + + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u bandwidth_hz=%u inversion=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->inversion); + + switch (c->bandwidth_hz) { + case 6000000: + bw_i = 0; + bw_param = 2; + break; + case 7000000: + bw_i = 1; + bw_param = 1; + break; + case 8000000: + bw_i = 2; + bw_param = 0; + break; + default: + return -EINVAL; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + if (priv->delivery_system != SYS_DVBT) { + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; + } + + priv->delivery_system = SYS_DVBT; + priv->ber_running = false; /* tune stops BER counter */ + + /* program IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto error; + dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto error; + } + + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, CXD2820R_CLK); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + ret = regmap_bulk_write(priv->regmap[0], 0x00b6, buf, 3); + if (ret) + goto error; + + ret = regmap_bulk_write(priv->regmap[0], 0x009f, bw_params1[bw_i], 5); + if (ret) + goto error; + + ret = regmap_update_bits(priv->regmap[0], 0x00d7, 0xc0, bw_param << 6); + if (ret) + goto error; + + ret = regmap_bulk_write(priv->regmap[0], 0x00d9, bw_params2[bw_i], 2); + if (ret) + goto error; + + ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); + if (ret) + goto error; + + ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_get_frontend_t(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + unsigned int utmp; + u8 buf[2]; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(priv->regmap[0], 0x002f, buf, sizeof(buf)); + if (ret) + goto error; + + switch ((buf[0] >> 6) & 0x03) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[1] >> 1) & 0x03) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch ((buf[1] >> 3) & 0x03) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 3) & 0x07) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[0] >> 0) & 0x07) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[1] >> 5) & 0x07) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + } + + ret = regmap_read(priv->regmap[0], 0x07c6, &utmp); + if (ret) + goto error; + + switch ((utmp >> 0) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp, utmp1, utmp2; + u8 buf[3]; + + /* Lock detection */ + ret = regmap_bulk_read(priv->regmap[0], 0x0010, &buf[0], 1); + if (ret) + goto error; + ret = regmap_bulk_read(priv->regmap[0], 0x0073, &buf[1], 1); + if (ret) + goto error; + + utmp1 = (buf[0] >> 0) & 0x07; + utmp2 = (buf[1] >> 3) & 0x01; + + if (utmp1 == 6 && utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (utmp1 == 6 || utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC; + } else { + *status = 0; + } + + dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", + *status, 2, buf, utmp1, utmp2); + + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + unsigned int strength; + + ret = regmap_bulk_read(priv->regmap[0], 0x0026, buf, 2); + if (ret) + goto error; + + utmp = buf[0] << 8 | buf[1] << 0; + utmp = ~utmp & 0x0fff; + /* Scale value to 0x0000-0xffff */ + strength = utmp << 4 | utmp >> 8; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (*status & FE_HAS_VITERBI) { + unsigned int cnr; + + ret = regmap_bulk_read(priv->regmap[0], 0x002c, buf, 2); + if (ret) + goto error; + + utmp = buf[0] << 8 | buf[1] << 0; + if (utmp) + cnr = div_u64((u64)(intlog10(utmp) + - intlog10(32000 - utmp) + 55532585) + * 10000, (1 << 24)); + else + cnr = 0; + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (*status & FE_HAS_SYNC) { + unsigned int post_bit_error; + bool start_ber; + + if (priv->ber_running) { + ret = regmap_bulk_read(priv->regmap[0], 0x0076, buf, 3); + if (ret) + goto error; + + if ((buf[2] >> 7) & 0x01) { + post_bit_error = buf[2] << 16 | buf[1] << 8 | + buf[0] << 0; + post_bit_error &= 0x0fffff; + start_ber = true; + } else { + post_bit_error = 0; + start_ber = false; + } + } else { + post_bit_error = 0; + start_ber = true; + } + + if (start_ber) { + ret = regmap_write(priv->regmap[0], 0x0079, 0x01); + if (ret) + goto error; + priv->ber_running = true; + } + + priv->post_bit_error += post_bit_error; + + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = priv->post_bit_error; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_init_t(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_write(priv->regmap[0], 0x0085, 0x07); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_sleep_t(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + static struct reg_val_mask tab[] = { + { 0x000ff, 0x1f, 0xff }, + { 0x00085, 0x00, 0xff }, + { 0x00088, 0x01, 0xff }, + { 0x00081, 0x00, 0xff }, + { 0x00080, 0x00, 0xff }, + }; + + dev_dbg(&client->dev, "\n"); + + priv->delivery_system = SYS_UNDEFINED; + + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 500; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c new file mode 100644 index 0000000000..34ef2bb2de --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sony CXD2820R demodulator driver + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + */ + + +#include "cxd2820r_priv.h" + +int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, bw_i; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], bw_param; + u8 bw_params1[][5] = { + { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */ + { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ + { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ + { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ + }; + struct reg_val_mask tab[] = { + { 0x00080, 0x02, 0xff }, + { 0x00081, 0x20, 0xff }, + { 0x00085, 0x07, 0xff }, + { 0x00088, 0x01, 0xff }, + { 0x02069, 0x01, 0xff }, + + { 0x0207f, 0x2a, 0xff }, + { 0x02082, 0x0a, 0xff }, + { 0x02083, 0x0a, 0xff }, + { 0x020cb, priv->if_agc_polarity << 6, 0x40 }, + { 0x02070, priv->ts_mode, 0xff }, + { 0x02071, !priv->ts_clk_inv << 6, 0x40 }, + { 0x020b5, priv->spec_inv << 4, 0x10 }, + { 0x02567, 0x07, 0x0f }, + { 0x02569, 0x03, 0x03 }, + { 0x02595, 0x1a, 0xff }, + { 0x02596, 0x50, 0xff }, + { 0x02a8c, 0x00, 0xff }, + { 0x02a8d, 0x34, 0xff }, + { 0x02a45, 0x06, 0x07 }, + { 0x03f10, 0x0d, 0xff }, + { 0x03f11, 0x02, 0xff }, + { 0x03f12, 0x01, 0xff }, + { 0x03f23, 0x2c, 0xff }, + { 0x03f51, 0x13, 0xff }, + { 0x03f52, 0x01, 0xff }, + { 0x03f53, 0x00, 0xff }, + { 0x027e6, 0x14, 0xff }, + { 0x02786, 0x02, 0x07 }, + { 0x02787, 0x40, 0xe0 }, + { 0x027ef, 0x10, 0x18 }, + }; + + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u bandwidth_hz=%u inversion=%d stream_id=%u\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->inversion, c->stream_id); + + switch (c->bandwidth_hz) { + case 5000000: + bw_i = 0; + bw_param = 3; + break; + case 6000000: + bw_i = 1; + bw_param = 2; + break; + case 7000000: + bw_i = 2; + bw_param = 1; + break; + case 8000000: + bw_i = 3; + bw_param = 0; + break; + default: + return -EINVAL; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + if (priv->delivery_system != SYS_DVBT2) { + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; + } + + priv->delivery_system = SYS_DVBT2; + + /* program IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto error; + dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto error; + } + + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, CXD2820R_CLK); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + ret = regmap_bulk_write(priv->regmap[0], 0x20b6, buf, 3); + if (ret) + goto error; + + /* PLP filtering */ + if (c->stream_id > 255) { + dev_dbg(&client->dev, "disable PLP filtering\n"); + ret = regmap_write(priv->regmap[0], 0x23ad, 0x00); + if (ret) + goto error; + } else { + dev_dbg(&client->dev, "enable PLP filtering\n"); + ret = regmap_write(priv->regmap[0], 0x23af, c->stream_id & 0xff); + if (ret) + goto error; + ret = regmap_write(priv->regmap[0], 0x23ad, 0x01); + if (ret) + goto error; + } + + ret = regmap_bulk_write(priv->regmap[0], 0x209f, bw_params1[bw_i], 5); + if (ret) + goto error; + + ret = regmap_update_bits(priv->regmap[0], 0x20d7, 0xc0, bw_param << 6); + if (ret) + goto error; + + ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); + if (ret) + goto error; + + ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; + +} + +int cxd2820r_get_frontend_t2(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + unsigned int utmp; + u8 buf[2]; + + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(priv->regmap[0], 0x205c, buf, 2); + if (ret) + goto error; + + switch ((buf[0] >> 0) & 0x07) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + case 2: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case 3: + c->transmission_mode = TRANSMISSION_MODE_1K; + break; + case 4: + c->transmission_mode = TRANSMISSION_MODE_16K; + break; + case 5: + c->transmission_mode = TRANSMISSION_MODE_32K; + break; + } + + switch ((buf[1] >> 4) & 0x07) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case 4: + c->guard_interval = GUARD_INTERVAL_1_128; + break; + case 5: + c->guard_interval = GUARD_INTERVAL_19_128; + break; + case 6: + c->guard_interval = GUARD_INTERVAL_19_256; + break; + } + + ret = regmap_bulk_read(priv->regmap[0], 0x225b, buf, 2); + if (ret) + goto error; + + switch ((buf[0] >> 0) & 0x07) { + case 0: + c->fec_inner = FEC_1_2; + break; + case 1: + c->fec_inner = FEC_3_5; + break; + case 2: + c->fec_inner = FEC_2_3; + break; + case 3: + c->fec_inner = FEC_3_4; + break; + case 4: + c->fec_inner = FEC_4_5; + break; + case 5: + c->fec_inner = FEC_5_6; + break; + } + + switch ((buf[1] >> 0) & 0x07) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + case 3: + c->modulation = QAM_256; + break; + } + + ret = regmap_read(priv->regmap[0], 0x20b5, &utmp); + if (ret) + goto error; + + switch ((utmp >> 4) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct i2c_client *client = priv->client[0]; + int ret; + unsigned int utmp, utmp1, utmp2; + u8 buf[4]; + + /* Lock detection */ + ret = regmap_bulk_read(priv->regmap[0], 0x2010, &buf[0], 1); + if (ret) + goto error; + + utmp1 = (buf[0] >> 0) & 0x07; + utmp2 = (buf[0] >> 5) & 0x01; + + if (utmp1 == 6 && utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (utmp1 == 6 || utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC; + } else { + *status = 0; + } + + dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", + *status, 1, buf, utmp1, utmp2); + + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + unsigned int strength; + + ret = regmap_bulk_read(priv->regmap[0], 0x2026, buf, 2); + if (ret) + goto error; + + utmp = buf[0] << 8 | buf[1] << 0; + utmp = ~utmp & 0x0fff; + /* Scale value to 0x0000-0xffff */ + strength = utmp << 4 | utmp >> 8; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (*status & FE_HAS_VITERBI) { + unsigned int cnr; + + ret = regmap_bulk_read(priv->regmap[0], 0x2028, buf, 2); + if (ret) + goto error; + + utmp = buf[0] << 8 | buf[1] << 0; + utmp = utmp & 0x0fff; + #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ + if (utmp) + cnr = div_u64((u64)(intlog10(utmp) + - CXD2820R_LOG10_8_24) * 10000, + (1 << 24)); + else + cnr = 0; + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (*status & FE_HAS_SYNC) { + unsigned int post_bit_error; + + ret = regmap_bulk_read(priv->regmap[0], 0x2039, buf, 4); + if (ret) + goto error; + + if ((buf[0] >> 4) & 0x01) { + post_bit_error = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + post_bit_error &= 0x0fffffff; + } else { + post_bit_error = 0; + } + + priv->post_bit_error += post_bit_error; + + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = priv->post_bit_error; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_sleep_t2(struct dvb_frontend *fe) +{ + struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + int ret; + static const struct reg_val_mask tab[] = { + { 0x000ff, 0x1f, 0xff }, + { 0x00085, 0x00, 0xff }, + { 0x00088, 0x01, 0xff }, + { 0x02069, 0x00, 0xff }, + { 0x00081, 0x00, 0xff }, + { 0x00080, 0x00, 0xff }, + }; + + dev_dbg(&client->dev, "\n"); + + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; + + priv->delivery_system = SYS_UNDEFINED; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1500; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c new file mode 100644 index 0000000000..d925ca2418 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -0,0 +1,4009 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * cxd2841er.c + * + * Sony digital demodulator driver for + * CXD2841ER - DVB-S/S2/T/T2/C/C2 + * CXD2854ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/math64.h> +#include <linux/log2.h> +#include <linux/dynamic_debug.h> +#include <linux/kernel.h> + +#include <linux/int_log.h> +#include <media/dvb_frontend.h> +#include "cxd2841er.h" +#include "cxd2841er_priv.h" + +#define MAX_WRITE_REGSIZE 16 +#define LOG2_E_100X 144 + +#define INTLOG10X100(x) ((u32) (((u64) intlog10(x) * 100) >> 24)) + +/* DVB-C constellation */ +enum sony_dvbc_constellation_t { + SONY_DVBC_CONSTELLATION_16QAM, + SONY_DVBC_CONSTELLATION_32QAM, + SONY_DVBC_CONSTELLATION_64QAM, + SONY_DVBC_CONSTELLATION_128QAM, + SONY_DVBC_CONSTELLATION_256QAM +}; + +enum cxd2841er_state { + STATE_SHUTDOWN = 0, + STATE_SLEEP_S, + STATE_ACTIVE_S, + STATE_SLEEP_TC, + STATE_ACTIVE_TC +}; + +struct cxd2841er_priv { + struct dvb_frontend frontend; + struct i2c_adapter *i2c; + u8 i2c_addr_slvx; + u8 i2c_addr_slvt; + const struct cxd2841er_config *config; + enum cxd2841er_state state; + u8 system; + enum cxd2841er_xtal xtal; + enum fe_caps caps; + u32 flags; + unsigned long stats_time; +}; + +static const struct cxd2841er_cnr_data s_cn_data[] = { + { 0x033e, 0 }, { 0x0339, 100 }, { 0x0333, 200 }, + { 0x032e, 300 }, { 0x0329, 400 }, { 0x0324, 500 }, + { 0x031e, 600 }, { 0x0319, 700 }, { 0x0314, 800 }, + { 0x030f, 900 }, { 0x030a, 1000 }, { 0x02ff, 1100 }, + { 0x02f4, 1200 }, { 0x02e9, 1300 }, { 0x02de, 1400 }, + { 0x02d4, 1500 }, { 0x02c9, 1600 }, { 0x02bf, 1700 }, + { 0x02b5, 1800 }, { 0x02ab, 1900 }, { 0x02a1, 2000 }, + { 0x029b, 2100 }, { 0x0295, 2200 }, { 0x0290, 2300 }, + { 0x028a, 2400 }, { 0x0284, 2500 }, { 0x027f, 2600 }, + { 0x0279, 2700 }, { 0x0274, 2800 }, { 0x026e, 2900 }, + { 0x0269, 3000 }, { 0x0262, 3100 }, { 0x025c, 3200 }, + { 0x0255, 3300 }, { 0x024f, 3400 }, { 0x0249, 3500 }, + { 0x0242, 3600 }, { 0x023c, 3700 }, { 0x0236, 3800 }, + { 0x0230, 3900 }, { 0x022a, 4000 }, { 0x0223, 4100 }, + { 0x021c, 4200 }, { 0x0215, 4300 }, { 0x020e, 4400 }, + { 0x0207, 4500 }, { 0x0201, 4600 }, { 0x01fa, 4700 }, + { 0x01f4, 4800 }, { 0x01ed, 4900 }, { 0x01e7, 5000 }, + { 0x01e0, 5100 }, { 0x01d9, 5200 }, { 0x01d2, 5300 }, + { 0x01cb, 5400 }, { 0x01c4, 5500 }, { 0x01be, 5600 }, + { 0x01b7, 5700 }, { 0x01b1, 5800 }, { 0x01aa, 5900 }, + { 0x01a4, 6000 }, { 0x019d, 6100 }, { 0x0196, 6200 }, + { 0x018f, 6300 }, { 0x0189, 6400 }, { 0x0182, 6500 }, + { 0x017c, 6600 }, { 0x0175, 6700 }, { 0x016f, 6800 }, + { 0x0169, 6900 }, { 0x0163, 7000 }, { 0x015c, 7100 }, + { 0x0156, 7200 }, { 0x0150, 7300 }, { 0x014a, 7400 }, + { 0x0144, 7500 }, { 0x013e, 7600 }, { 0x0138, 7700 }, + { 0x0132, 7800 }, { 0x012d, 7900 }, { 0x0127, 8000 }, + { 0x0121, 8100 }, { 0x011c, 8200 }, { 0x0116, 8300 }, + { 0x0111, 8400 }, { 0x010b, 8500 }, { 0x0106, 8600 }, + { 0x0101, 8700 }, { 0x00fc, 8800 }, { 0x00f7, 8900 }, + { 0x00f2, 9000 }, { 0x00ee, 9100 }, { 0x00ea, 9200 }, + { 0x00e6, 9300 }, { 0x00e2, 9400 }, { 0x00de, 9500 }, + { 0x00da, 9600 }, { 0x00d7, 9700 }, { 0x00d3, 9800 }, + { 0x00d0, 9900 }, { 0x00cc, 10000 }, { 0x00c7, 10100 }, + { 0x00c3, 10200 }, { 0x00bf, 10300 }, { 0x00ba, 10400 }, + { 0x00b6, 10500 }, { 0x00b2, 10600 }, { 0x00ae, 10700 }, + { 0x00aa, 10800 }, { 0x00a7, 10900 }, { 0x00a3, 11000 }, + { 0x009f, 11100 }, { 0x009c, 11200 }, { 0x0098, 11300 }, + { 0x0094, 11400 }, { 0x0091, 11500 }, { 0x008e, 11600 }, + { 0x008a, 11700 }, { 0x0087, 11800 }, { 0x0084, 11900 }, + { 0x0081, 12000 }, { 0x007e, 12100 }, { 0x007b, 12200 }, + { 0x0079, 12300 }, { 0x0076, 12400 }, { 0x0073, 12500 }, + { 0x0071, 12600 }, { 0x006e, 12700 }, { 0x006c, 12800 }, + { 0x0069, 12900 }, { 0x0067, 13000 }, { 0x0065, 13100 }, + { 0x0062, 13200 }, { 0x0060, 13300 }, { 0x005e, 13400 }, + { 0x005c, 13500 }, { 0x005a, 13600 }, { 0x0058, 13700 }, + { 0x0056, 13800 }, { 0x0054, 13900 }, { 0x0052, 14000 }, + { 0x0050, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 }, + { 0x004b, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 }, + { 0x0046, 14700 }, { 0x0044, 14800 }, { 0x0043, 14900 }, + { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 }, + { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 }, + { 0x0037, 15700 }, { 0x0036, 15800 }, { 0x0034, 15900 }, + { 0x0033, 16000 }, { 0x0032, 16100 }, { 0x0031, 16200 }, + { 0x0030, 16300 }, { 0x002f, 16400 }, { 0x002e, 16500 }, + { 0x002d, 16600 }, { 0x002c, 16700 }, { 0x002b, 16800 }, + { 0x002a, 16900 }, { 0x0029, 17000 }, { 0x0028, 17100 }, + { 0x0027, 17200 }, { 0x0026, 17300 }, { 0x0025, 17400 }, + { 0x0024, 17500 }, { 0x0023, 17600 }, { 0x0022, 17800 }, + { 0x0021, 17900 }, { 0x0020, 18000 }, { 0x001f, 18200 }, + { 0x001e, 18300 }, { 0x001d, 18500 }, { 0x001c, 18700 }, + { 0x001b, 18900 }, { 0x001a, 19000 }, { 0x0019, 19200 }, + { 0x0018, 19300 }, { 0x0017, 19500 }, { 0x0016, 19700 }, + { 0x0015, 19900 }, { 0x0014, 20000 }, +}; + +static const struct cxd2841er_cnr_data s2_cn_data[] = { + { 0x05af, 0 }, { 0x0597, 100 }, { 0x057e, 200 }, + { 0x0567, 300 }, { 0x0550, 400 }, { 0x0539, 500 }, + { 0x0522, 600 }, { 0x050c, 700 }, { 0x04f6, 800 }, + { 0x04e1, 900 }, { 0x04cc, 1000 }, { 0x04b6, 1100 }, + { 0x04a1, 1200 }, { 0x048c, 1300 }, { 0x0477, 1400 }, + { 0x0463, 1500 }, { 0x044f, 1600 }, { 0x043c, 1700 }, + { 0x0428, 1800 }, { 0x0416, 1900 }, { 0x0403, 2000 }, + { 0x03ef, 2100 }, { 0x03dc, 2200 }, { 0x03c9, 2300 }, + { 0x03b6, 2400 }, { 0x03a4, 2500 }, { 0x0392, 2600 }, + { 0x0381, 2700 }, { 0x036f, 2800 }, { 0x035f, 2900 }, + { 0x034e, 3000 }, { 0x033d, 3100 }, { 0x032d, 3200 }, + { 0x031d, 3300 }, { 0x030d, 3400 }, { 0x02fd, 3500 }, + { 0x02ee, 3600 }, { 0x02df, 3700 }, { 0x02d0, 3800 }, + { 0x02c2, 3900 }, { 0x02b4, 4000 }, { 0x02a6, 4100 }, + { 0x0299, 4200 }, { 0x028c, 4300 }, { 0x027f, 4400 }, + { 0x0272, 4500 }, { 0x0265, 4600 }, { 0x0259, 4700 }, + { 0x024d, 4800 }, { 0x0241, 4900 }, { 0x0236, 5000 }, + { 0x022b, 5100 }, { 0x0220, 5200 }, { 0x0215, 5300 }, + { 0x020a, 5400 }, { 0x0200, 5500 }, { 0x01f6, 5600 }, + { 0x01ec, 5700 }, { 0x01e2, 5800 }, { 0x01d8, 5900 }, + { 0x01cf, 6000 }, { 0x01c6, 6100 }, { 0x01bc, 6200 }, + { 0x01b3, 6300 }, { 0x01aa, 6400 }, { 0x01a2, 6500 }, + { 0x0199, 6600 }, { 0x0191, 6700 }, { 0x0189, 6800 }, + { 0x0181, 6900 }, { 0x0179, 7000 }, { 0x0171, 7100 }, + { 0x0169, 7200 }, { 0x0161, 7300 }, { 0x015a, 7400 }, + { 0x0153, 7500 }, { 0x014b, 7600 }, { 0x0144, 7700 }, + { 0x013d, 7800 }, { 0x0137, 7900 }, { 0x0130, 8000 }, + { 0x012a, 8100 }, { 0x0124, 8200 }, { 0x011e, 8300 }, + { 0x0118, 8400 }, { 0x0112, 8500 }, { 0x010c, 8600 }, + { 0x0107, 8700 }, { 0x0101, 8800 }, { 0x00fc, 8900 }, + { 0x00f7, 9000 }, { 0x00f2, 9100 }, { 0x00ec, 9200 }, + { 0x00e7, 9300 }, { 0x00e2, 9400 }, { 0x00dd, 9500 }, + { 0x00d8, 9600 }, { 0x00d4, 9700 }, { 0x00cf, 9800 }, + { 0x00ca, 9900 }, { 0x00c6, 10000 }, { 0x00c2, 10100 }, + { 0x00be, 10200 }, { 0x00b9, 10300 }, { 0x00b5, 10400 }, + { 0x00b1, 10500 }, { 0x00ae, 10600 }, { 0x00aa, 10700 }, + { 0x00a6, 10800 }, { 0x00a3, 10900 }, { 0x009f, 11000 }, + { 0x009b, 11100 }, { 0x0098, 11200 }, { 0x0095, 11300 }, + { 0x0091, 11400 }, { 0x008e, 11500 }, { 0x008b, 11600 }, + { 0x0088, 11700 }, { 0x0085, 11800 }, { 0x0082, 11900 }, + { 0x007f, 12000 }, { 0x007c, 12100 }, { 0x007a, 12200 }, + { 0x0077, 12300 }, { 0x0074, 12400 }, { 0x0072, 12500 }, + { 0x006f, 12600 }, { 0x006d, 12700 }, { 0x006b, 12800 }, + { 0x0068, 12900 }, { 0x0066, 13000 }, { 0x0064, 13100 }, + { 0x0061, 13200 }, { 0x005f, 13300 }, { 0x005d, 13400 }, + { 0x005b, 13500 }, { 0x0059, 13600 }, { 0x0057, 13700 }, + { 0x0055, 13800 }, { 0x0053, 13900 }, { 0x0051, 14000 }, + { 0x004f, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 }, + { 0x004a, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 }, + { 0x0045, 14700 }, { 0x0044, 14800 }, { 0x0042, 14900 }, + { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 }, + { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 }, + { 0x0038, 15600 }, { 0x0037, 15700 }, { 0x0036, 15800 }, + { 0x0034, 15900 }, { 0x0033, 16000 }, { 0x0032, 16100 }, + { 0x0031, 16200 }, { 0x0030, 16300 }, { 0x002f, 16400 }, + { 0x002e, 16500 }, { 0x002d, 16600 }, { 0x002c, 16700 }, + { 0x002b, 16800 }, { 0x002a, 16900 }, { 0x0029, 17000 }, + { 0x0028, 17100 }, { 0x0027, 17200 }, { 0x0026, 17300 }, + { 0x0025, 17400 }, { 0x0024, 17500 }, { 0x0023, 17600 }, + { 0x0022, 17800 }, { 0x0021, 17900 }, { 0x0020, 18000 }, + { 0x001f, 18200 }, { 0x001e, 18300 }, { 0x001d, 18500 }, + { 0x001c, 18700 }, { 0x001b, 18900 }, { 0x001a, 19000 }, + { 0x0019, 19200 }, { 0x0018, 19300 }, { 0x0017, 19500 }, + { 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 }, +}; + +static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv); +static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv); + +static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 write, + const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, + "cxd2841er: I2C %s addr %02x reg 0x%02x size %d data %*ph\n", + (write == 0 ? "read" : "write"), addr, reg, len, len, data); +} + +static int cxd2841er_write_regs(struct cxd2841er_priv *priv, + u8 addr, u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + u8 i2c_addr = (addr == I2C_SLVX ? + priv->i2c_addr_slvx : priv->i2c_addr_slvt); + struct i2c_msg msg[1] = { + { + .addr = i2c_addr, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev, "wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + + cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d addr=%02x reg=%02x len=%d\n", + KBUILD_MODNAME, ret, i2c_addr, reg, len); + return ret; + } + return 0; +} + +static int cxd2841er_write_reg(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 val) +{ + u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return cxd2841er_write_regs(priv, addr, reg, &tmp, 1); +} + +static int cxd2841er_read_regs(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 *val, u32 len) +{ + int ret; + u8 i2c_addr = (addr == I2C_SLVX ? + priv->i2c_addr_slvx : priv->i2c_addr_slvt); + struct i2c_msg msg[2] = { + { + .addr = i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret >= 0 && ret != 2) + ret = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, i2c_addr, reg); + return ret; + } + cxd2841er_i2c_debug(priv, i2c_addr, reg, 0, val, len); + return 0; +} + +static int cxd2841er_read_reg(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 *val) +{ + return cxd2841er_read_regs(priv, addr, reg, val, 1); +} + +static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = cxd2841er_read_reg(priv, addr, reg, &rdata); + if (res) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return cxd2841er_write_reg(priv, addr, reg, data); +} + +static u32 cxd2841er_calc_iffreq_xtal(enum cxd2841er_xtal xtal, u32 ifhz) +{ + u64 tmp; + + tmp = (u64) ifhz * 16777216; + do_div(tmp, ((xtal == SONY_XTAL_24000) ? 48000000 : 41000000)); + + return (u32) tmp; +} + +static u32 cxd2841er_calc_iffreq(u32 ifhz) +{ + return cxd2841er_calc_iffreq_xtal(SONY_XTAL_20500, ifhz); +} + +static int cxd2841er_get_if_hz(struct cxd2841er_priv *priv, u32 def_hz) +{ + u32 hz; + + if (priv->frontend.ops.tuner_ops.get_if_frequency + && (priv->flags & CXD2841ER_AUTO_IFHZ)) + priv->frontend.ops.tuner_ops.get_if_frequency( + &priv->frontend, &hz); + else + hz = def_hz; + + return hz; +} + +static int cxd2841er_tuner_set(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + if ((priv->flags & CXD2841ER_USE_GATECTRL) && fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + if ((priv->flags & CXD2841ER_USE_GATECTRL) && fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv, + u32 symbol_rate) +{ + u32 reg_value = 0; + u8 data[3] = {0, 0, 0}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* + * regValue = (symbolRateKSps * 2^14 / 1000) + 0.5 + * = ((symbolRateKSps * 2^14) + 500) / 1000 + * = ((symbolRateKSps * 16384) + 500) / 1000 + */ + reg_value = DIV_ROUND_CLOSEST(symbol_rate * 16384, 1000); + if ((reg_value == 0) || (reg_value > 0xFFFFF)) { + dev_err(&priv->i2c->dev, + "%s(): reg_value is out of range\n", __func__); + return -EINVAL; + } + data[0] = (u8)((reg_value >> 16) & 0x0F); + data[1] = (u8)((reg_value >> 8) & 0xFF); + data[2] = (u8)(reg_value & 0xFF); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + cxd2841er_write_regs(priv, I2C_SLVT, 0x20, data, 3); + return 0; +} + +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, + u8 system); + +static int cxd2841er_sleep_s_to_active_s(struct cxd2841er_priv *priv, + u8 system, u32 symbol_rate) +{ + int ret; + u8 data[4] = { 0, 0, 0, 0 }; + + if (priv->state != STATE_SLEEP_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, (int)priv->state); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBS); + /* Set demod mode */ + if (system == SYS_DVBS) { + data[0] = 0x0A; + } else if (system == SYS_DVBS2) { + data[0] = 0x0B; + } else { + dev_err(&priv->i2c->dev, "%s(): invalid delsys %d\n", + __func__, system); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, data[0]); + /* DVB-S/S2 */ + data[0] = 0x00; + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable S/S2 auto detection 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, data[0]); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + /* Enable S/S2 auto detection 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, data[0]); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x01); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + /* Enable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x3f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Enable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0xA3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa3); + cxd2841er_write_reg(priv, I2C_SLVT, 0xac, 0x00); + data[0] = 0x07; + data[1] = 0x3B; + data[2] = 0x08; + data[3] = 0xC5; + /* Set SLV-T Bank : 0xAB */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xab); + cxd2841er_write_regs(priv, I2C_SLVT, 0x98, data, 4); + data[0] = 0x05; + data[1] = 0x80; + data[2] = 0x0A; + data[3] = 0x80; + cxd2841er_write_regs(priv, I2C_SLVT, 0xa8, data, 4); + data[0] = 0x0C; + data[1] = 0xCC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xc3, data, 2); + /* Set demod parameter */ + ret = cxd2841er_dvbs2_set_symbol_rate(priv, symbol_rate); + if (ret != 0) + return ret; + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x10); + /* disable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_S; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv); + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv); + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv); + +static int cxd2841er_sleep_tc(struct dvb_frontend *fe); + +static int cxd2841er_retune_active(struct cxd2841er_priv *priv, + struct dtv_frontend_properties *p) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_S && + priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + if (priv->state == STATE_ACTIVE_S) + return cxd2841er_dvbs2_set_symbol_rate( + priv, p->symbol_rate / 1000); + else if (priv->state == STATE_ACTIVE_TC) { + switch (priv->system) { + case SYS_DVBT: + return cxd2841er_sleep_tc_to_active_t_band( + priv, p->bandwidth_hz); + case SYS_DVBT2: + return cxd2841er_sleep_tc_to_active_t2_band( + priv, p->bandwidth_hz); + case SYS_DVBC_ANNEX_A: + return cxd2841er_sleep_tc_to_active_c_band( + priv, p->bandwidth_hz); + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + cxd2841er_sleep_tc_to_shutdown(priv); + cxd2841er_shutdown_to_sleep_tc(priv); + return cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); + } + } + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; +} + +static int cxd2841er_active_s_to_sleep_s(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x00); + /* disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + /* disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + /* SADC Bias ON */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + /* disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + /* disable S/S2 auto detection1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable S/S2 auto detection2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, 0x00); + priv->state = STATE_SLEEP_S; + return 0; +} + +static int cxd2841er_sleep_s_to_shutdown(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable DSQOUT */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* Disable DSQIN */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9c, 0x00); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Disable oscillator */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + priv->state = STATE_SHUTDOWN; + return 0; +} + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SLEEP_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Disable oscillator */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + priv->state = STATE_SHUTDOWN; + return 0; +} + +static int cxd2841er_active_t_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_t2_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Cancel DVB-T2 setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x40); + cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x21); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xfb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x00, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x00, 0x3f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Cancel DVB-C setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa3, 0x00, 0x1f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + + /* TODO: Cancel demod parameter */ + + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SHUTDOWN) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Clear all demodulator registers */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00); + usleep_range(3000, 5000); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); + + switch (priv->xtal) { + case SONY_XTAL_20500: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x03); + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x01); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid demod xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a); + /* Clear demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); + usleep_range(1000, 2000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* enable DSQOUT */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1F); + /* enable DSQIN */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9C, 0x40); + /* TADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* SADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + priv->state = STATE_SLEEP_S; + return 0; +} + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SHUTDOWN) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Clear all demodulator registers */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00); + usleep_range(3000, 5000); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); + /* Select ADC clock mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + data = 0x0; + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x3; + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x1; + break; + } + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, data); + /* Clear demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); + usleep_range(1000, 2000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* SADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_tune_done(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); + /* SW Reset */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xfe, 0x01); + /* Enable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x00); + return 0; +} + +/* Set TS parallel mode */ +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, + u8 system) +{ + u8 serial_ts, ts_rate_ctrl_off, ts_in_off; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_read_reg(priv, I2C_SLVT, 0xc4, &serial_ts); + cxd2841er_read_reg(priv, I2C_SLVT, 0xd3, &ts_rate_ctrl_off); + cxd2841er_read_reg(priv, I2C_SLVT, 0xde, &ts_in_off); + dev_dbg(&priv->i2c->dev, "%s(): ser_ts=0x%02x rate_ctrl_off=0x%02x in_off=0x%02x\n", + __func__, serial_ts, ts_rate_ctrl_off, ts_in_off); + + /* + * slave Bank Addr Bit default Name + * <SLV-T> 00h C4h [1:0] 2'b?? OSERCKMODE + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, + ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03); + /* + * slave Bank Addr Bit default Name + * <SLV-T> 00h D1h [1:0] 2'b?? OSERDUTYMODE + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd1, + ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03); + /* + * slave Bank Addr Bit default Name + * <SLV-T> 00h D9h [7:0] 8'h08 OTSCKPERIOD + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08); + /* + * Disable TS IF Clock + * slave Bank Addr Bit default Name + * <SLV-T> 00h 32h [0] 1'b1 OREG_CK_TSIF_EN + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x00, 0x01); + /* + * slave Bank Addr Bit default Name + * <SLV-T> 00h 33h [1:0] 2'b01 OREG_CKSEL_TSIF + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33, + ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03); + /* + * Enable TS IF Clock + * slave Bank Addr Bit default Name + * <SLV-T> 00h 32h [0] 1'b1 OREG_CK_TSIF_EN + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x01, 0x01); + + if (system == SYS_DVBT) { + /* Enable parity period for DVB-T */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01); + } else if (system == SYS_DVBC_ANNEX_A) { + /* Enable parity period for DVB-C */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01); + } +} + +static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv) +{ + u8 chip_id = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (cxd2841er_write_reg(priv, I2C_SLVT, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + else if (cxd2841er_write_reg(priv, I2C_SLVX, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVX, 0xfd, &chip_id); + + return chip_id; +} + +static int cxd2841er_read_status_s(struct dvb_frontend *fe, + enum fe_status *status) +{ + u8 reg = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + *status = 0; + if (priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * <SLV-T> A0h 11h [2] ITSLOCK + */ + cxd2841er_read_reg(priv, I2C_SLVT, 0x11, ®); + if (reg & 0x04) { + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC + | FE_HAS_LOCK; + } + dev_dbg(&priv->i2c->dev, "%s(): result 0x%x\n", __func__, *status); + return 0; +} + +static int cxd2841er_read_status_t_t2(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + if (priv->system == SYS_DVBT) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + } else { + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + } + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + if ((data & 0x07) == 0x07) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid hardware state detected\n", __func__); + *sync = 0; + *tslock = 0; + *unlock = 0; + } else { + *sync = ((data & 0x07) == 0x6 ? 1 : 0); + *tslock = ((data & 0x20) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + } + return 0; +} + +static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock) +{ + u8 data; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_reg(priv, I2C_SLVT, 0x88, &data); + if ((data & 0x01) == 0) { + *tslock = 0; + } else { + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + *tslock = ((data & 0x20) ? 1 : 0); + } + return 0; +} + +static int cxd2841er_read_status_i(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + dev_dbg(&priv->i2c->dev, + "%s(): lock=0x%x\n", __func__, data); + *sync = ((data & 0x02) ? 1 : 0); + *tslock = ((data & 0x01) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + return 0; +} + +static int cxd2841er_read_status_tc(struct dvb_frontend *fe, + enum fe_status *status) +{ + int ret = 0; + u8 sync = 0; + u8 tslock = 0; + u8 unlock = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + *status = 0; + if (priv->state == STATE_ACTIVE_TC) { + if (priv->system == SYS_DVBT || priv->system == SYS_DVBT2) { + ret = cxd2841er_read_status_t_t2( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_ISDBT) { + ret = cxd2841er_read_status_i( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_DVBC_ANNEX_A) { + ret = cxd2841er_read_status_c(priv, &tslock); + if (ret) + goto done; + if (tslock) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + } +done: + dev_dbg(&priv->i2c->dev, "%s(): status 0x%x\n", __func__, *status); + return ret; +} + +static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, + int *offset) +{ + u8 data[3]; + u8 is_hs_mode; + s32 cfrl_ctrlval; + s32 temp_div, temp_q, temp_r; + + if (priv->state != STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* + * Get High Sampling Rate mode + * slave Bank Addr Bit Signal name + * <SLV-T> A0h 10h [0] ITRL_LOCK + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data[0]); + if (data[0] & 0x01) { + /* + * slave Bank Addr Bit Signal name + * <SLV-T> A0h 50h [4] IHSMODE + */ + cxd2841er_read_reg(priv, I2C_SLVT, 0x50, &data[0]); + is_hs_mode = (data[0] & 0x10 ? 1 : 0); + } else { + dev_dbg(&priv->i2c->dev, + "%s(): unable to detect sampling rate mode\n", + __func__); + return -EINVAL; + } + /* + * slave Bank Addr Bit Signal name + * <SLV-T> A0h 45h [4:0] ICFRL_CTRLVAL[20:16] + * <SLV-T> A0h 46h [7:0] ICFRL_CTRLVAL[15:8] + * <SLV-T> A0h 47h [7:0] ICFRL_CTRLVAL[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x45, data, 3); + cfrl_ctrlval = sign_extend32((((u32)data[0] & 0x1F) << 16) | + (((u32)data[1] & 0xFF) << 8) | + ((u32)data[2] & 0xFF), 20); + temp_div = (is_hs_mode ? 1048576 : 1572864); + if (cfrl_ctrlval > 0) { + temp_q = div_s64_rem(97375LL * cfrl_ctrlval, + temp_div, &temp_r); + } else { + temp_q = div_s64_rem(-97375LL * cfrl_ctrlval, + temp_div, &temp_r); + } + if (temp_r >= temp_div / 2) + temp_q++; + if (cfrl_ctrlval > 0) + temp_q *= -1; + *offset = temp_q; + return 0; +} + +static int cxd2841er_get_carrier_offset_i(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_ISDBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + + switch (bandwidth) { + case 6000000: + *offset = -1 * ((*offset) * 8/264); + break; + case 7000000: + *offset = -1 * ((*offset) * 8/231); + break; + case 8000000: + *offset = -1 * ((*offset) * 8/198); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + + dev_dbg(&priv->i2c->dev, "%s(): bandwidth %d offset %d\n", + __func__, bandwidth, *offset); + + return 0; +} + +static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + *offset *= (bandwidth / 1000000); + *offset /= 235; + return 0; +} + +static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT2) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x0F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 27); + switch (bandwidth) { + case 1712000: + *offset /= 582; + break; + case 5000000: + case 6000000: + case 7000000: + case 8000000: + *offset *= (bandwidth / 1000000); + *offset /= 940; + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + return 0; +} + +static int cxd2841er_get_carrier_offset_c(struct cxd2841er_priv *priv, + int *offset) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBC_ANNEX_A) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x15, data, sizeof(data)); + *offset = div_s64(41000LL * sign_extend32((((u32)data[0] & 0x3f) << 8) + | (u32)data[1], 13), 16384); + return 0; +} + +static int cxd2841er_read_packet_errors_c( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data)); + if (data[2] & 0x01) + *penum = ((u32)data[0] << 8) | (u32)data[1]; + return 0; +} + +static int cxd2841er_read_packet_errors_t( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data)); + if (data[2] & 0x01) + *penum = ((u32)data[0] << 8) | (u32)data[1]; + return 0; +} + +static int cxd2841er_read_packet_errors_t2( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + cxd2841er_read_regs(priv, I2C_SLVT, 0xfd, data, sizeof(data)); + if (data[0] & 0x01) + *penum = ((u32)data[1] << 8) | (u32)data[2]; + return 0; +} + +static int cxd2841er_read_packet_errors_i( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[2]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0xA1, data, 1); + + if (!(data[0] & 0x01)) + return 0; + + /* Layer A */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA2, data, sizeof(data)); + *penum = ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer B */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA4, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer C */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA6, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + return 0; +} + +static int cxd2841er_read_ber_c(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[3]; + u32 bit_err, period_exp; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x62, data, sizeof(data)); + if (!(data[0] & 0x80)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return -EINVAL; + } + bit_err = ((u32)(data[0] & 0x3f) << 16) | + ((u32)data[1] << 8) | + (u32)data[2]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x60, data); + period_exp = data[0] & 0x1f; + + if ((period_exp <= 11) && (bit_err > (1 << period_exp) * 204 * 8)) { + dev_dbg(&priv->i2c->dev, + "%s(): period_exp(%u) or bit_err(%u) not in range. no valid BER data\n", + __func__, period_exp, bit_err); + return -EINVAL; + } + + dev_dbg(&priv->i2c->dev, + "%s(): period_exp(%u) or bit_err(%u) count=%d\n", + __func__, period_exp, bit_err, + ((1 << period_exp) * 204 * 8)); + + *bit_error = bit_err; + *bit_count = ((1 << period_exp) * 204 * 8); + + return 0; +} + +static int cxd2841er_read_ber_i(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[3]; + u8 pktnum[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + + cxd2841er_freeze_regs(priv); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x5B, pktnum, sizeof(pktnum)); + cxd2841er_read_regs(priv, I2C_SLVT, 0x16, data, sizeof(data)); + cxd2841er_unfreeze_regs(priv); + + if (!pktnum[0] && !pktnum[1]) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return -EINVAL; + } + + *bit_error = ((u32)(data[0] & 0x7F) << 16) | + ((u32)data[1] << 8) | data[2]; + *bit_count = ((((u32)pktnum[0] << 8) | pktnum[1]) * 204 * 8); + dev_dbg(&priv->i2c->dev, "%s(): bit_error=%u bit_count=%u\n", + __func__, *bit_error, *bit_count); + + return 0; +} + +static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[11]; + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * <SLV-T> A0h 35h [0] IFVBER_VALID + * <SLV-T> A0h 36h [5:0] IFVBER_BITERR[21:16] + * <SLV-T> A0h 37h [7:0] IFVBER_BITERR[15:8] + * <SLV-T> A0h 38h [7:0] IFVBER_BITERR[7:0] + * <SLV-T> A0h 3Dh [5:0] IFVBER_BITNUM[21:16] + * <SLV-T> A0h 3Eh [7:0] IFVBER_BITNUM[15:8] + * <SLV-T> A0h 3Fh [7:0] IFVBER_BITNUM[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11); + if (data[0] & 0x01) { + *bit_error = ((u32)(data[1] & 0x3F) << 16) | + ((u32)(data[2] & 0xFF) << 8) | + (u32)(data[3] & 0xFF); + *bit_count = ((u32)(data[8] & 0x3F) << 16) | + ((u32)(data[9] & 0xFF) << 8) | + (u32)(data[10] & 0xFF); + if ((*bit_count == 0) || (*bit_error > *bit_count)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid bit_error %d, bit_count %d\n", + __func__, *bit_error, *bit_count); + return -EINVAL; + } + return 0; + } + dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); + return -EINVAL; +} + + +static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[5]; + u32 period; + + /* Set SLV-T Bank : 0xB2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2); + /* + * slave Bank Addr Bit Signal name + * <SLV-T> B2h 30h [0] IFLBER_VALID + * <SLV-T> B2h 31h [3:0] IFLBER_BITERR[27:24] + * <SLV-T> B2h 32h [7:0] IFLBER_BITERR[23:16] + * <SLV-T> B2h 33h [7:0] IFLBER_BITERR[15:8] + * <SLV-T> B2h 34h [7:0] IFLBER_BITERR[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5); + if (data[0] & 0x01) { + /* Bit error count */ + *bit_error = ((u32)(data[1] & 0x0F) << 24) | + ((u32)(data[2] & 0xFF) << 16) | + ((u32)(data[3] & 0xFF) << 8) | + (u32)(data[4] & 0xFF); + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_read_reg(priv, I2C_SLVT, 0x7a, data); + /* Measurement period */ + period = (u32)(1 << (data[0] & 0x0F)); + if (period == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): period is 0\n", __func__); + return -EINVAL; + } + if (*bit_error > (period * 64800)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid bit_err 0x%x period 0x%x\n", + __func__, *bit_error, period); + return -EINVAL; + } + *bit_count = period * 64800; + + return 0; + } else { + dev_dbg(&priv->i2c->dev, + "%s(): no data available\n", __func__); + } + return -EINVAL; +} + +static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[4]; + u32 period_exp, n_ldpc; + + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x39, data, sizeof(data)); + if (!(data[0] & 0x10)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return -EINVAL; + } + *bit_error = ((u32)(data[0] & 0x0f) << 24) | + ((u32)data[1] << 16) | + ((u32)data[2] << 8) | + (u32)data[3]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); + period_exp = data[0] & 0x0f; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22); + cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data); + n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800); + if (*bit_error > ((1U << period_exp) * n_ldpc)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid BER value\n", __func__); + return -EINVAL; + } + + /* + * FIXME: the right thing would be to return bit_error untouched, + * but, as we don't know the scale returned by the counters, let's + * at least preserver BER = bit_error/bit_count. + */ + if (period_exp >= 4) { + *bit_count = (1U << (period_exp - 4)) * (n_ldpc / 200); + *bit_error *= 3125ULL; + } else { + *bit_count = (1U << period_exp) * (n_ldpc / 200); + *bit_error *= 50000ULL; + } + return 0; +} + +static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[2]; + u32 period; + + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_reg(priv, I2C_SLVT, 0x39, data); + if (!(data[0] & 0x01)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return 0; + } + cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data)); + *bit_error = ((u32)data[0] << 8) | (u32)data[1]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); + period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07)); + + /* + * FIXME: the right thing would be to return bit_error untouched, + * but, as we don't know the scale returned by the counters, let's + * at least preserver BER = bit_error/bit_count. + */ + *bit_count = period / 128; + *bit_error *= 78125ULL; + return 0; +} + +static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv) +{ + /* + * Freeze registers: ensure multiple separate register reads + * are from the same snapshot + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); + return 0; +} + +static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv) +{ + /* + * un-freeze registers + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x00); + return 0; +} + +static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, + u8 delsys, u32 *snr) +{ + u8 data[3]; + u32 res = 0, value; + int min_index, max_index, index; + static const struct cxd2841er_cnr_data *cn_data; + + cxd2841er_freeze_regs(priv); + /* Set SLV-T Bank : 0xA1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1); + /* + * slave Bank Addr Bit Signal name + * <SLV-T> A1h 10h [0] ICPM_QUICKRDY + * <SLV-T> A1h 11h [4:0] ICPM_QUICKCNDT[12:8] + * <SLV-T> A1h 12h [7:0] ICPM_QUICKCNDT[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3); + cxd2841er_unfreeze_regs(priv); + + if (data[0] & 0x01) { + value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF); + min_index = 0; + if (delsys == SYS_DVBS) { + cn_data = s_cn_data; + max_index = ARRAY_SIZE(s_cn_data) - 1; + } else { + cn_data = s2_cn_data; + max_index = ARRAY_SIZE(s2_cn_data) - 1; + } + if (value >= cn_data[min_index].value) { + res = cn_data[min_index].cnr_x1000; + goto done; + } + if (value <= cn_data[max_index].value) { + res = cn_data[max_index].cnr_x1000; + goto done; + } + while ((max_index - min_index) > 1) { + index = (max_index + min_index) / 2; + if (value == cn_data[index].value) { + res = cn_data[index].cnr_x1000; + goto done; + } else if (value > cn_data[index].value) + max_index = index; + else + min_index = index; + if ((max_index - min_index) <= 1) { + if (value == cn_data[max_index].value) { + res = cn_data[max_index].cnr_x1000; + goto done; + } else { + res = cn_data[min_index].cnr_x1000; + goto done; + } + } + } + } else { + dev_dbg(&priv->i2c->dev, + "%s(): no data available\n", __func__); + return -EINVAL; + } +done: + *snr = res; + return 0; +} + +static uint32_t sony_log(uint32_t x) +{ + return (((10000>>8)*(intlog2(x)>>16) + LOG2_E_100X/2)/LOG2_E_100X); +} + +static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + enum sony_dvbc_constellation_t qam = SONY_DVBC_CONSTELLATION_16QAM; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + + cxd2841er_freeze_regs(priv); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x19, data, 1); + qam = (enum sony_dvbc_constellation_t) (data[0] & 0x07); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4C, data, 2); + cxd2841er_unfreeze_regs(priv); + + reg = ((u32)(data[0]&0x1f) << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + + switch (qam) { + case SONY_DVBC_CONSTELLATION_16QAM: + case SONY_DVBC_CONSTELLATION_64QAM: + case SONY_DVBC_CONSTELLATION_256QAM: + /* SNR(dB) = -9.50 * ln(IREG_SNR_ESTIMATE / (24320)) */ + if (reg < 126) + reg = 126; + *snr = -95 * (int32_t)sony_log(reg) + 95941; + break; + case SONY_DVBC_CONSTELLATION_32QAM: + case SONY_DVBC_CONSTELLATION_128QAM: + /* SNR(dB) = -8.75 * ln(IREG_SNR_ESTIMATE / (20800)) */ + if (reg < 69) + reg = 69; + *snr = -88 * (int32_t)sony_log(reg) + 86999; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + + cxd2841er_freeze_regs(priv); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + cxd2841er_unfreeze_regs(priv); + + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 4996) + reg = 4996; + *snr = 100 * ((INTLOG10X100(reg) - INTLOG10X100(5350 - reg)) + 285); + return 0; +} + +static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + + cxd2841er_freeze_regs(priv); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + cxd2841er_unfreeze_regs(priv); + + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 10876) + reg = 10876; + *snr = 100 * ((INTLOG10X100(reg) - INTLOG10X100(12600 - reg)) + 320); + return 0; +} + +static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, + priv->state); + return -EINVAL; + } + + cxd2841er_freeze_regs(priv); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + cxd2841er_unfreeze_regs(priv); + + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + *snr = 10000 * (intlog10(reg) >> 24) - 9031; + return 0; +} + +static u16 cxd2841er_read_agc_gain_c(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x49, data, 2); + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20)); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_i(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv) +{ + u8 data[2]; + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * <SLV-T> A0h 1Fh [4:0] IRFAGC_GAIN[12:8] + * <SLV-T> A0h 20h [7:0] IRFAGC_GAIN[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x1f, data, 2); + return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3; +} + +static void cxd2841er_read_ber(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + u32 ret, bit_error = 0, bit_count = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + ret = cxd2841er_read_ber_c(priv, &bit_error, &bit_count); + break; + case SYS_ISDBT: + ret = cxd2841er_read_ber_i(priv, &bit_error, &bit_count); + break; + case SYS_DVBS: + ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count); + break; + case SYS_DVBS2: + ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count); + break; + case SYS_DVBT: + ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count); + break; + case SYS_DVBT2: + ret = cxd2841er_read_ber_t2(priv, &bit_error, &bit_count); + break; + default: + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + if (!ret) { + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue += bit_error; + p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_count.stat[0].uvalue += bit_count; + } else { + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } +} + +static void cxd2841er_read_signal_strength(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + s32 strength; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + strength = cxd2841er_read_agc_gain_t_t2(priv, + p->delivery_system); + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + /* Formula was empirically determinated @ 410 MHz */ + p->strength.stat[0].uvalue = strength * 366 / 100 - 89520; + break; /* Code moved out of the function */ + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + strength = cxd2841er_read_agc_gain_c(priv, + p->delivery_system); + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + /* + * Formula was empirically determinated via linear regression, + * using frequencies: 175 MHz, 410 MHz and 800 MHz, and a + * stream modulated with QAM64 + */ + p->strength.stat[0].uvalue = strength * 4045 / 1000 - 85224; + break; + case SYS_ISDBT: + strength = cxd2841er_read_agc_gain_i(priv, p->delivery_system); + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + /* + * Formula was empirically determinated via linear regression, + * using frequencies: 175 MHz, 410 MHz and 800 MHz. + */ + p->strength.stat[0].uvalue = strength * 3775 / 1000 - 90185; + break; + case SYS_DVBS: + case SYS_DVBS2: + strength = 65535 - cxd2841er_read_agc_gain_s(priv); + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->strength.stat[0].uvalue = strength; + break; + default: + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + break; + } +} + +static void cxd2841er_read_snr(struct dvb_frontend *fe) +{ + u32 tmp = 0; + int ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + ret = cxd2841er_read_snr_c(priv, &tmp); + break; + case SYS_DVBT: + ret = cxd2841er_read_snr_t(priv, &tmp); + break; + case SYS_DVBT2: + ret = cxd2841er_read_snr_t2(priv, &tmp); + break; + case SYS_ISDBT: + ret = cxd2841er_read_snr_i(priv, &tmp); + break; + case SYS_DVBS: + case SYS_DVBS2: + ret = cxd2841er_dvbs_read_snr(priv, p->delivery_system, &tmp); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n", + __func__, p->delivery_system); + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + dev_dbg(&priv->i2c->dev, "%s(): snr=%d\n", + __func__, (int32_t)tmp); + + if (!ret) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = tmp; + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } +} + +static void cxd2841er_read_ucblocks(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + u32 ucblocks = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_B: + case SYS_DVBC_ANNEX_C: + cxd2841er_read_packet_errors_c(priv, &ucblocks); + break; + case SYS_DVBT: + cxd2841er_read_packet_errors_t(priv, &ucblocks); + break; + case SYS_DVBT2: + cxd2841er_read_packet_errors_t2(priv, &ucblocks); + break; + case SYS_ISDBT: + cxd2841er_read_packet_errors_i(priv, &ucblocks); + break; + default: + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + dev_dbg(&priv->i2c->dev, "%s() ucblocks=%u\n", __func__, ucblocks); + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue = ucblocks; +} + +static int cxd2841er_dvbt2_set_profile( + struct cxd2841er_priv *priv, enum cxd2841er_dvbt2_profile_t profile) +{ + u8 tune_mode; + u8 seq_not2d_time; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (profile) { + case DVBT2_PROFILE_BASE: + tune_mode = 0x01; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x0E:0x0C; + break; + case DVBT2_PROFILE_LITE: + tune_mode = 0x05; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; + break; + case DVBT2_PROFILE_ANY: + tune_mode = 0x00; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; + break; + default: + return -EINVAL; + } + /* Set SLV-T Bank : 0x2E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2e); + /* Set profile and tune mode */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x10, tune_mode, 0x07); + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + /* Set early unlock detection time */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9d, seq_not2d_time); + return 0; +} + +static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv, + u8 is_auto, u8 plp_id) +{ + if (is_auto) { + dev_dbg(&priv->i2c->dev, + "%s() using auto PLP selection\n", __func__); + } else { + dev_dbg(&priv->i2c->dev, + "%s() using manual PLP selection, ID %d\n", + __func__, plp_id); + } + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + if (!is_auto) { + /* Manual PLP selection mode. Set the data PLP Id. */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xaf, plp_id); + } + /* Auto PLP select (Scanning mode = 0x00). Data PLP select = 0x01. */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xad, (is_auto ? 0x00 : 0x01)); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u32 iffreq, ifhz; + u8 data[MAX_WRITE_REGSIZE]; + + static const uint8_t nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + static const uint8_t nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + static const uint8_t nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + + static const uint8_t nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; + + static const uint8_t nominalRate17bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC}, /* 20.5MHz XTal */ + {0x68, 0x0F, 0xA2, 0x32, 0xD0}, /* 24MHz XTal */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC} /* 41MHz XTal */ + }; + + static const uint8_t itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, + 0x29, 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + + static const uint8_t itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, + 0x29, 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + + static const uint8_t itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + static const uint8_t itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + static const uint8_t itbCoef17bw[3][14] = { + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}, /* 20.5MHz XTal */ + {0x33, 0x8E, 0x2B, 0x97, 0x2D, 0x95, 0x37, 0x8B, + 0x30, 0x97, 0x2D, 0x9A, 0x21, 0xA4}, /* 24MHz XTal */ + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + + switch (bandwidth) { + case 8000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 4800000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); + break; + case 7000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 4200000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); + break; + case 6000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 3600000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); + break; + case 5000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 3600000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); + break; + case 1712000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate17bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x03, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef17bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 3500000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x03, 0x07); + break; + default: + return -EINVAL; + } + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 iffreq, ifhz; + static const u8 nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + static const u8 nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + static const u8 nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + static const u8 nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; + + static const u8 itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, 0xA5, + 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + static const u8 itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, 0xA2, + 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + static const u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + static const u8 itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x13 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + /* Echo performance optimization setting */ + data[0] = 0x01; + data[1] = 0x14; + cxd2841er_write_regs(priv, I2C_SLVT, 0x9C, data, 2); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + switch (bandwidth) { + case 8000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 4800000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x15; + data[1] = 0x28; + } else { + data[0] = 0x01; + data[1] = 0xE0; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x01; + data[1] = 0x02; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + case 7000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 4200000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0xF8; + } else { + data[0] = 0x12; + data[1] = 0xF8; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + case 6000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 3600000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x25; + data[1] = 0x4C; + } else { + data[0] = 0x1F; + data[1] = 0xDC; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + case 5000000: + /* <Timing Recovery setting> */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* <IF freq setting> */ + ifhz = cxd2841er_get_if_hz(priv, 3600000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x2C; + data[1] = 0xC2; + } else { + data[0] = 0x26; + data[1] = 0x3C; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + } + + return 0; +} + +static int cxd2841er_sleep_tc_to_active_i_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u32 iffreq, ifhz; + u8 data[3]; + + /* TRCG Nominal Rate */ + static const u8 nominalRate8bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x11, 0xB8, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + static const u8 nominalRate7bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x14, 0x40, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + static const u8 nominalRate6bw[3][5] = { + {0x14, 0x2E, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x17, 0xA0, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x2E, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + static const u8 itbCoef8bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, + 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz Xtal */ + {0x0}, /* 41MHz XTal */ + }; + + static const u8 itbCoef7bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, + 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz Xtal */ + {0x00}, /* 41MHz XTal */ + }; + + static const u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, + 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz Xtal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 41MHz XTal */ + }; + + dev_dbg(&priv->i2c->dev, "%s() bandwidth=%u\n", __func__, bandwidth); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* 20.5/41MHz Xtal support is not available + * on ISDB-T 7MHzBW and 8MHzBW + */ + if (priv->xtal != SONY_XTAL_24000 && bandwidth > 6000000) { + dev_err(&priv->i2c->dev, + "%s(): bandwidth %d supported only for 24MHz xtal\n", + __func__, bandwidth); + return -EINVAL; + } + + switch (bandwidth) { + case 8000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + + /* IF freq setting */ + ifhz = cxd2841er_get_if_hz(priv, 4750000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x0, 0x7); + + /* Demod core latency setting */ + data[0] = 0x13; + data[1] = 0xFC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x03); + break; + case 7000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + + /* IF freq setting */ + ifhz = cxd2841er_get_if_hz(priv, 4150000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x02, 0x7); + + /* Demod core latency setting */ + data[0] = 0x1A; + data[1] = 0xFA; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); + break; + case 6000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + + /* IF freq setting */ + ifhz = cxd2841er_get_if_hz(priv, 3550000); + iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x04, 0x7); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0x79; + } else { + data[0] = 0x1A; + data[1] = 0xE2; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x07, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + return 0; +} + +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 bw7_8mhz_b10_a6[] = { + 0x2D, 0xC7, 0x04, 0xF4, 0x07, 0xC5, 0x2A, 0xB8, + 0x27, 0x9E, 0x27, 0xA4, 0x29, 0xAB }; + u8 bw6mhz_b10_a6[] = { + 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 b10_b6[3]; + u32 iffreq, ifhz; + + if (bandwidth != 6000000 && + bandwidth != 7000000 && + bandwidth != 8000000) { + dev_info(&priv->i2c->dev, "%s(): unsupported bandwidth %d. Forcing 8Mhz!\n", + __func__, bandwidth); + bandwidth = 8000000; + } + + dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + switch (bandwidth) { + case 8000000: + case 7000000: + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, + bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6)); + ifhz = cxd2841er_get_if_hz(priv, 4900000); + iffreq = cxd2841er_calc_iffreq(ifhz); + break; + case 6000000: + if (priv->flags & CXD2841ER_ASCOT) + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, + bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6)); + ifhz = cxd2841er_get_if_hz(priv, 3700000); + iffreq = cxd2841er_calc_iffreq(ifhz); + break; + default: + dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + /* <IF freq setting> */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + switch (bandwidth) { + case 8000000: + case 7000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xa3, 0x00, 0x1f); + break; + case 6000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xa3, 0x14, 0x1f); + break; + } + /* Set SLV-T Bank : 0x40 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + switch (bandwidth) { + case 8000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x0b, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x3e); + break; + case 7000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0xd6); + break; + case 6000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x08, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x6e); + break; + } + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + u8 data24m[3] = {0xDC, 0x6C, 0x00}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* Enable ADC 2 & 3 */ + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* ASCOT setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, + ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01); + /* Set SLV-T Bank : 0x18 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + /* Pre-RS BER monitor setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x01, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBF, 0x60); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data24m, 3); + } + + cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[MAX_WRITE_REGSIZE]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x02); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00); + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } else { + data[0] = 0x09; + data[1] = 0x54; + } + + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* ASCOT setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, + ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01); + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x8b, 0x3c); + /* Set SLV-T Bank : 0x2b */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70); + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + /* L1 Control setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE6, 0x00, 0x03); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + /* DVB-T2 initial setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x34); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xd8); + /* Set SLV-T Bank : 0x2a */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x04, 0x0f); + /* Set SLV-T Bank : 0x2b */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f); + + /* 24MHz Xtal setting */ + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + data[0] = 0xEB; + data[1] = 0x03; + data[2] = 0x3B; + cxd2841er_write_regs(priv, I2C_SLVT, 0x33, data, 3); + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + data[0] = 0x5E; + data[1] = 0x5E; + data[2] = 0x47; + cxd2841er_write_regs(priv, I2C_SLVT, 0x95, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0x99, 0x18); + + data[0] = 0x3F; + data[1] = 0xFF; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Set SLV-T Bank : 0x24 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + data[0] = 0x0B; + data[1] = 0x72; + cxd2841er_write_regs(priv, I2C_SLVT, 0x34, data, 2); + + data[0] = 0x93; + data[1] = 0xF3; + data[2] = 0x00; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD2, data, 3); + + data[0] = 0x05; + data[1] = 0xB8; + data[2] = 0xD8; + cxd2841er_write_regs(priv, I2C_SLVT, 0xDD, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0xE0, 0x00); + + /* Set SLV-T Bank : 0x25 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x25); + cxd2841er_write_reg(priv, I2C_SLVT, 0xED, 0x60); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0xFA, 0x34); + + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2B); + cxd2841er_write_reg(priv, I2C_SLVT, 0x4B, 0x2F); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9E, 0x0E); + + /* Set SLV-T Bank : 0x2D */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2D); + data[0] = 0x89; + data[1] = 0x89; + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data, 2); + + /* Set SLV-T Bank : 0x5E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x5E); + data[0] = 0x24; + data[1] = 0x95; + cxd2841er_write_regs(priv, I2C_SLVT, 0x8C, data, 2); + } + + cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth); + + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +/* ISDB-Tb part */ +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + u8 data24m[2] = {0x60, 0x00}; + u8 data24m2[3] = {0xB7, 0x1B, 0x00}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x06); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz or 24M */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* ASCOT setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, + ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01); + /* ISDB-T initial setting */ + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x00, 0x01); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x69, 0x04, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x6B, 0x03, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9D, 0x50, 0xFF); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xD3, 0x06, 0x1F); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xED, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE2, 0xCE, 0x80); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xF2, 0x13, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x2E, 0x3F); + /* Set SLV-T Bank : 0x15 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x02, 0x03); + /* Set SLV-T Bank : 0x1E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x1E); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x73, 0x68, 0xFF); + /* Set SLV-T Bank : 0x63 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x63); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x81, 0x00, 0x01); + + /* for xtal 24MHz */ + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_regs(priv, I2C_SLVT, 0xBF, data24m, 2); + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_write_regs(priv, I2C_SLVT, 0xA8, data24m2, 3); + + cxd2841er_sleep_tc_to_active_i_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBC_ANNEX_A); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x04); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00); + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x09, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x48); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* ASCOT setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, + ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01); + /* Set SLV-T Bank : 0x40 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + /* Demod setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc3, 0x00, 0x04); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + cxd2841er_sleep_tc_to_active_c_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + enum fe_status status = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE_S) + cxd2841er_read_status_s(fe, &status); + else if (priv->state == STATE_ACTIVE_TC) + cxd2841er_read_status_tc(fe, &status); + + if (priv->state == STATE_ACTIVE_TC || priv->state == STATE_ACTIVE_S) + cxd2841er_read_signal_strength(fe); + else + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (status & FE_HAS_LOCK) { + if (priv->stats_time && + (!time_after(jiffies, priv->stats_time))) + return 0; + + /* Prevent retrieving stats faster than once per second */ + priv->stats_time = jiffies + msecs_to_jiffies(1000); + + cxd2841er_read_snr(fe); + cxd2841er_read_ucblocks(fe); + cxd2841er_read_ber(fe); + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + return 0; +} + +static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) +{ + int ret = 0, i, timeout, carr_offset; + enum fe_status status; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d xtal=%d\n", + __func__, + (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"), + p->frequency, symbol_rate, priv->xtal); + + if (priv->flags & CXD2841ER_EARLY_TUNE) + cxd2841er_tuner_set(fe); + + switch (priv->state) { + case STATE_SLEEP_S: + ret = cxd2841er_sleep_s_to_active_s( + priv, p->delivery_system, symbol_rate); + break; + case STATE_ACTIVE_S: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + goto done; + } + if (ret) { + dev_dbg(&priv->i2c->dev, "%s(): tune failed\n", __func__); + goto done; + } + + if (!(priv->flags & CXD2841ER_EARLY_TUNE)) + cxd2841er_tuner_set(fe); + + cxd2841er_tune_done(priv); + timeout = DIV_ROUND_UP(3000000, symbol_rate) + 150; + + i = 0; + do { + usleep_range(CXD2841ER_DVBS_POLLING_INVL*1000, + (CXD2841ER_DVBS_POLLING_INVL + 2) * 1000); + cxd2841er_read_status_s(fe, &status); + if (status & FE_HAS_LOCK) + break; + i++; + } while (i < timeout / CXD2841ER_DVBS_POLLING_INVL); + + if (status & FE_HAS_LOCK) { + if (cxd2841er_get_carrier_offset_s_s2( + priv, &carr_offset)) { + ret = -EINVAL; + goto done; + } + dev_dbg(&priv->i2c->dev, "%s(): carrier_offset=%d\n", + __func__, carr_offset); + } +done: + /* Reset stats */ + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* Reset the wait for jiffies logic */ + priv->stats_time = 0; + + return ret; +} + +static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) +{ + int ret = 0, timeout; + enum fe_status status; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n", + __func__, p->delivery_system, p->bandwidth_hz); + + if (priv->flags & CXD2841ER_EARLY_TUNE) + cxd2841er_tuner_set(fe); + + /* deconfigure/put demod to sleep on delsys switch if active */ + if (priv->state == STATE_ACTIVE_TC && + priv->system != p->delivery_system) { + dev_dbg(&priv->i2c->dev, "%s(): old_delsys=%d, new_delsys=%d -> sleep\n", + __func__, priv->system, p->delivery_system); + cxd2841er_sleep_tc(fe); + } + + if (p->delivery_system == SYS_DVBT) { + priv->system = SYS_DVBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_t( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_DVBT2) { + priv->system = SYS_DVBT2; + cxd2841er_dvbt2_set_plp_config(priv, + (int)(p->stream_id > 255), p->stream_id); + cxd2841er_dvbt2_set_profile(priv, DVBT2_PROFILE_BASE); + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_t2(priv, + p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_ISDBT) { + priv->system = SYS_ISDBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A || + p->delivery_system == SYS_DVBC_ANNEX_C) { + priv->system = SYS_DVBC_ANNEX_A; + /* correct bandwidth */ + if (p->bandwidth_hz != 6000000 && + p->bandwidth_hz != 7000000 && + p->bandwidth_hz != 8000000) { + p->bandwidth_hz = 8000000; + dev_dbg(&priv->i2c->dev, "%s(): forcing bandwidth to %d\n", + __func__, p->bandwidth_hz); + } + + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_c( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else { + dev_dbg(&priv->i2c->dev, + "%s(): invalid delivery system %d\n", + __func__, p->delivery_system); + ret = -EINVAL; + } + if (ret) + goto done; + + if (!(priv->flags & CXD2841ER_EARLY_TUNE)) + cxd2841er_tuner_set(fe); + + cxd2841er_tune_done(priv); + + if (priv->flags & CXD2841ER_NO_WAIT_LOCK) + goto done; + + timeout = 2500; + while (timeout > 0) { + ret = cxd2841er_read_status_tc(fe, &status); + if (ret) + goto done; + if (status & FE_HAS_LOCK) + break; + msleep(20); + timeout -= 20; + } + if (timeout < 0) + dev_dbg(&priv->i2c->dev, + "%s(): LOCK wait timeout\n", __func__); +done: + return ret; +} + +static int cxd2841er_tune_s(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret, carrier_offset; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s() re_tune=%d\n", __func__, re_tune); + if (re_tune) { + ret = cxd2841er_set_frontend_s(fe); + if (ret) + return ret; + cxd2841er_read_status_s(fe, status); + if (*status & FE_HAS_LOCK) { + if (cxd2841er_get_carrier_offset_s_s2( + priv, &carrier_offset)) + return -EINVAL; + p->frequency += carrier_offset; + ret = cxd2841er_set_frontend_s(fe); + if (ret) + return ret; + } + } + *delay = HZ / 5; + return cxd2841er_read_status_s(fe, status); +} + +static int cxd2841er_tune_tc(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret, carrier_offset; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s(): re_tune %d bandwidth=%d\n", __func__, + re_tune, p->bandwidth_hz); + if (re_tune) { + ret = cxd2841er_set_frontend_tc(fe); + if (ret) + return ret; + cxd2841er_read_status_tc(fe, status); + if (*status & FE_HAS_LOCK) { + switch (priv->system) { + case SYS_ISDBT: + ret = cxd2841er_get_carrier_offset_i( + priv, p->bandwidth_hz, + &carrier_offset); + if (ret) + return ret; + break; + case SYS_DVBT: + ret = cxd2841er_get_carrier_offset_t( + priv, p->bandwidth_hz, + &carrier_offset); + if (ret) + return ret; + break; + case SYS_DVBT2: + ret = cxd2841er_get_carrier_offset_t2( + priv, p->bandwidth_hz, + &carrier_offset); + if (ret) + return ret; + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2841er_get_carrier_offset_c( + priv, &carrier_offset); + if (ret) + return ret; + break; + default: + dev_dbg(&priv->i2c->dev, + "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, "%s(): carrier offset %d\n", + __func__, carrier_offset); + p->frequency += carrier_offset; + ret = cxd2841er_set_frontend_tc(fe); + if (ret) + return ret; + } + } + *delay = HZ / 5; + return cxd2841er_read_status_tc(fe, status); +} + +static int cxd2841er_sleep_s(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_active_s_to_sleep_s(fe->demodulator_priv); + cxd2841er_sleep_s_to_shutdown(fe->demodulator_priv); + return 0; +} + +static int cxd2841er_sleep_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + + if (priv->state == STATE_ACTIVE_TC) { + switch (priv->system) { + case SYS_DVBT: + cxd2841er_active_t_to_sleep_tc(priv); + break; + case SYS_DVBT2: + cxd2841er_active_t2_to_sleep_tc(priv); + break; + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + break; + case SYS_DVBC_ANNEX_A: + cxd2841er_active_c_to_sleep_tc(priv); + break; + default: + dev_warn(&priv->i2c->dev, + "%s(): unknown delivery system %d\n", + __func__, priv->system); + } + } + if (priv->state != STATE_SLEEP_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + return 0; +} + +static int cxd2841er_shutdown_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + + if (!cxd2841er_sleep_tc(fe)) + cxd2841er_sleep_tc_to_shutdown(priv); + return 0; +} + +static int cxd2841er_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + u8 data; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): burst mode %s\n", __func__, + (burst == SEC_MINI_A ? "A" : "B")); + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + data = (burst == SEC_MINI_A ? 0 : 1); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x34, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x35, data); + return 0; +} + +static int cxd2841er_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + u8 data; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): tone %s\n", __func__, + (tone == SEC_TONE_ON ? "On" : "Off")); + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + data = (tone == SEC_TONE_ON ? 1 : 0); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x36, data); + return 0; +} + +static int cxd2841er_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + int i; + u8 data[12]; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, + "%s(): cmd->len %d\n", __func__, cmd->msg_len); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + /* DiDEqC enable */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x33, 0x01); + /* cmd1 length & data */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x3d, cmd->msg_len); + memset(data, 0, sizeof(data)); + for (i = 0; i < cmd->msg_len && i < sizeof(data); i++) + data[i] = cmd->msg[i]; + cxd2841er_write_regs(priv, I2C_SLVT, 0x3e, data, sizeof(data)); + /* repeat count for cmd1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x37, 1); + /* repeat count for cmd2: always 0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x38, 0); + /* start transmit */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x32, 0x01); + /* wait for 1 sec timeout */ + for (i = 0; i < 50; i++) { + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, data); + if (!data[0]) { + dev_dbg(&priv->i2c->dev, + "%s(): DiSEqC cmd has been sent\n", __func__); + return 0; + } + msleep(20); + } + dev_dbg(&priv->i2c->dev, + "%s(): DiSEqC cmd transmit timeout\n", __func__); + return -ETIMEDOUT; +} + +static void cxd2841er_release(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(priv); +} + +static int cxd2841er_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): enable=%d\n", __func__, enable); + cxd2841er_set_reg_bits( + priv, I2C_SLVX, 0x8, (enable ? 0x01 : 0x00), 0x01); + return 0; +} + +static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return DVBFE_ALGO_HW; +} + +static void cxd2841er_init_stats(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.len = 1; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; +} + + +static int cxd2841er_init_s(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + /* sanity. force demod to SHUTDOWN state */ + if (priv->state == STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing sleep->shutdown\n", + __func__); + cxd2841er_sleep_s_to_shutdown(priv); + } else if (priv->state == STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing active->sleep->shutdown\n", + __func__); + cxd2841er_active_s_to_sleep_s(priv); + cxd2841er_sleep_s_to_shutdown(priv); + } + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_shutdown_to_sleep_s(priv); + /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01); + + cxd2841er_init_stats(fe); + + return 0; +} + +static int cxd2841er_init_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n", + __func__, p->bandwidth_hz); + cxd2841er_shutdown_to_sleep_tc(priv); + /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 (0 for NO_AGCNEG */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb, + ((priv->flags & CXD2841ER_NO_AGCNEG) ? 0x00 : 0x40), 0x40); + /* SONY_DEMOD_CONFIG_IFAGC_ADC_FS = 0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xcd, 0x50); + /* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, + ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x80 : 0x00), 0x80); + + /* clear TSCFG bits 3+4 */ + if (priv->flags & CXD2841ER_TSBITS) + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x18); + + cxd2841er_init_stats(fe); + + return 0; +} + +static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; +static struct dvb_frontend_ops cxd2841er_t_c_ops; + +static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c, + u8 system) +{ + u8 chip_id = 0; + const char *type; + const char *name; + struct cxd2841er_priv *priv = NULL; + + /* allocate memory for the internal state */ + priv = kzalloc(sizeof(struct cxd2841er_priv), GFP_KERNEL); + if (!priv) + return NULL; + priv->i2c = i2c; + priv->config = cfg; + priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; + priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; + priv->xtal = cfg->xtal; + priv->flags = cfg->flags; + priv->frontend.demodulator_priv = priv; + dev_info(&priv->i2c->dev, + "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n", + __func__, priv->i2c, + priv->i2c_addr_slvx, priv->i2c_addr_slvt); + chip_id = cxd2841er_chip_id(priv); + switch (chip_id) { + case CXD2837ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2837ER DVB-T/T2/C demodulator"); + name = "CXD2837ER"; + type = "C/T/T2"; + break; + case CXD2838ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2838ER ISDB-T demodulator"); + cxd2841er_t_c_ops.delsys[0] = SYS_ISDBT; + cxd2841er_t_c_ops.delsys[1] = SYS_UNDEFINED; + cxd2841er_t_c_ops.delsys[2] = SYS_UNDEFINED; + name = "CXD2838ER"; + type = "ISDB-T"; + break; + case CXD2841ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2841ER DVB-T/T2/C demodulator"); + name = "CXD2841ER"; + type = "T/T2/C/ISDB-T"; + break; + case CXD2843ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2843ER DVB-T/T2/C/C2 demodulator"); + name = "CXD2843ER"; + type = "C/C2/T/T2"; + break; + case CXD2854ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator"); + cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT; + name = "CXD2854ER"; + type = "C/C2/T/T2/ISDB-T"; + break; + default: + dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", + __func__, chip_id); + priv->frontend.demodulator_priv = NULL; + kfree(priv); + return NULL; + } + + /* create dvb_frontend */ + if (system == SYS_DVBS) { + memcpy(&priv->frontend.ops, + &cxd2841er_dvbs_s2_ops, + sizeof(struct dvb_frontend_ops)); + type = "S/S2"; + } else { + memcpy(&priv->frontend.ops, + &cxd2841er_t_c_ops, + sizeof(struct dvb_frontend_ops)); + } + + dev_info(&priv->i2c->dev, + "%s(): attaching %s DVB-%s frontend\n", + __func__, name, type); + dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n", + __func__, chip_id); + return &priv->frontend; +} + +struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBS); +} +EXPORT_SYMBOL_GPL(cxd2841er_attach_s); + +struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, 0); +} +EXPORT_SYMBOL_GPL(cxd2841er_attach_t_c); + +static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Sony CXD2841ER DVB-S/S2 demodulator", + .frequency_min_hz = 500 * MHz, + .frequency_max_hz = 2500 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK, + }, + .init = cxd2841er_init_s, + .sleep = cxd2841er_sleep_s, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_s, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_s, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo, + .set_tone = cxd2841er_set_tone, + .diseqc_send_burst = cxd2841er_send_burst, + .diseqc_send_master_cmd = cxd2841er_send_diseqc_msg, + .tune = cxd2841er_tune_s +}; + +static struct dvb_frontend_ops cxd2841er_t_c_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, + .info = { + .name = "", /* will set in attach function */ + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION, + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 1002 * MHz, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000 + }, + .init = cxd2841er_init_tc, + .sleep = cxd2841er_shutdown_tc, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_tc, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_tc, + .tune = cxd2841er_tune_tc, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo +}; + +MODULE_DESCRIPTION("Sony CXD2837/38/41/43/54ER DVB-C/C2/T/T2/S/S2 demodulator driver"); +MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>, Abylay Ospan <aospan@netup.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h new file mode 100644 index 0000000000..eb5467f581 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2841er.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * cxd2841er.h + * + * Sony CXD2441ER digital demodulator driver public definitions + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#ifndef CXD2841ER_H +#define CXD2841ER_H + +#include <linux/dvb/frontend.h> + +#define CXD2841ER_USE_GATECTRL 1 /* bit 0 */ +#define CXD2841ER_AUTO_IFHZ 2 /* bit 1 */ +#define CXD2841ER_TS_SERIAL 4 /* bit 2 */ +#define CXD2841ER_ASCOT 8 /* bit 3 */ +#define CXD2841ER_EARLY_TUNE 16 /* bit 4 */ +#define CXD2841ER_NO_WAIT_LOCK 32 /* bit 5 */ +#define CXD2841ER_NO_AGCNEG 64 /* bit 6 */ +#define CXD2841ER_TSBITS 128 /* bit 7 */ + +enum cxd2841er_xtal { + SONY_XTAL_20500, /* 20.5 MHz */ + SONY_XTAL_24000, /* 24 MHz */ + SONY_XTAL_41000 /* 41 MHz */ +}; + +struct cxd2841er_config { + u8 i2c_addr; + enum cxd2841er_xtal xtal; + u32 flags; +}; + +#if IS_REACHABLE(CONFIG_DVB_CXD2841ER) +extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); + +extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *cxd2841er_attach_s( + struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *cxd2841er_attach_t_c( + struct cxd2841er_config *cfg, struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif + +#endif diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h new file mode 100644 index 0000000000..e030f456e2 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2841er_priv.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * cxd2841er_priv.h + * + * Sony CXD2441ER digital demodulator driver internal definitions + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#ifndef CXD2841ER_PRIV_H +#define CXD2841ER_PRIV_H + +#define I2C_SLVX 0 +#define I2C_SLVT 1 + +#define CXD2837ER_CHIP_ID 0xb1 +#define CXD2838ER_CHIP_ID 0xb0 +#define CXD2841ER_CHIP_ID 0xa7 +#define CXD2843ER_CHIP_ID 0xa4 +#define CXD2854ER_CHIP_ID 0xc1 + +#define CXD2841ER_DVBS_POLLING_INVL 10 + +struct cxd2841er_cnr_data { + u32 value; + int cnr_x1000; +}; + +enum cxd2841er_dvbt2_profile_t { + DVBT2_PROFILE_ANY = 0, + DVBT2_PROFILE_BASE = 1, + DVBT2_PROFILE_LITE = 2 +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig new file mode 100644 index 0000000000..9d989676e8 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DVB_CXD2880 + tristate "Sony CXD2880 DVB-T2/T tuner + demodulator" + depends on DVB_CORE && SPI + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend.
\ No newline at end of file diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile new file mode 100644 index 0000000000..646598b556 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 + +cxd2880-objs := cxd2880_common.o \ + cxd2880_devio_spi.o \ + cxd2880_integ.o \ + cxd2880_io.o \ + cxd2880_spi_device.o \ + cxd2880_tnrdmd.o \ + cxd2880_tnrdmd_dvbt2.o \ + cxd2880_tnrdmd_dvbt2_mon.o \ + cxd2880_tnrdmd_dvbt.o \ + cxd2880_tnrdmd_dvbt_mon.o\ + cxd2880_tnrdmd_mon.o\ + cxd2880_top.o + +obj-$(CONFIG_DVB_CXD2880) += cxd2880.o diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h new file mode 100644 index 0000000000..4ea3510aab --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_H +#define CXD2880_H + +struct cxd2880_config { + struct spi_device *spi; + struct mutex *spi_mutex; /* For SPI access exclusive control */ +}; + +#if IS_REACHABLE(CONFIG_DVB_CXD2880) +extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, + struct cxd2880_config *cfg); +#else +static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, + struct cxd2880_config *cfg) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_CXD2880 */ + +#endif /* CXD2880_H */ diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c new file mode 100644 index 0000000000..d6f5af6609 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_common.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_common.h" + +int cxd2880_convert2s_complement(u32 value, u32 bitlen) +{ + if (!bitlen || bitlen >= 32) + return (int)value; + + if (value & (u32)(1 << (bitlen - 1))) + return (int)(GENMASK(31, bitlen) | value); + else + return (int)(GENMASK(bitlen - 1, 0) & value); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h new file mode 100644 index 0000000000..9dc15a5a96 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_common.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_COMMON_H +#define CXD2880_COMMON_H + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/bits.h> +#include <linux/string.h> + +int cxd2880_convert2s_complement(u32 value, u32 bitlen); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c new file mode 100644 index 0000000000..aba5940085 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_devio_spi.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * I/O interface via SPI + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_devio_spi.h" + +#define BURST_WRITE_MAX 128 + +static int cxd2880_io_spi_read_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 *data, + u32 size) +{ + int ret = 0; + struct cxd2880_spi *spi = NULL; + u8 send_data[6]; + u8 *read_data_top = data; + + if (!io || !io->if_object || !data) + return -EINVAL; + + if (sub_address + size > 0x100) + return -EINVAL; + + spi = io->if_object; + + if (tgt == CXD2880_IO_TGT_SYS) + send_data[0] = 0x0b; + else + send_data[0] = 0x0a; + + send_data[3] = 0; + send_data[4] = 0; + send_data[5] = 0; + + while (size > 0) { + send_data[1] = sub_address; + if (size > 255) + send_data[2] = 255; + else + send_data[2] = size; + + ret = + spi->write_read(spi, send_data, sizeof(send_data), + read_data_top, send_data[2]); + if (ret) + return ret; + + sub_address += send_data[2]; + read_data_top += send_data[2]; + size -= send_data[2]; + } + + return ret; +} + +static int cxd2880_io_spi_write_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, + const u8 *data, u32 size) +{ + int ret = 0; + struct cxd2880_spi *spi = NULL; + u8 send_data[BURST_WRITE_MAX + 4]; + const u8 *write_data_top = data; + + if (!io || !io->if_object || !data) + return -EINVAL; + + if (size > BURST_WRITE_MAX) + return -EINVAL; + + if (sub_address + size > 0x100) + return -EINVAL; + + spi = io->if_object; + + if (tgt == CXD2880_IO_TGT_SYS) + send_data[0] = 0x0f; + else + send_data[0] = 0x0e; + + while (size > 0) { + send_data[1] = sub_address; + if (size > 255) + send_data[2] = 255; + else + send_data[2] = size; + + memcpy(&send_data[3], write_data_top, send_data[2]); + + if (tgt == CXD2880_IO_TGT_SYS) { + send_data[3 + send_data[2]] = 0x00; + ret = spi->write(spi, send_data, send_data[2] + 4); + } else { + ret = spi->write(spi, send_data, send_data[2] + 3); + } + if (ret) + return ret; + + sub_address += send_data[2]; + write_data_top += send_data[2]; + size -= send_data[2]; + } + + return ret; +} + +int cxd2880_io_spi_create(struct cxd2880_io *io, + struct cxd2880_spi *spi, u8 slave_select) +{ + if (!io || !spi) + return -EINVAL; + + io->read_regs = cxd2880_io_spi_read_reg; + io->write_regs = cxd2880_io_spi_write_reg; + io->write_reg = cxd2880_io_common_write_one_reg; + io->if_object = spi; + io->i2c_address_sys = 0; + io->i2c_address_demod = 0; + io->slave_select = slave_select; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h new file mode 100644 index 0000000000..27f7cb12fa --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_devio_spi.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * I/O interface via SPI + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DEVIO_SPI_H +#define CXD2880_DEVIO_SPI_H + +#include "cxd2880_common.h" +#include "cxd2880_io.h" +#include "cxd2880_spi.h" + +#include "cxd2880_tnrdmd.h" + +int cxd2880_io_spi_create(struct cxd2880_io *io, + struct cxd2880_spi *spi, + u8 slave_select); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h new file mode 100644 index 0000000000..820f4757a5 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_dtv.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DTV related definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DTV_H +#define CXD2880_DTV_H + +enum cxd2880_dtv_sys { + CXD2880_DTV_SYS_UNKNOWN, + CXD2880_DTV_SYS_DVBT, + CXD2880_DTV_SYS_DVBT2, + CXD2880_DTV_SYS_ANY +}; + +enum cxd2880_dtv_bandwidth { + CXD2880_DTV_BW_UNKNOWN = 0, + CXD2880_DTV_BW_1_7_MHZ = 1, + CXD2880_DTV_BW_5_MHZ = 5, + CXD2880_DTV_BW_6_MHZ = 6, + CXD2880_DTV_BW_7_MHZ = 7, + CXD2880_DTV_BW_8_MHZ = 8 +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h new file mode 100644 index 0000000000..76a1acc346 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_dvbt.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T related definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DVBT_H +#define CXD2880_DVBT_H + +#include "cxd2880_common.h" + +enum cxd2880_dvbt_constellation { + CXD2880_DVBT_CONSTELLATION_QPSK, + CXD2880_DVBT_CONSTELLATION_16QAM, + CXD2880_DVBT_CONSTELLATION_64QAM, + CXD2880_DVBT_CONSTELLATION_RESERVED_3 +}; + +enum cxd2880_dvbt_hierarchy { + CXD2880_DVBT_HIERARCHY_NON, + CXD2880_DVBT_HIERARCHY_1, + CXD2880_DVBT_HIERARCHY_2, + CXD2880_DVBT_HIERARCHY_4 +}; + +enum cxd2880_dvbt_coderate { + CXD2880_DVBT_CODERATE_1_2, + CXD2880_DVBT_CODERATE_2_3, + CXD2880_DVBT_CODERATE_3_4, + CXD2880_DVBT_CODERATE_5_6, + CXD2880_DVBT_CODERATE_7_8, + CXD2880_DVBT_CODERATE_RESERVED_5, + CXD2880_DVBT_CODERATE_RESERVED_6, + CXD2880_DVBT_CODERATE_RESERVED_7 +}; + +enum cxd2880_dvbt_guard { + CXD2880_DVBT_GUARD_1_32, + CXD2880_DVBT_GUARD_1_16, + CXD2880_DVBT_GUARD_1_8, + CXD2880_DVBT_GUARD_1_4 +}; + +enum cxd2880_dvbt_mode { + CXD2880_DVBT_MODE_2K, + CXD2880_DVBT_MODE_8K, + CXD2880_DVBT_MODE_RESERVED_2, + CXD2880_DVBT_MODE_RESERVED_3 +}; + +enum cxd2880_dvbt_profile { + CXD2880_DVBT_PROFILE_HP = 0, + CXD2880_DVBT_PROFILE_LP +}; + +struct cxd2880_dvbt_tpsinfo { + enum cxd2880_dvbt_constellation constellation; + enum cxd2880_dvbt_hierarchy hierarchy; + enum cxd2880_dvbt_coderate rate_hp; + enum cxd2880_dvbt_coderate rate_lp; + enum cxd2880_dvbt_guard guard; + enum cxd2880_dvbt_mode mode; + u8 fnum; + u8 length_indicator; + u16 cell_id; + u8 cell_id_ok; + u8 reserved_even; + u8 reserved_odd; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h new file mode 100644 index 0000000000..191047b158 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_dvbt2.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T2 related definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_DVBT2_H +#define CXD2880_DVBT2_H + +#include "cxd2880_common.h" + +enum cxd2880_dvbt2_profile { + CXD2880_DVBT2_PROFILE_BASE, + CXD2880_DVBT2_PROFILE_LITE, + CXD2880_DVBT2_PROFILE_ANY +}; + +enum cxd2880_dvbt2_version { + CXD2880_DVBT2_V111, + CXD2880_DVBT2_V121, + CXD2880_DVBT2_V131 +}; + +enum cxd2880_dvbt2_s1 { + CXD2880_DVBT2_S1_BASE_SISO = 0x00, + CXD2880_DVBT2_S1_BASE_MISO = 0x01, + CXD2880_DVBT2_S1_NON_DVBT2 = 0x02, + CXD2880_DVBT2_S1_LITE_SISO = 0x03, + CXD2880_DVBT2_S1_LITE_MISO = 0x04, + CXD2880_DVBT2_S1_RSVD3 = 0x05, + CXD2880_DVBT2_S1_RSVD4 = 0x06, + CXD2880_DVBT2_S1_RSVD5 = 0x07, + CXD2880_DVBT2_S1_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_base_s2 { + CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00, + CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01, + CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02, + CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03, + CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04, + CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05, + CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06, + CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07, + CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_lite_s2 { + CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00, + CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01, + CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02, + CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03, + CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04, + CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05, + CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06, + CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07, + CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_guard { + CXD2880_DVBT2_G1_32 = 0x00, + CXD2880_DVBT2_G1_16 = 0x01, + CXD2880_DVBT2_G1_8 = 0x02, + CXD2880_DVBT2_G1_4 = 0x03, + CXD2880_DVBT2_G1_128 = 0x04, + CXD2880_DVBT2_G19_128 = 0x05, + CXD2880_DVBT2_G19_256 = 0x06, + CXD2880_DVBT2_G_RSVD1 = 0x07, + CXD2880_DVBT2_G_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_mode { + CXD2880_DVBT2_M2K = 0x00, + CXD2880_DVBT2_M8K = 0x01, + CXD2880_DVBT2_M4K = 0x02, + CXD2880_DVBT2_M1K = 0x03, + CXD2880_DVBT2_M16K = 0x04, + CXD2880_DVBT2_M32K = 0x05, + CXD2880_DVBT2_M_RSVD1 = 0x06, + CXD2880_DVBT2_M_RSVD2 = 0x07 +}; + +enum cxd2880_dvbt2_bw { + CXD2880_DVBT2_BW_8 = 0x00, + CXD2880_DVBT2_BW_7 = 0x01, + CXD2880_DVBT2_BW_6 = 0x02, + CXD2880_DVBT2_BW_5 = 0x03, + CXD2880_DVBT2_BW_10 = 0x04, + CXD2880_DVBT2_BW_1_7 = 0x05, + CXD2880_DVBT2_BW_RSVD1 = 0x06, + CXD2880_DVBT2_BW_RSVD2 = 0x07, + CXD2880_DVBT2_BW_RSVD3 = 0x08, + CXD2880_DVBT2_BW_RSVD4 = 0x09, + CXD2880_DVBT2_BW_RSVD5 = 0x0a, + CXD2880_DVBT2_BW_RSVD6 = 0x0b, + CXD2880_DVBT2_BW_RSVD7 = 0x0c, + CXD2880_DVBT2_BW_RSVD8 = 0x0d, + CXD2880_DVBT2_BW_RSVD9 = 0x0e, + CXD2880_DVBT2_BW_RSVD10 = 0x0f, + CXD2880_DVBT2_BW_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1pre_type { + CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00, + CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01, + CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02, + CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03, + CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_papr { + CXD2880_DVBT2_PAPR_0 = 0x00, + CXD2880_DVBT2_PAPR_1 = 0x01, + CXD2880_DVBT2_PAPR_2 = 0x02, + CXD2880_DVBT2_PAPR_3 = 0x03, + CXD2880_DVBT2_PAPR_RSVD1 = 0x04, + CXD2880_DVBT2_PAPR_RSVD2 = 0x05, + CXD2880_DVBT2_PAPR_RSVD3 = 0x06, + CXD2880_DVBT2_PAPR_RSVD4 = 0x07, + CXD2880_DVBT2_PAPR_RSVD5 = 0x08, + CXD2880_DVBT2_PAPR_RSVD6 = 0x09, + CXD2880_DVBT2_PAPR_RSVD7 = 0x0a, + CXD2880_DVBT2_PAPR_RSVD8 = 0x0b, + CXD2880_DVBT2_PAPR_RSVD9 = 0x0c, + CXD2880_DVBT2_PAPR_RSVD10 = 0x0d, + CXD2880_DVBT2_PAPR_RSVD11 = 0x0e, + CXD2880_DVBT2_PAPR_RSVD12 = 0x0f, + CXD2880_DVBT2_PAPR_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1post_constell { + CXD2880_DVBT2_L1POST_BPSK = 0x00, + CXD2880_DVBT2_L1POST_QPSK = 0x01, + CXD2880_DVBT2_L1POST_QAM16 = 0x02, + CXD2880_DVBT2_L1POST_QAM64 = 0x03, + CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04, + CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05, + CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06, + CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07, + CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08, + CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09, + CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0a, + CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0b, + CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0c, + CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0d, + CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0e, + CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0f, + CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1post_cr { + CXD2880_DVBT2_L1POST_R1_2 = 0x00, + CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01, + CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02, + CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03, + CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_l1post_fec_type { + CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00, + CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01, + CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02, + CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03, + CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_pp { + CXD2880_DVBT2_PP1 = 0x00, + CXD2880_DVBT2_PP2 = 0x01, + CXD2880_DVBT2_PP3 = 0x02, + CXD2880_DVBT2_PP4 = 0x03, + CXD2880_DVBT2_PP5 = 0x04, + CXD2880_DVBT2_PP6 = 0x05, + CXD2880_DVBT2_PP7 = 0x06, + CXD2880_DVBT2_PP8 = 0x07, + CXD2880_DVBT2_PP_RSVD1 = 0x08, + CXD2880_DVBT2_PP_RSVD2 = 0x09, + CXD2880_DVBT2_PP_RSVD3 = 0x0a, + CXD2880_DVBT2_PP_RSVD4 = 0x0b, + CXD2880_DVBT2_PP_RSVD5 = 0x0c, + CXD2880_DVBT2_PP_RSVD6 = 0x0d, + CXD2880_DVBT2_PP_RSVD7 = 0x0e, + CXD2880_DVBT2_PP_RSVD8 = 0x0f, + CXD2880_DVBT2_PP_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_code_rate { + CXD2880_DVBT2_R1_2 = 0x00, + CXD2880_DVBT2_R3_5 = 0x01, + CXD2880_DVBT2_R2_3 = 0x02, + CXD2880_DVBT2_R3_4 = 0x03, + CXD2880_DVBT2_R4_5 = 0x04, + CXD2880_DVBT2_R5_6 = 0x05, + CXD2880_DVBT2_R1_3 = 0x06, + CXD2880_DVBT2_R2_5 = 0x07, + CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_constell { + CXD2880_DVBT2_QPSK = 0x00, + CXD2880_DVBT2_QAM16 = 0x01, + CXD2880_DVBT2_QAM64 = 0x02, + CXD2880_DVBT2_QAM256 = 0x03, + CXD2880_DVBT2_CON_RSVD1 = 0x04, + CXD2880_DVBT2_CON_RSVD2 = 0x05, + CXD2880_DVBT2_CON_RSVD3 = 0x06, + CXD2880_DVBT2_CON_RSVD4 = 0x07, + CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_type { + CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00, + CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01, + CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02, + CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03, + CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04, + CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05, + CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06, + CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07, + CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_payload { + CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00, + CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01, + CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02, + CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0a, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0b, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0c, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0d, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0e, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0f, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1a, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1b, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1c, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1d, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1e, + CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1f, + CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_fec { + CXD2880_DVBT2_FEC_LDPC_16K = 0x00, + CXD2880_DVBT2_FEC_LDPC_64K = 0x01, + CXD2880_DVBT2_FEC_RSVD1 = 0x02, + CXD2880_DVBT2_FEC_RSVD2 = 0x03, + CXD2880_DVBT2_FEC_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_mode { + CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00, + CXD2880_DVBT2_PLP_MODE_NM = 0x01, + CXD2880_DVBT2_PLP_MODE_HEM = 0x02, + CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03, + CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xff +}; + +enum cxd2880_dvbt2_plp_btype { + CXD2880_DVBT2_PLP_COMMON, + CXD2880_DVBT2_PLP_DATA +}; + +enum cxd2880_dvbt2_stream { + CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00, + CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01, + CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02, + CXD2880_DVBT2_STREAM_TRANSPORT = 0x03, + CXD2880_DVBT2_STREAM_UNKNOWN = 0xff +}; + +struct cxd2880_dvbt2_l1pre { + enum cxd2880_dvbt2_l1pre_type type; + u8 bw_ext; + enum cxd2880_dvbt2_s1 s1; + u8 s2; + u8 mixed; + enum cxd2880_dvbt2_mode fft_mode; + u8 l1_rep; + enum cxd2880_dvbt2_guard gi; + enum cxd2880_dvbt2_papr papr; + enum cxd2880_dvbt2_l1post_constell mod; + enum cxd2880_dvbt2_l1post_cr cr; + enum cxd2880_dvbt2_l1post_fec_type fec; + u32 l1_post_size; + u32 l1_post_info_size; + enum cxd2880_dvbt2_pp pp; + u8 tx_id_availability; + u16 cell_id; + u16 network_id; + u16 sys_id; + u8 num_frames; + u16 num_symbols; + u8 regen; + u8 post_ext; + u8 num_rf_freqs; + u8 rf_idx; + enum cxd2880_dvbt2_version t2_version; + u8 l1_post_scrambled; + u8 t2_base_lite; + u32 crc32; +}; + +struct cxd2880_dvbt2_plp { + u8 id; + enum cxd2880_dvbt2_plp_type type; + enum cxd2880_dvbt2_plp_payload payload; + u8 ff; + u8 first_rf_idx; + u8 first_frm_idx; + u8 group_id; + enum cxd2880_dvbt2_plp_constell constell; + enum cxd2880_dvbt2_plp_code_rate plp_cr; + u8 rot; + enum cxd2880_dvbt2_plp_fec fec; + u16 num_blocks_max; + u8 frm_int; + u8 til_len; + u8 til_type; + u8 in_band_a_flag; + u8 in_band_b_flag; + u16 rsvd; + enum cxd2880_dvbt2_plp_mode plp_mode; + u8 static_flag; + u8 static_padding_flag; +}; + +struct cxd2880_dvbt2_l1post { + u16 sub_slices_per_frame; + u8 num_plps; + u8 num_aux; + u8 aux_cfg_rfu; + u8 rf_idx; + u32 freq; + u8 fef_type; + u32 fef_length; + u8 fef_intvl; +}; + +struct cxd2880_dvbt2_ofdm { + u8 mixed; + u8 is_miso; + enum cxd2880_dvbt2_mode mode; + enum cxd2880_dvbt2_guard gi; + enum cxd2880_dvbt2_pp pp; + u8 bw_ext; + enum cxd2880_dvbt2_papr papr; + u16 num_symbols; +}; + +struct cxd2880_dvbt2_bbheader { + enum cxd2880_dvbt2_stream stream_input; + u8 is_single_input_stream; + u8 is_constant_coding_modulation; + u8 issy_indicator; + u8 null_packet_deletion; + u8 ext; + u8 input_stream_identifier; + u16 user_packet_length; + u16 data_field_length; + u8 sync_byte; + u32 issy; + enum cxd2880_dvbt2_plp_mode plp_mode; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c new file mode 100644 index 0000000000..5302ab0964 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_integ.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * integration layer common functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <linux/ktime.h> +#include <linux/errno.h> + +#include "cxd2880_tnrdmd.h" +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_integ.h" + +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + ktime_t start; + u8 cpu_task_completed = 0; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_tnrdmd_init1(tnr_dmd); + if (ret) + return ret; + + start = ktime_get(); + + while (1) { + ret = + cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd, + &cpu_task_completed); + if (ret) + return ret; + + if (cpu_task_completed) + break; + + if (ktime_to_ms(ktime_sub(ktime_get(), start)) > + CXD2880_TNRDMD_WAIT_INIT_TIMEOUT) + return -ETIMEDOUT; + + usleep_range(CXD2880_TNRDMD_WAIT_INIT_INTVL, + CXD2880_TNRDMD_WAIT_INIT_INTVL + 1000); + } + + return cxd2880_tnrdmd_init2(tnr_dmd); +} + +int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + atomic_set(&tnr_dmd->cancel, 1); + + return 0; +} + +int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + if (atomic_read(&tnr_dmd->cancel) != 0) + return -ECANCELED; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h new file mode 100644 index 0000000000..7160225db8 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_integ.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * integration layer common interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_INTEG_H +#define CXD2880_INTEG_H + +#include "cxd2880_tnrdmd.h" + +#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT 500 +#define CXD2880_TNRDMD_WAIT_INIT_INTVL 10 + +#define CXD2880_TNRDMD_WAIT_AGC_STABLE 100 + +int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd + *tnr_dmd); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c new file mode 100644 index 0000000000..9d932bccfa --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_io.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * register I/O interface functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_io.h" + +int cxd2880_io_common_write_one_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data) +{ + if (!io) + return -EINVAL; + + return io->write_regs(io, tgt, sub_address, &data, 1); +} + +int cxd2880_io_set_reg_bits(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data, u8 mask) +{ + int ret; + + if (!io) + return -EINVAL; + + if (mask == 0x00) + return 0; + + if (mask != 0xff) { + u8 rdata = 0x00; + + ret = io->read_regs(io, tgt, sub_address, &rdata, 1); + if (ret) + return ret; + + data = (data & mask) | (rdata & (mask ^ 0xff)); + } + + return io->write_reg(io, tgt, sub_address, data); +} + +int cxd2880_io_write_multi_regs(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + const struct cxd2880_reg_value reg_value[], + u8 size) +{ + int ret; + int i; + + if (!io) + return -EINVAL; + + for (i = 0; i < size ; i++) { + ret = io->write_reg(io, tgt, reg_value[i].addr, + reg_value[i].value); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h new file mode 100644 index 0000000000..ba55027888 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_io.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * register I/O interface definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_IO_H +#define CXD2880_IO_H + +#include "cxd2880_common.h" + +enum cxd2880_io_tgt { + CXD2880_IO_TGT_SYS, + CXD2880_IO_TGT_DMD +}; + +struct cxd2880_reg_value { + u8 addr; + u8 value; +}; + +struct cxd2880_io { + int (*read_regs)(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, u8 sub_address, + u8 *data, u32 size); + int (*write_regs)(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, u8 sub_address, + const u8 *data, u32 size); + int (*write_reg)(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, u8 sub_address, + u8 data); + void *if_object; + u8 i2c_address_sys; + u8 i2c_address_demod; + u8 slave_select; + void *user; +}; + +int cxd2880_io_common_write_one_reg(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data); + +int cxd2880_io_set_reg_bits(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + u8 sub_address, u8 data, u8 mask); + +int cxd2880_io_write_multi_regs(struct cxd2880_io *io, + enum cxd2880_io_tgt tgt, + const struct cxd2880_reg_value reg_value[], + u8 size); +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h new file mode 100644 index 0000000000..2be2074618 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_spi.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI access definitions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_SPI_H +#define CXD2880_SPI_H + +#include "cxd2880_common.h" + +enum cxd2880_spi_mode { + CXD2880_SPI_MODE_0, + CXD2880_SPI_MODE_1, + CXD2880_SPI_MODE_2, + CXD2880_SPI_MODE_3 +}; + +struct cxd2880_spi { + int (*read)(struct cxd2880_spi *spi, u8 *data, + u32 size); + int (*write)(struct cxd2880_spi *spi, const u8 *data, + u32 size); + int (*write_read)(struct cxd2880_spi *spi, + const u8 *tx_data, u32 tx_size, + u8 *rx_data, u32 rx_size); + u32 flags; + void *user; +}; + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c new file mode 100644 index 0000000000..b8cbaa8d7a --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_spi_device.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI access functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <linux/spi/spi.h> + +#include "cxd2880_spi_device.h" + +static int cxd2880_spi_device_write(struct cxd2880_spi *spi, + const u8 *data, u32 size) +{ + struct cxd2880_spi_device *spi_device = NULL; + struct spi_message msg; + struct spi_transfer tx; + int result = 0; + + if (!spi || !spi->user || !data || size == 0) + return -EINVAL; + + spi_device = spi->user; + + memset(&tx, 0, sizeof(tx)); + tx.tx_buf = data; + tx.len = size; + + spi_message_init(&msg); + spi_message_add_tail(&tx, &msg); + result = spi_sync(spi_device->spi, &msg); + + if (result < 0) + return -EIO; + + return 0; +} + +static int cxd2880_spi_device_write_read(struct cxd2880_spi *spi, + const u8 *tx_data, + u32 tx_size, + u8 *rx_data, + u32 rx_size) +{ + struct cxd2880_spi_device *spi_device = NULL; + int result = 0; + + if (!spi || !spi->user || !tx_data || + !tx_size || !rx_data || !rx_size) + return -EINVAL; + + spi_device = spi->user; + + result = spi_write_then_read(spi_device->spi, tx_data, + tx_size, rx_data, rx_size); + if (result < 0) + return -EIO; + + return 0; +} + +int +cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device, + enum cxd2880_spi_mode mode, + u32 speed_hz) +{ + int result = 0; + struct spi_device *spi = spi_device->spi; + + switch (mode) { + case CXD2880_SPI_MODE_0: + spi->mode = SPI_MODE_0; + break; + case CXD2880_SPI_MODE_1: + spi->mode = SPI_MODE_1; + break; + case CXD2880_SPI_MODE_2: + spi->mode = SPI_MODE_2; + break; + case CXD2880_SPI_MODE_3: + spi->mode = SPI_MODE_3; + break; + default: + return -EINVAL; + } + + spi->max_speed_hz = speed_hz; + spi->bits_per_word = 8; + result = spi_setup(spi); + if (result != 0) { + pr_err("spi_setup failed %d\n", result); + return -EINVAL; + } + + return 0; +} + +int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi, + struct cxd2880_spi_device *spi_device) +{ + if (!spi || !spi_device) + return -EINVAL; + + spi->read = NULL; + spi->write = cxd2880_spi_device_write; + spi->write_read = cxd2880_spi_device_write_read; + spi->flags = 0; + spi->user = spi_device; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h new file mode 100644 index 0000000000..05e3a03de3 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_spi_device.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI access interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_SPI_DEVICE_H +#define CXD2880_SPI_DEVICE_H + +#include "cxd2880_spi.h" + +struct cxd2880_spi_device { + struct spi_device *spi; +}; + +int cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device, + enum cxd2880_spi_mode mode, + u32 speedHz); + +int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi, + struct cxd2880_spi_device *spi_device); + +#endif /* CXD2880_SPI_DEVICE_H */ diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c new file mode 100644 index 0000000000..0a1f3899d7 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c @@ -0,0 +1,3519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common control functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <media/dvb_frontend.h> +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_tnrdmd_dvbt2.h" + +static const struct cxd2880_reg_value p_init1_seq[] = { + {0x11, 0x16}, {0x00, 0x10}, +}; + +static const struct cxd2880_reg_value rf_init1_seq1[] = { + {0x4f, 0x18}, {0x61, 0x00}, {0x71, 0x00}, {0x9d, 0x01}, + {0x7d, 0x02}, {0x8f, 0x01}, {0x8b, 0xc6}, {0x9a, 0x03}, + {0x1c, 0x00}, +}; + +static const struct cxd2880_reg_value rf_init1_seq2[] = { + {0xb9, 0x07}, {0x33, 0x01}, {0xc1, 0x01}, {0xc4, 0x1e}, +}; + +static const struct cxd2880_reg_value rf_init1_seq3[] = { + {0x00, 0x10}, {0x51, 0x01}, {0xc5, 0x07}, {0x00, 0x11}, + {0x70, 0xe9}, {0x76, 0x0a}, {0x78, 0x32}, {0x7a, 0x46}, + {0x7c, 0x86}, {0x7e, 0xa4}, {0x00, 0x10}, {0xe1, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init1_seq4[] = { + {0x15, 0x00}, {0x00, 0x16} +}; + +static const struct cxd2880_reg_value rf_init1_seq5[] = { + {0x00, 0x00}, {0x25, 0x00} +}; + +static const struct cxd2880_reg_value rf_init1_seq6[] = { + {0x02, 0x00}, {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, + {0x8f, 0x16}, {0x67, 0x60}, {0x6a, 0x0f}, {0x6c, 0x17} +}; + +static const struct cxd2880_reg_value rf_init1_seq7[] = { + {0x00, 0xe2}, {0x41, 0xa0}, {0x4b, 0x68}, {0x00, 0x00}, + {0x21, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init1_seq8[] = { + {0x00, 0x10}, {0x25, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init1_seq9[] = { + {0x00, 0x10}, {0x14, 0x01}, {0x00, 0x00}, {0x26, 0x00}, +}; + +static const struct cxd2880_reg_value rf_init2_seq1[] = { + {0x00, 0x14}, {0x1b, 0x01}, +}; + +static const struct cxd2880_reg_value rf_init2_seq2[] = { + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, {0xd3, 0x00}, + {0x00, 0x00}, {0x21, 0x00}, +}; + +static const struct cxd2880_reg_value x_tune1_seq1[] = { + {0x00, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune1_seq2[] = { + {0x62, 0x00}, {0x00, 0x15}, +}; + +static const struct cxd2880_reg_value x_tune2_seq1[] = { + {0x00, 0x1a}, {0x29, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune2_seq2[] = { + {0x62, 0x01}, {0x00, 0x11}, {0x2d, 0x00}, {0x2f, 0x00}, +}; + +static const struct cxd2880_reg_value x_tune2_seq3[] = { + {0x00, 0x00}, {0x10, 0x00}, {0x21, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune2_seq4[] = { + {0x00, 0xe1}, {0x8a, 0x87}, +}; + +static const struct cxd2880_reg_value x_tune2_seq5[] = { + {0x00, 0x00}, {0x21, 0x00}, +}; + +static const struct cxd2880_reg_value x_tune3_seq[] = { + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0xa0}, + {0x00, 0x00}, {0x21, 0x00}, {0xfe, 0x01}, +}; + +static const struct cxd2880_reg_value x_tune4_seq[] = { + {0x00, 0x00}, {0xfe, 0x01}, +}; + +static const struct cxd2880_reg_value x_sleep1_seq[] = { + {0x00, 0x00}, {0x57, 0x03}, +}; + +static const struct cxd2880_reg_value x_sleep2_seq1[] = { + {0x00, 0x2d}, {0xb1, 0x01}, +}; + +static const struct cxd2880_reg_value x_sleep2_seq2[] = { + {0x00, 0x10}, {0xf4, 0x00}, {0xf3, 0x00}, {0xf2, 0x00}, + {0xf1, 0x00}, {0xf0, 0x00}, {0xef, 0x00}, +}; + +static const struct cxd2880_reg_value x_sleep3_seq[] = { + {0x00, 0x00}, {0xfd, 0x00}, +}; + +static const struct cxd2880_reg_value x_sleep4_seq[] = { + {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0x00}, + {0x00, 0x00}, {0x21, 0x00}, +}; + +static const struct cxd2880_reg_value spll_reset_seq1[] = { + {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01}, + {0x26, 0x01}, +}; + +static const struct cxd2880_reg_value spll_reset_seq2[] = { + {0x00, 0x00}, {0x10, 0x00}, +}; + +static const struct cxd2880_reg_value spll_reset_seq3[] = { + {0x00, 0x00}, {0x27, 0x00}, {0x22, 0x01}, +}; + +static const struct cxd2880_reg_value spll_reset_seq4[] = { + {0x00, 0x00}, {0x27, 0x01}, +}; + +static const struct cxd2880_reg_value spll_reset_seq5[] = { + {0x00, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq1[] = { + {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq2[] = { + {0x00, 0x00}, {0x10, 0x00}, +}; + +static const struct cxd2880_reg_value t_power_x_seq3[] = { + {0x00, 0x00}, {0x27, 0x00}, {0x25, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq4[] = { + {0x00, 0x00}, {0x2a, 0x00}, +}; + +static const struct cxd2880_reg_value t_power_x_seq5[] = { + {0x00, 0x00}, {0x25, 0x00}, +}; + +static const struct cxd2880_reg_value t_power_x_seq6[] = { + {0x00, 0x00}, {0x27, 0x01}, +}; + +static const struct cxd2880_reg_value t_power_x_seq7[] = { + {0x00, 0x00}, {0x10, 0x01}, +}; + +static const struct cxd2880_reg_value set_ts_pin_seq[] = { + {0x50, 0x3f}, {0x52, 0x1f}, + +}; + +static const struct cxd2880_reg_value set_ts_output_seq1[] = { + {0x00, 0x00}, {0x52, 0x00}, +}; + +static const struct cxd2880_reg_value set_ts_output_seq2[] = { + {0x00, 0x00}, {0xc3, 0x00}, + +}; + +static const struct cxd2880_reg_value set_ts_output_seq3[] = { + {0x00, 0x00}, {0xc3, 0x01}, + +}; + +static const struct cxd2880_reg_value set_ts_output_seq4[] = { + {0x00, 0x00}, {0x52, 0x1f}, + +}; + +static int p_init1(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE || + tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (tnr_dmd->create_param.ts_output_if) { + case CXD2880_TNRDMD_TSOUT_IF_TS: + data = 0x00; + break; + case CXD2880_TNRDMD_TSOUT_IF_SPI: + data = 0x01; + break; + case CXD2880_TNRDMD_TSOUT_IF_SDIO: + data = 0x02; + break; + default: + return -EINVAL; + } + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + p_init1_seq, + ARRAY_SIZE(p_init1_seq)); + if (ret) + return ret; + + switch (tnr_dmd->chip_id) { + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X: + data = 0x1a; + break; + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11: + data = 0x16; + break; + default: + return -ENOTTY; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data); + if (ret) + return ret; + + if (tnr_dmd->create_param.en_internal_ldo) + data = 0x01; + else + data = 0x00; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, data); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x13, data); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x12, data); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + switch (tnr_dmd->chip_id) { + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X: + data = 0x01; + break; + case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11: + data = 0x00; + break; + default: + return -ENOTTY; + } + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x69, data); +} + +static int p_init2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[6] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = tnr_dmd->create_param.xosc_cap; + data[1] = tnr_dmd->create_param.xosc_i; + switch (tnr_dmd->create_param.xtal_share_type) { + case CXD2880_TNRDMD_XTAL_SHARE_NONE: + data[2] = 0x01; + data[3] = 0x00; + break; + case CXD2880_TNRDMD_XTAL_SHARE_EXTREF: + data[2] = 0x00; + data[3] = 0x00; + break; + case CXD2880_TNRDMD_XTAL_SHARE_MASTER: + data[2] = 0x01; + data[3] = 0x01; + break; + case CXD2880_TNRDMD_XTAL_SHARE_SLAVE: + data[2] = 0x00; + data[3] = 0x01; + break; + default: + return -EINVAL; + } + data[4] = 0x06; + data[5] = 0x00; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x13, data, 6); +} + +static int p_init3(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[2] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + switch (tnr_dmd->diver_mode) { + case CXD2880_TNRDMD_DIVERMODE_SINGLE: + data[0] = 0x00; + break; + case CXD2880_TNRDMD_DIVERMODE_MAIN: + data[0] = 0x03; + break; + case CXD2880_TNRDMD_DIVERMODE_SUB: + data[0] = 0x02; + break; + default: + return -EINVAL; + } + + data[1] = 0x01; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x1f, data, 2); +} + +static int rf_init1(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[8] = { 0 }; + static const u8 rf_init1_cdata1[40] = { + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x04, 0x04, 0x04 + }; + + static const u8 rf_init1_cdata2[5] = {0xff, 0x00, 0x00, 0x00, 0x00}; + static const u8 rf_init1_cdata3[80] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09, + 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, + 0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x10, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x05, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0a, 0x03, 0xe0 + }; + + static const u8 rf_init1_cdata4[8] = { + 0x20, 0x20, 0x30, 0x41, 0x50, 0x5f, 0x6f, 0x80 + }; + + static const u8 rf_init1_cdata5[50] = { + 0x00, 0x09, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, + 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0f, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, + 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x0e + }; + + u8 addr = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x01; + data[1] = 0x00; + data[2] = 0x01; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x21, data, 3); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + data[0] = 0x01; + data[1] = 0x01; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x17, data, 2); + if (ret) + return ret; + + if (tnr_dmd->create_param.stationary_use) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x1a, 0x06); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq1, + ARRAY_SIZE(rf_init1_seq1)); + if (ret) + return ret; + + data[0] = 0x00; + if (tnr_dmd->create_param.is_cxd2881gg && + tnr_dmd->create_param.xtal_share_type == + CXD2880_TNRDMD_XTAL_SHARE_SLAVE) + data[1] = 0x00; + else + data[1] = 0x1f; + data[2] = 0x0a; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xb5, data, 3); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq2, + ARRAY_SIZE(rf_init1_seq2)); + if (ret) + return ret; + + if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) { + data[0] = 0x34; + data[1] = 0x2c; + } else { + data[0] = 0x2f; + data[1] = 0x25; + } + data[2] = 0x15; + data[3] = 0x19; + data[4] = 0x1b; + data[5] = 0x15; + data[6] = 0x19; + data[7] = 0x1b; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xd9, data, 8); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x11); + if (ret) + return ret; + data[0] = 0x6c; + data[1] = 0x10; + data[2] = 0xa6; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x44, data, 3); + if (ret) + return ret; + data[0] = 0x16; + data[1] = 0xa8; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x50, data, 2); + if (ret) + return ret; + data[0] = 0x00; + data[1] = 0x22; + data[2] = 0x00; + data[3] = 0x88; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x62, data, 4); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x74, 0x75); + if (ret) + return ret; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x7f, rf_init1_cdata1, 40); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x16); + if (ret) + return ret; + data[0] = 0x00; + data[1] = 0x71; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 2); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x23, 0x89); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x27, rf_init1_cdata2, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x3a, rf_init1_cdata3, 80); + if (ret) + return ret; + + data[0] = 0x03; + data[1] = 0xe0; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xbc, data, 2); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq3, + ARRAY_SIZE(rf_init1_seq3)); + if (ret) + return ret; + + if (tnr_dmd->create_param.stationary_use) { + data[0] = 0x06; + data[1] = 0x07; + data[2] = 0x1a; + } else { + data[0] = 0x00; + data[1] = 0x08; + data[2] = 0x19; + } + data[3] = 0x0e; + data[4] = 0x09; + data[5] = 0x0e; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x12); + if (ret) + return ret; + for (addr = 0x10; addr < 0x9f; addr += 6) { + if (tnr_dmd->lna_thrs_tbl_air) { + u8 idx = 0; + + idx = (addr - 0x10) / 6; + data[0] = + tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on; + data[1] = + tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off; + } + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + addr, data, 6); + if (ret) + return ret; + } + + data[0] = 0x00; + data[1] = 0x08; + if (tnr_dmd->create_param.stationary_use) + data[2] = 0x1a; + else + data[2] = 0x19; + data[3] = 0x0e; + data[4] = 0x09; + data[5] = 0x0e; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x13); + if (ret) + return ret; + for (addr = 0x10; addr < 0xcf; addr += 6) { + if (tnr_dmd->lna_thrs_tbl_cable) { + u8 idx = 0; + + idx = (addr - 0x10) / 6; + data[0] = + tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on; + data[1] = + tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off; + } + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + addr, data, 6); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x11); + if (ret) + return ret; + data[0] = 0x08; + data[1] = 0x09; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xbd, data, 2); + if (ret) + return ret; + data[0] = 0x08; + data[1] = 0x09; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xc4, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xc9, rf_init1_cdata4, 8); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x14); + if (ret) + return ret; + data[0] = 0x15; + data[1] = 0x18; + data[2] = 0x00; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 3); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq4, + ARRAY_SIZE(rf_init1_seq4)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x12, rf_init1_cdata5, 50); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq5, + ARRAY_SIZE(rf_init1_seq5)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + rf_init1_seq6, + ARRAY_SIZE(rf_init1_seq6)); + if (ret) + return ret; + + data[0] = 0x00; + data[1] = 0xfe; + data[2] = 0xee; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6e, data, 3); + if (ret) + return ret; + data[0] = 0xa1; + data[1] = 0x8b; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x8d, data, 2); + if (ret) + return ret; + data[0] = 0x08; + data[1] = 0x09; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x77, data, 2); + if (ret) + return ret; + + if (tnr_dmd->create_param.stationary_use) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x80, 0xaa); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + rf_init1_seq7, + ARRAY_SIZE(rf_init1_seq7)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq8, + ARRAY_SIZE(rf_init1_seq8)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x1a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init1_seq9, + ARRAY_SIZE(rf_init1_seq9)); +} + +static int rf_init2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[5] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + data[0] = 0x40; + data[1] = 0x40; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xea, data, 2); + if (ret) + return ret; + + usleep_range(1000, 2000); + + data[0] = 0x00; + if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) + data[1] = 0x00; + else + data[1] = 0x01; + data[2] = 0x01; + data[3] = 0x03; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x30, data, 4); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + rf_init2_seq1, + ARRAY_SIZE(rf_init2_seq1)); + if (ret) + return ret; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + rf_init2_seq2, + ARRAY_SIZE(rf_init2_seq2)); +} + +static int x_tune1(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, u32 freq_khz, + enum cxd2880_dtv_bandwidth bandwidth, + u8 is_cable, int shift_frequency_khz) +{ + u8 data[11] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune1_seq1, + ARRAY_SIZE(x_tune1_seq1)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + data[2] = 0x0e; + data[4] = 0x03; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xe7, data, 5); + if (ret) + return ret; + + data[0] = 0x1f; + data[1] = 0x80; + data[2] = 0x18; + data[3] = 0x00; + data[4] = 0x07; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xe7, data, 5); + if (ret) + return ret; + + usleep_range(1000, 2000); + + data[0] = 0x72; + data[1] = 0x81; + data[3] = 0x1d; + data[4] = 0x6f; + data[5] = 0x7e; + data[7] = 0x1c; + switch (sys) { + case CXD2880_DTV_SYS_DVBT: + data[2] = 0x94; + data[6] = 0x91; + break; + case CXD2880_DTV_SYS_DVBT2: + data[2] = 0x96; + data[6] = 0x93; + break; + default: + return -EINVAL; + } + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x44, data, 8); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_tune1_seq2, + ARRAY_SIZE(x_tune1_seq2)); + if (ret) + return ret; + + data[0] = 0x03; + data[1] = 0xe2; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x1e, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + data[0] = is_cable ? 0x01 : 0x00; + data[1] = 0x00; + data[2] = 0x6b; + data[3] = 0x4d; + + switch (bandwidth) { + case CXD2880_DTV_BW_1_7_MHZ: + data[4] = 0x03; + break; + case CXD2880_DTV_BW_5_MHZ: + case CXD2880_DTV_BW_6_MHZ: + data[4] = 0x00; + break; + case CXD2880_DTV_BW_7_MHZ: + data[4] = 0x01; + break; + case CXD2880_DTV_BW_8_MHZ: + data[4] = 0x02; + break; + default: + return -EINVAL; + } + + data[5] = 0x00; + + freq_khz += shift_frequency_khz; + + data[6] = (freq_khz >> 16) & 0x0f; + data[7] = (freq_khz >> 8) & 0xff; + data[8] = freq_khz & 0xff; + data[9] = 0xff; + data[10] = 0xfe; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x52, data, 11); +} + +static int x_tune2(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_bandwidth bandwidth, + enum cxd2880_tnrdmd_clockmode clk_mode, + int shift_frequency_khz) +{ + u8 data[3] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x11); + if (ret) + return ret; + + data[0] = 0x01; + data[1] = 0x0e; + data[2] = 0x01; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2d, data, 3); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_tune2_seq1, + ARRAY_SIZE(x_tune2_seq1)); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2c, data, 1); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x60, data[0]); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_tune2_seq2, + ARRAY_SIZE(x_tune2_seq2)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune2_seq3, + ARRAY_SIZE(x_tune2_seq3)); + if (ret) + return ret; + + if (shift_frequency_khz != 0) { + int shift_freq = 0; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0xe1); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 2); + if (ret) + return ret; + + shift_freq = shift_frequency_khz * 1000; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + default: + if (shift_freq >= 0) + shift_freq = (shift_freq + 183 / 2) / 183; + else + shift_freq = (shift_freq - 183 / 2) / 183; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + if (shift_freq >= 0) + shift_freq = (shift_freq + 178 / 2) / 178; + else + shift_freq = (shift_freq - 178 / 2) / 178; + break; + } + + shift_freq += + cxd2880_convert2s_complement((data[0] << 8) | data[1], 16); + + if (shift_freq > 32767) + shift_freq = 32767; + else if (shift_freq < -32768) + shift_freq = -32768; + + data[0] = (shift_freq >> 8) & 0xff; + data[1] = shift_freq & 0xff; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x69, data, 1); + if (ret) + return ret; + + shift_freq = -shift_frequency_khz; + + if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + default: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 17578 / 2) / 17578; + else + shift_freq = + (shift_freq * 1000 - + 17578 / 2) / 17578; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 17090 / 2) / 17090; + else + shift_freq = + (shift_freq * 1000 - + 17090 / 2) / 17090; + break; + } + } else { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + default: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 35156 / 2) / 35156; + else + shift_freq = + (shift_freq * 1000 - + 35156 / 2) / 35156; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + if (shift_freq >= 0) + shift_freq = + (shift_freq * 1000 + + 34180 / 2) / 34180; + else + shift_freq = + (shift_freq * 1000 - + 34180 / 2) / 34180; + break; + } + } + + shift_freq += cxd2880_convert2s_complement(data[0], 8); + + if (shift_freq > 127) + shift_freq = 127; + else if (shift_freq < -128) + shift_freq = -128; + + data[0] = shift_freq & 0xff; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x69, data[0]); + if (ret) + return ret; + } + + if (tnr_dmd->create_param.stationary_use) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune2_seq4, + ARRAY_SIZE(x_tune2_seq4)); + if (ret) + return ret; + } + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune2_seq5, + ARRAY_SIZE(x_tune2_seq5)); +} + +static int x_tune3(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 en_fef_intmtnt_ctrl) +{ + u8 data[6] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune3_seq, + ARRAY_SIZE(x_tune3_seq)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl) + memset(data, 0x01, sizeof(data)); + else + memset(data, 0x00, sizeof(data)); + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xef, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2d); + if (ret) + return ret; + if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl) + data[0] = 0x00; + else + data[0] = 0x01; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb1, data[0]); +} + +static int x_tune4(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[2] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x14; + data[1] = 0x00; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x0b; + data[1] = 0xff; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x57, 0x01); + if (ret) + return ret; + data[0] = 0x0b; + data[1] = 0xff; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x14; + data[1] = 0x00; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x57, 0x02); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_tune4_seq, + ARRAY_SIZE(x_tune4_seq)); + if (ret) + return ret; + + return cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_DMD, + x_tune4_seq, + ARRAY_SIZE(x_tune4_seq)); +} + +static int x_sleep1(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data[3] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_sleep1_seq, + ARRAY_SIZE(x_sleep1_seq)); + if (ret) + return ret; + + data[0] = 0x00; + data[1] = 0x00; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x1f; + data[1] = 0xff; + data[2] = 0x03; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 3); + if (ret) + return ret; + data[0] = 0x00; + data[1] = 0x00; + ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io, + CXD2880_IO_TGT_SYS, + 0x53, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + data[0] = 0x1f; + data[1] = 0xff; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x55, data, 2); +} + +static int x_sleep2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_sleep2_seq1, + ARRAY_SIZE(x_sleep2_seq1)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb2, &data, 1); + if (ret) + return ret; + + if ((data & 0x01) == 0x00) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + x_sleep2_seq2, + ARRAY_SIZE(x_sleep2_seq2)); +} + +static int x_sleep3(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_sleep3_seq, + ARRAY_SIZE(x_sleep3_seq)); +} + +static int x_sleep4(struct cxd2880_tnrdmd *tnr_dmd) +{ + if (!tnr_dmd) + return -EINVAL; + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + x_sleep4_seq, + ARRAY_SIZE(x_sleep4_seq)); +} + +static int spll_reset(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_clockmode clockmode) +{ + u8 data[4] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + spll_reset_seq1, + ARRAY_SIZE(spll_reset_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + spll_reset_seq2, + ARRAY_SIZE(spll_reset_seq2)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + spll_reset_seq3, + ARRAY_SIZE(spll_reset_seq3)); + if (ret) + return ret; + + switch (clockmode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data[0] = 0x00; + break; + + case CXD2880_TNRDMD_CLOCKMODE_B: + data[0] = 0x01; + break; + + case CXD2880_TNRDMD_CLOCKMODE_C: + data[0] = 0x02; + break; + + default: + return -EINVAL; + } + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x30, data[0]); + if (ret) + return ret; + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x22, 0x00); + if (ret) + return ret; + + usleep_range(2000, 3000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x10, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0x00) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + spll_reset_seq4, + ARRAY_SIZE(spll_reset_seq4)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + spll_reset_seq5, + ARRAY_SIZE(spll_reset_seq5)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + memset(data, 0x00, sizeof(data)); + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x26, data, 4); +} + +static int t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on) +{ + u8 data[3] = { 0 }; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq1, + ARRAY_SIZE(t_power_x_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + t_power_x_seq2, + ARRAY_SIZE(t_power_x_seq2)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq3, + ARRAY_SIZE(t_power_x_seq3)); + if (ret) + return ret; + + if (on) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2b, 0x01); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x12, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq4, + ARRAY_SIZE(t_power_x_seq4)); + if (ret) + return ret; + } else { + data[0] = 0x03; + data[1] = 0x00; + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2a, data, 2); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x13, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0) + return -EINVAL; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq5, + ARRAY_SIZE(t_power_x_seq5)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, data, 1); + if (ret) + return ret; + if ((data[0] & 0x01) == 0) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + t_power_x_seq6, + ARRAY_SIZE(t_power_x_seq6)); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + t_power_x_seq7, + ARRAY_SIZE(t_power_x_seq7)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + memset(data, 0x00, sizeof(data)); + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x27, data, 3); +} + +struct cxd2880_tnrdmd_ts_clk_cfg { + u8 srl_clk_mode; + u8 srl_duty_mode; + u8 ts_clk_period; +}; + +static int set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys) +{ + int ret; + u8 backwards_compatible = 0; + struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg; + u8 ts_rate_ctrl_off = 0; + u8 ts_in_off = 0; + u8 ts_clk_manaul_on = 0; + u8 data = 0; + + static const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = { + { + {3, 1, 8,}, + {0, 2, 16,} + }, { + {1, 1, 8,}, + {2, 2, 16,} + } + }; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + if (tnr_dmd->is_ts_backwards_compatible_mode) { + backwards_compatible = 1; + ts_rate_ctrl_off = 1; + ts_in_off = 1; + } else { + backwards_compatible = 0; + ts_rate_ctrl_off = 0; + ts_in_off = 0; + } + + if (tnr_dmd->ts_byte_clk_manual_setting) { + ts_clk_manaul_on = 1; + ts_rate_ctrl_off = 0; + } + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xd3, ts_rate_ctrl_off, 0x01); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xde, ts_in_off, 0x01); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xda, ts_clk_manaul_on, 0x01); + if (ret) + return ret; + + ts_clk_cfg = srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts] + [tnr_dmd->srl_ts_clk_frq]; + + if (tnr_dmd->ts_byte_clk_manual_setting) + ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc4, ts_clk_cfg.srl_clk_mode, 0x03); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xd1, ts_clk_cfg.srl_duty_mode, 0x03); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, 0xd9, + ts_clk_cfg.ts_clk_period); + if (ret) + return ret; + + data = backwards_compatible ? 0x00 : 0x01; + + if (sys == CXD2880_DTV_SYS_DVBT) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + ret = + cxd2880_io_set_reg_bits(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x66, data, 0x01); + } + + return ret; +} + +static int pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_pid_ftr_cfg + *pid_ftr_cfg) +{ + int i; + int ret; + u8 data[65]; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + if (!pid_ftr_cfg) + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x50, 0x02); + + data[0] = pid_ftr_cfg->is_negative ? 0x01 : 0x00; + + for (i = 0; i < 32; i++) { + if (pid_ftr_cfg->pid_cfg[i].is_en) { + data[1 + (i * 2)] = (pid_ftr_cfg->pid_cfg[i].pid >> 8) | 0x20; + data[2 + (i * 2)] = pid_ftr_cfg->pid_cfg[i].pid & 0xff; + } else { + data[1 + (i * 2)] = 0x00; + data[2 + (i * 2)] = 0x00; + } + } + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x50, data, 65); +} + +static int load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + u8 i; + + if (!tnr_dmd) + return -EINVAL; + + for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + tnr_dmd->cfg_mem[i].tgt, + 0x00, tnr_dmd->cfg_mem[i].bank); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + tnr_dmd->cfg_mem[i].tgt, + tnr_dmd->cfg_mem[i].address, + tnr_dmd->cfg_mem[i].value, + tnr_dmd->cfg_mem[i].bit_mask); + if (ret) + return ret; + } + + return 0; +} + +static int set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_io_tgt tgt, + u8 bank, u8 address, u8 value, u8 bit_mask) +{ + u8 i; + u8 value_stored = 0; + + if (!tnr_dmd) + return -EINVAL; + + for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) { + if (value_stored == 0 && + tnr_dmd->cfg_mem[i].tgt == tgt && + tnr_dmd->cfg_mem[i].bank == bank && + tnr_dmd->cfg_mem[i].address == address) { + tnr_dmd->cfg_mem[i].value &= ~bit_mask; + tnr_dmd->cfg_mem[i].value |= (value & bit_mask); + + tnr_dmd->cfg_mem[i].bit_mask |= bit_mask; + + value_stored = 1; + } + } + + if (value_stored) + return 0; + + if (tnr_dmd->cfg_mem_last_entry < CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) { + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt; + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank = bank; + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address = address; + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value = (value & bit_mask); + tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask = bit_mask; + tnr_dmd->cfg_mem_last_entry++; + } else { + return -ENOMEM; + } + + return 0; +} + +int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_io *io, + struct cxd2880_tnrdmd_create_param + *create_param) +{ + if (!tnr_dmd || !io || !create_param) + return -EINVAL; + + memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd)); + + tnr_dmd->io = io; + tnr_dmd->create_param = *create_param; + + tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE; + tnr_dmd->diver_sub = NULL; + + tnr_dmd->srl_ts_clk_mod_cnts = 1; + tnr_dmd->en_fef_intmtnt_base = 1; + tnr_dmd->en_fef_intmtnt_lite = 1; + tnr_dmd->rf_lvl_cmpstn = NULL; + tnr_dmd->lna_thrs_tbl_air = NULL; + tnr_dmd->lna_thrs_tbl_cable = NULL; + atomic_set(&tnr_dmd->cancel, 0); + + return 0; +} + +int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd + *tnr_dmd_main, + struct cxd2880_io *io_main, + struct cxd2880_tnrdmd *tnr_dmd_sub, + struct cxd2880_io *io_sub, + struct + cxd2880_tnrdmd_diver_create_param + *create_param) +{ + struct cxd2880_tnrdmd_create_param *main_param, *sub_param; + + if (!tnr_dmd_main || !io_main || !tnr_dmd_sub || !io_sub || + !create_param) + return -EINVAL; + + memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd)); + memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd)); + + main_param = &tnr_dmd_main->create_param; + sub_param = &tnr_dmd_sub->create_param; + + tnr_dmd_main->io = io_main; + tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN; + tnr_dmd_main->diver_sub = tnr_dmd_sub; + tnr_dmd_main->create_param.en_internal_ldo = + create_param->en_internal_ldo; + + main_param->ts_output_if = create_param->ts_output_if; + main_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_MASTER; + main_param->xosc_cap = create_param->xosc_cap_main; + main_param->xosc_i = create_param->xosc_i_main; + main_param->is_cxd2881gg = create_param->is_cxd2881gg; + main_param->stationary_use = create_param->stationary_use; + + tnr_dmd_sub->io = io_sub; + tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB; + tnr_dmd_sub->diver_sub = NULL; + + sub_param->en_internal_ldo = create_param->en_internal_ldo; + sub_param->ts_output_if = create_param->ts_output_if; + sub_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_SLAVE; + sub_param->xosc_cap = 0; + sub_param->xosc_i = create_param->xosc_i_sub; + sub_param->is_cxd2881gg = create_param->is_cxd2881gg; + sub_param->stationary_use = create_param->stationary_use; + + tnr_dmd_main->srl_ts_clk_mod_cnts = 1; + tnr_dmd_main->en_fef_intmtnt_base = 1; + tnr_dmd_main->en_fef_intmtnt_lite = 1; + tnr_dmd_main->rf_lvl_cmpstn = NULL; + tnr_dmd_main->lna_thrs_tbl_air = NULL; + tnr_dmd_main->lna_thrs_tbl_cable = NULL; + + tnr_dmd_sub->srl_ts_clk_mod_cnts = 1; + tnr_dmd_sub->en_fef_intmtnt_base = 1; + tnr_dmd_sub->en_fef_intmtnt_lite = 1; + tnr_dmd_sub->rf_lvl_cmpstn = NULL; + tnr_dmd_sub->lna_thrs_tbl_air = NULL; + tnr_dmd_sub->lna_thrs_tbl_cable = NULL; + + return 0; +} + +int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + + if (!tnr_dmd || tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN; + tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN; + tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN; + tnr_dmd->frequency_khz = 0; + tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN; + tnr_dmd->scan_mode = 0; + atomic_set(&tnr_dmd->cancel, 0); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN; + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN; + tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN; + tnr_dmd->diver_sub->frequency_khz = 0; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN; + tnr_dmd->diver_sub->scan_mode = 0; + atomic_set(&tnr_dmd->diver_sub->cancel, 0); + } + + ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id); + if (ret) + return ret; + + if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id)) + return -ENOTTY; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub, + &tnr_dmd->diver_sub->chip_id); + if (ret) + return ret; + + if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id)) + return -ENOTTY; + } + + ret = p_init1(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = p_init1(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + usleep_range(1000, 2000); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = p_init2(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = p_init2(tnr_dmd); + if (ret) + return ret; + + usleep_range(5000, 6000); + + ret = p_init3(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = p_init3(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = rf_init1(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = rf_init1(tnr_dmd->diver_sub); + + return ret; +} + +int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 cpu_task_completed; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + ret = cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd, + &cpu_task_completed); + if (ret) + return ret; + + if (!cpu_task_completed) + return -EINVAL; + + ret = rf_init2(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = rf_init2(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = load_cfg_mem(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = load_cfg_mem(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP; + + return ret; +} + +int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *task_completed) +{ + u16 cpu_status = 0; + int ret; + + if (!tnr_dmd || !task_completed) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (cpu_status == 0) + *task_completed = 1; + else + *task_completed = 0; + + return 0; + } + if (cpu_status != 0) { + *task_completed = 0; + return 0; + } + + ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status); + if (ret) + return ret; + + if (cpu_status == 0) + *task_completed = 1; + else + *task_completed = 0; + + return ret; +} + +int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u32 frequency_khz, + enum cxd2880_dtv_bandwidth + bandwidth, u8 one_seg_opt, + u8 one_seg_opt_shft_dir) +{ + u8 data; + enum cxd2880_tnrdmd_clockmode new_clk_mode = + CXD2880_TNRDMD_CLOCKMODE_A; + int shift_frequency_khz; + u8 cpu_task_completed; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (frequency_khz < 4000) + return -EINVAL; + + ret = cxd2880_tnrdmd_sleep(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, + 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x2b, + &data, + 1); + if (ret) + return ret; + + switch (sys) { + case CXD2880_DTV_SYS_DVBT: + if (data == 0x00) { + ret = t_power_x(tnr_dmd, 1); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = t_power_x(tnr_dmd->diver_sub, 1); + if (ret) + return ret; + } + } + break; + + case CXD2880_DTV_SYS_DVBT2: + if (data == 0x01) { + ret = t_power_x(tnr_dmd, 0); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = t_power_x(tnr_dmd->diver_sub, 0); + if (ret) + return ret; + } + } + break; + + default: + return -EINVAL; + } + + ret = spll_reset(tnr_dmd, new_clk_mode); + if (ret) + return ret; + + tnr_dmd->clk_mode = new_clk_mode; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode); + if (ret) + return ret; + + tnr_dmd->diver_sub->clk_mode = new_clk_mode; + } + + ret = load_cfg_mem(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = load_cfg_mem(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + if (one_seg_opt) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + shift_frequency_khz = 350; + } else { + if (one_seg_opt_shft_dir) + shift_frequency_khz = 350; + else + shift_frequency_khz = -350; + + if (tnr_dmd->create_param.xtal_share_type == + CXD2880_TNRDMD_XTAL_SHARE_SLAVE) + shift_frequency_khz *= -1; + } + } else { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + shift_frequency_khz = 150; + } else { + switch (tnr_dmd->create_param.xtal_share_type) { + case CXD2880_TNRDMD_XTAL_SHARE_NONE: + case CXD2880_TNRDMD_XTAL_SHARE_EXTREF: + default: + shift_frequency_khz = 0; + break; + case CXD2880_TNRDMD_XTAL_SHARE_MASTER: + shift_frequency_khz = 150; + break; + case CXD2880_TNRDMD_XTAL_SHARE_SLAVE: + shift_frequency_khz = -150; + break; + } + } + } + + ret = + x_tune1(tnr_dmd, sys, frequency_khz, bandwidth, + tnr_dmd->is_cable_input, shift_frequency_khz); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune1(tnr_dmd->diver_sub, sys, frequency_khz, + bandwidth, tnr_dmd->is_cable_input, + -shift_frequency_khz); + if (ret) + return ret; + } + + usleep_range(10000, 11000); + + ret = + cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd, + &cpu_task_completed); + if (ret) + return ret; + + if (!cpu_task_completed) + return -EINVAL; + + ret = + x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode, + shift_frequency_khz); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune2(tnr_dmd->diver_sub, bandwidth, + tnr_dmd->diver_sub->clk_mode, + -shift_frequency_khz); + if (ret) + return ret; + } + + if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) { + ret = set_ts_clk_mode_and_freq(tnr_dmd, sys); + } else { + struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg; + + if (tnr_dmd->pid_ftr_cfg_en) + pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg; + else + pid_ftr_cfg = NULL; + + ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg); + } + + return ret; +} + +int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 en_fef_intmtnt_ctrl) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl); + if (ret) + return ret; + ret = x_tune4(tnr_dmd); + if (ret) + return ret; + } + + return cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1); +} + +int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP) + return 0; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep1(tnr_dmd); + if (ret) + return ret; + } + + ret = x_sleep2(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep2(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + switch (tnr_dmd->sys) { + case CXD2880_DTV_SYS_DVBT: + ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd); + if (ret) + return ret; + break; + + case CXD2880_DTV_SYS_DVBT2: + ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + ret = x_sleep3(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep3(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + ret = x_sleep4(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = x_sleep4(tnr_dmd->diver_sub); + if (ret) + return ret; + } + + tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP; + tnr_dmd->frequency_khz = 0; + tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP; + tnr_dmd->diver_sub->frequency_khz = 0; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN; + tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN; + } + + return 0; +} + +int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_cfg_id id, + int value) +{ + int ret = 0; + u8 data[2] = { 0 }; + u8 need_sub_setting = 0; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + switch (id) { + case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc4, + value ? 0x00 : 0x10, + 0x10); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc5, + value ? 0x00 : 0x02, + 0x02); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc5, + value ? 0x00 : 0x04, + 0x04); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xcb, + value ? 0x00 : 0x01, + 0x01); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc5, + value ? 0x01 : 0x00, + 0x01); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSCLK_CONT: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + tnr_dmd->srl_ts_clk_mod_cnts = value ? 0x01 : 0x00; + break; + + case CXD2880_TNRDMD_CFG_TSCLK_MASK: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0x1f) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc6, value, + 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSVALID_MASK: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0x1f) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc8, value, + 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSERR_MASK: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0x1f) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xc9, value, + 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x91, + value ? 0x01 : 0x00, + 0x01); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSPIN_CURRENT: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x51, value, + 0x3f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x50, + value ? 0x80 : 0x00, + 0x80); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSPIN_PULLUP: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x50, value, + 0x3f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TSCLK_FREQ: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 1) + return -EINVAL; + + tnr_dmd->srl_ts_clk_frq = + (enum cxd2880_tnrdmd_serial_ts_clk)value; + break; + + case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 0xff) + return -EINVAL; + + tnr_dmd->ts_byte_clk_manual_setting = value; + + break; + + case CXD2880_TNRDMD_CFG_TS_PACKET_GAP: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (value < 0 || value > 7) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0xd6, value, + 0x07); + if (ret) + return ret; + + break; + + case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE: + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0; + + break; + + case CXD2880_TNRDMD_CFG_PWM_VALUE: + if (value < 0 || value > 0x1000) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x22, + value ? 0x01 : 0x00, + 0x01); + if (ret) + return ret; + + data[0] = (value >> 8) & 0x1f; + data[1] = value & 0xff; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x23, + data[0], 0x1f); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x24, + data[1], 0xff); + if (ret) + return ret; + + break; + + case CXD2880_TNRDMD_CFG_INTERRUPT: + data[0] = (value >> 8) & 0xff; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x48, data[0], + 0xff); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x49, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL: + data[0] = value & 0x07; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x4a, data[0], + 0x07); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL: + data[0] = (value & 0x07) << 3; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x4a, data[0], + 0x38); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE: + if (value < CXD2880_TNRDMD_CLOCKMODE_UNKNOWN || + value > CXD2880_TNRDMD_CLOCKMODE_C) + return -EINVAL; + tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value; + break; + + case CXD2880_TNRDMD_CFG_CABLE_INPUT: + tnr_dmd->is_cable_input = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE: + tnr_dmd->en_fef_intmtnt_base = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE: + tnr_dmd->en_fef_intmtnt_lite = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS: + data[0] = (value >> 8) & 0x07; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x99, data[0], + 0x07); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9a, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS: + data[0] = (value >> 8) & 0x07; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9b, data[0], + 0x07); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9c, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS: + data[0] = (value >> 8) & 0x07; + data[1] = value & 0xff; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9d, data[0], + 0x07); + if (ret) + return ret; + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x00, 0x9e, data[1], + 0xff); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST: + tnr_dmd->blind_tune_dvbt2_first = value ? 1 : 0; + break; + + case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD: + if (value < 0 || value > 31) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x10, 0x60, + value & 0x1f, 0x1f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD: + if (value < 0 || value > 7) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x10, 0x6f, + value & 0x07, 0x07); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x20, 0x72, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x20, 0x6f, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT_PER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x10, 0x5c, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + case CXD2880_TNRDMD_CFG_DVBT2_PER_MES: + if (value < 0 || value > 15) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_DMD, + 0x24, 0xdc, + value & 0x0f, 0x0f); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + if (need_sub_setting && + tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value); + + return ret; +} + +int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode mode, + u8 open_drain, u8 invert) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (id > 2) + return -EINVAL; + + if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS, + 0x00, 0x40 + id, mode, + 0x0f); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS, + 0x00, 0x43, + open_drain ? (1 << id) : 0, + 1 << id); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS, + 0x00, 0x44, + invert ? (1 << id) : 0, + 1 << id); + if (ret) + return ret; + + return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x45, + en ? 0 : (1 << id), + 1 << id); +} + +int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode + mode, u8 open_drain, u8 invert) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode, + open_drain, invert); +} + +int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd || !value) + return -EINVAL; + + if (id > 2) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x20, &data, 1); + if (ret) + return ret; + + *value = (data >> id) & 0x01; + + return 0; +} + +int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value); +} + +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value) +{ + if (!tnr_dmd) + return -EINVAL; + + if (id > 2) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, + CXD2880_IO_TGT_SYS, + 0x00, 0x46, + value ? (1 << id) : 0, + 1 << id); +} + +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value); +} + +int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd, + u16 *value) +{ + int ret; + u8 data[2] = { 0 }; + + if (!tnr_dmd || !value) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x0a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x15, data, 2); + if (ret) + return ret; + + *value = (data[0] << 8) | data[1]; + + return 0; +} + +int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd, + u16 value) +{ + int ret; + u8 data[2] = { 0 }; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + data[0] = (value >> 8) & 0xff; + data[1] = value & 0xff; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x3c, data, 2); +} + +int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd, + u8 clear_overflow_flag, + u8 clear_underflow_flag, + u8 clear_buf) +{ + int ret; + u8 data[2] = { 0 }; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + data[0] = clear_overflow_flag ? 0x02 : 0x00; + data[0] |= clear_underflow_flag ? 0x01 : 0x00; + data[1] = clear_buf ? 0x01 : 0x00; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x9f, data, 2); +} + +int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_chip_id *chip_id) +{ + int ret; + u8 data = 0; + + if (!tnr_dmd || !chip_id) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0xfd, &data, 1); + if (ret) + return ret; + + *chip_id = (enum cxd2880_tnrdmd_chip_id)data; + + return 0; +} + +int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_io_tgt tgt, + u8 bank, u8 address, + u8 value, u8 bit_mask) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, tgt, 0x00, bank); + if (ret) + return ret; + + ret = cxd2880_io_set_reg_bits(tnr_dmd->io, + tgt, address, value, bit_mask); + if (ret) + return ret; + + return set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask); +} + +int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 scan_mode_end) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + tnr_dmd->scan_mode = scan_mode_end; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + return cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys, + scan_mode_end); + else + return 0; +} + +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_pid_ftr_cfg + *pid_ftr_cfg) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) + return -ENOTTY; + + if (pid_ftr_cfg) { + tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg; + tnr_dmd->pid_ftr_cfg_en = 1; + } else { + tnr_dmd->pid_ftr_cfg_en = 0; + } + + if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) + return pid_ftr_setting(tnr_dmd, pid_ftr_cfg); + else + return 0; +} + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd + *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)) +{ + if (!tnr_dmd) + return -EINVAL; + + tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn; + + return 0; +} + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd + *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub, + rf_lvl_cmpstn); +} + +int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable) +{ + if (!tnr_dmd) + return -EINVAL; + + tnr_dmd->lna_thrs_tbl_air = tbl_air; + tnr_dmd->lna_thrs_tbl_cable = tbl_cable; + + return 0; +} + +int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd, + struct + cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable) +{ + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub, + tbl_air, tbl_cable); +} + +int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd + *tnr_dmd, u8 en, u8 value) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) + return -EINVAL; + + if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS) + return -ENOTTY; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x00); + if (ret) + return ret; + + if (en) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x50, ((value & 0x1f) | 0x80)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x52, (value & 0x1f)); + } else { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + set_ts_pin_seq, + ARRAY_SIZE(set_ts_pin_seq)); + if (ret) + return ret; + + ret = load_cfg_mem(tnr_dmd); + } + + return ret; +} + +int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd, + u8 en) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + switch (tnr_dmd->create_param.ts_output_if) { + case CXD2880_TNRDMD_TSOUT_IF_TS: + if (en) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + set_ts_output_seq1, + ARRAY_SIZE(set_ts_output_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + set_ts_output_seq2, + ARRAY_SIZE(set_ts_output_seq2)); + if (ret) + return ret; + } else { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + set_ts_output_seq3, + ARRAY_SIZE(set_ts_output_seq3)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + set_ts_output_seq4, + ARRAY_SIZE(set_ts_output_seq4)); + if (ret) + return ret; + } + break; + + case CXD2880_TNRDMD_TSOUT_IF_SPI: + break; + + case CXD2880_TNRDMD_TSOUT_IF_SDIO: + break; + + default: + return -EINVAL; + } + + return 0; +} + +int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 data; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + switch (tnr_dmd->create_param.ts_output_if) { + case CXD2880_TNRDMD_TSOUT_IF_SPI: + case CXD2880_TNRDMD_TSOUT_IF_SDIO: + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, &data, 1); + if (ret) + return ret; + + break; + case CXD2880_TNRDMD_TSOUT_IF_TS: + default: + break; + } + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x01, 0x01); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h new file mode 100644 index 0000000000..9d809a251f --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h @@ -0,0 +1,365 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common control interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_H +#define CXD2880_TNRDMD_H + +#include <linux/atomic.h> + +#include "cxd2880_common.h" +#include "cxd2880_io.h" +#include "cxd2880_dtv.h" +#include "cxd2880_dvbt.h" +#include "cxd2880_dvbt2.h" + +#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100 + +#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\ +((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00))) + +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW 0x0001 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW 0x0002 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY 0x0004 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL 0x0008 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY 0x0010 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND 0x0020 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS 0x0040 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR 0x0100 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK 0x0200 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK 0x0400 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM 0x0800 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS 0x1000 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW 0x2000 +#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL 0x4000 + +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK 0x01 +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK 0x02 +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK 0x04 + +enum cxd2880_tnrdmd_chip_id { + CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00, + CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62, + CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6a +}; + +#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) \ + (((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \ + ((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) + +enum cxd2880_tnrdmd_state { + CXD2880_TNRDMD_STATE_UNKNOWN, + CXD2880_TNRDMD_STATE_SLEEP, + CXD2880_TNRDMD_STATE_ACTIVE, + CXD2880_TNRDMD_STATE_INVALID +}; + +enum cxd2880_tnrdmd_divermode { + CXD2880_TNRDMD_DIVERMODE_SINGLE, + CXD2880_TNRDMD_DIVERMODE_MAIN, + CXD2880_TNRDMD_DIVERMODE_SUB +}; + +enum cxd2880_tnrdmd_clockmode { + CXD2880_TNRDMD_CLOCKMODE_UNKNOWN, + CXD2880_TNRDMD_CLOCKMODE_A, + CXD2880_TNRDMD_CLOCKMODE_B, + CXD2880_TNRDMD_CLOCKMODE_C +}; + +enum cxd2880_tnrdmd_tsout_if { + CXD2880_TNRDMD_TSOUT_IF_TS, + CXD2880_TNRDMD_TSOUT_IF_SPI, + CXD2880_TNRDMD_TSOUT_IF_SDIO +}; + +enum cxd2880_tnrdmd_xtal_share { + CXD2880_TNRDMD_XTAL_SHARE_NONE, + CXD2880_TNRDMD_XTAL_SHARE_EXTREF, + CXD2880_TNRDMD_XTAL_SHARE_MASTER, + CXD2880_TNRDMD_XTAL_SHARE_SLAVE +}; + +enum cxd2880_tnrdmd_spectrum_sense { + CXD2880_TNRDMD_SPECTRUM_NORMAL, + CXD2880_TNRDMD_SPECTRUM_INV +}; + +enum cxd2880_tnrdmd_cfg_id { + CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB, + CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI, + CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI, + CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI, + CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE, + CXD2880_TNRDMD_CFG_TSCLK_CONT, + CXD2880_TNRDMD_CFG_TSCLK_MASK, + CXD2880_TNRDMD_CFG_TSVALID_MASK, + CXD2880_TNRDMD_CFG_TSERR_MASK, + CXD2880_TNRDMD_CFG_TSERR_VALID_DIS, + CXD2880_TNRDMD_CFG_TSPIN_CURRENT, + CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL, + CXD2880_TNRDMD_CFG_TSPIN_PULLUP, + CXD2880_TNRDMD_CFG_TSCLK_FREQ, + CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL, + CXD2880_TNRDMD_CFG_TS_PACKET_GAP, + CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE, + CXD2880_TNRDMD_CFG_PWM_VALUE, + CXD2880_TNRDMD_CFG_INTERRUPT, + CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL, + CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL, + CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS, + CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS, + CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS, + CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE, + CXD2880_TNRDMD_CFG_CABLE_INPUT, + CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE, + CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE, + CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST, + CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD, + CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD, + CXD2880_TNRDMD_CFG_DVBT_PER_MES, + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, + CXD2880_TNRDMD_CFG_DVBT2_PER_MES, +}; + +enum cxd2880_tnrdmd_lock_result { + CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT, + CXD2880_TNRDMD_LOCK_RESULT_LOCKED, + CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED +}; + +enum cxd2880_tnrdmd_gpio_mode { + CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00, + CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01, + CXD2880_TNRDMD_GPIO_MODE_INT = 0x02, + CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03, + CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04, + CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05, + CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06 +}; + +enum cxd2880_tnrdmd_serial_ts_clk { + CXD2880_TNRDMD_SERIAL_TS_CLK_FULL, + CXD2880_TNRDMD_SERIAL_TS_CLK_HALF +}; + +struct cxd2880_tnrdmd_cfg_mem { + enum cxd2880_io_tgt tgt; + u8 bank; + u8 address; + u8 value; + u8 bit_mask; +}; + +struct cxd2880_tnrdmd_pid_cfg { + u8 is_en; + u16 pid; +}; + +struct cxd2880_tnrdmd_pid_ftr_cfg { + u8 is_negative; + struct cxd2880_tnrdmd_pid_cfg pid_cfg[32]; +}; + +struct cxd2880_tnrdmd_lna_thrs { + u8 off_on; + u8 on_off; +}; + +struct cxd2880_tnrdmd_lna_thrs_tbl_air { + struct cxd2880_tnrdmd_lna_thrs thrs[24]; +}; + +struct cxd2880_tnrdmd_lna_thrs_tbl_cable { + struct cxd2880_tnrdmd_lna_thrs thrs[32]; +}; + +struct cxd2880_tnrdmd_create_param { + enum cxd2880_tnrdmd_tsout_if ts_output_if; + u8 en_internal_ldo; + enum cxd2880_tnrdmd_xtal_share xtal_share_type; + u8 xosc_cap; + u8 xosc_i; + u8 is_cxd2881gg; + u8 stationary_use; +}; + +struct cxd2880_tnrdmd_diver_create_param { + enum cxd2880_tnrdmd_tsout_if ts_output_if; + u8 en_internal_ldo; + u8 xosc_cap_main; + u8 xosc_i_main; + u8 xosc_i_sub; + u8 is_cxd2881gg; + u8 stationary_use; +}; + +struct cxd2880_tnrdmd { + struct cxd2880_tnrdmd *diver_sub; + struct cxd2880_io *io; + struct cxd2880_tnrdmd_create_param create_param; + enum cxd2880_tnrdmd_divermode diver_mode; + enum cxd2880_tnrdmd_clockmode fixed_clk_mode; + u8 is_cable_input; + u8 en_fef_intmtnt_base; + u8 en_fef_intmtnt_lite; + u8 blind_tune_dvbt2_first; + int (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db); + struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air; + struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable; + u8 srl_ts_clk_mod_cnts; + enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq; + u8 ts_byte_clk_manual_setting; + u8 is_ts_backwards_compatible_mode; + struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT]; + u8 cfg_mem_last_entry; + struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg; + u8 pid_ftr_cfg_en; + void *user; + enum cxd2880_tnrdmd_chip_id chip_id; + enum cxd2880_tnrdmd_state state; + enum cxd2880_tnrdmd_clockmode clk_mode; + u32 frequency_khz; + enum cxd2880_dtv_sys sys; + enum cxd2880_dtv_bandwidth bandwidth; + u8 scan_mode; + atomic_t cancel; +}; + +int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_io *io, + struct cxd2880_tnrdmd_create_param + *create_param); + +int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd + *tnr_dmd_main, + struct cxd2880_io *io_main, + struct cxd2880_tnrdmd *tnr_dmd_sub, + struct cxd2880_io *io_sub, + struct + cxd2880_tnrdmd_diver_create_param + *create_param); + +int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *task_completed); + +int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_sys sys, + u32 frequency_khz, + enum cxd2880_dtv_bandwidth + bandwidth, u8 one_seg_opt, + u8 one_seg_opt_shft_dir); + +int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 en_fef_intmtnt_ctrl); + +int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_cfg_id id, + int value); + +int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode mode, + u8 open_drain, u8 invert); + +int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, + u8 en, + enum cxd2880_tnrdmd_gpio_mode + mode, u8 open_drain, + u8 invert); + +int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value); + +int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 *value); + +int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value); + +int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 id, u8 value); + +int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd, + u16 *value); + +int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd, + u16 value); + +int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd, + u8 clear_overflow_flag, + u8 clear_underflow_flag, + u8 clear_buf); + +int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_tnrdmd_chip_id *chip_id); + +int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_io_tgt tgt, + u8 bank, u8 address, + u8 value, u8 bit_mask); + +int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dtv_sys sys, + u8 scan_mode_end); + +int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_tnrdmd_pid_ftr_cfg + *pid_ftr_cfg); + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd + *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)); + +int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd *tnr_dmd, + int (*rf_lvl_cmpstn) + (struct cxd2880_tnrdmd *, + int *)); + +int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd, + struct + cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct + cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable); + +int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd, + struct + cxd2880_tnrdmd_lna_thrs_tbl_air + *tbl_air, + struct + cxd2880_tnrdmd_lna_thrs_tbl_cable + *tbl_cable); + +int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd + *tnr_dmd, u8 en, u8 value); + +int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd, + u8 en); + +int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h new file mode 100644 index 0000000000..c6d6c8dd16 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_driver_version.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * version information + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.5" + +#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-04-25" diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c new file mode 100644 index 0000000000..c7e79da8c4 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c @@ -0,0 +1,919 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control functions for DVB-T + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <media/dvb_frontend.h> + +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_tnrdmd_dvbt_mon.h" + +static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = { + {0x00, 0x00}, {0x31, 0x01}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = { + {0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03}, + {0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = { + {0x00, 0x12}, {0x44, 0x00}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = { + {0x00, 0x11}, {0x87, 0xd2}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = { + {0x00, 0x00}, {0xfd, 0x01}, +}; + +static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = { + {0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00}, +}; + +static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = { + {0x00, 0x11}, {0x87, 0x04}, +}; + +static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_bandwidth + bandwidth, + enum cxd2880_tnrdmd_clockmode + clk_mode) +{ + static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 }; + static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 }; + static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 }; + static const u8 ratectl_margin[2] = { 0x01, 0xf0 }; + static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 }; + static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa }; + static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 }; + + static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00}; + static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa}; + static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 }; + static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 }; + static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 }; + static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 }; + static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 }; + static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 }; + static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 }; + + static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00}; + static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55}; + static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c }; + static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 }; + static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 }; + static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 }; + static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa }; + static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 }; + static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 }; + + static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00}; + static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38}; + static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 }; + static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 }; + static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c }; + static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 }; + static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 }; + static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff }; + static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 }; + + static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99}; + static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa}; + static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d }; + static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 }; + static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 }; + static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 }; + static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 }; + static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc }; + static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 }; + const u8 *data = NULL; + u8 sst_data; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + tune_dmd_setting_seq1, + ARRAY_SIZE(tune_dmd_setting_seq1)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = clk_mode_ckffrq_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = clk_mode_ckffrq_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = clk_mode_ckffrq_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x65, data, 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x5d, 0x07); + if (ret) + return ret; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + u8 data[2] = { 0x01, 0x01 }; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xce, data, 2); + if (ret) + return ret; + } + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq2, + ARRAY_SIZE(tune_dmd_setting_seq2)); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xf0, ratectl_margin, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN || + tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq3, + ARRAY_SIZE(tune_dmd_setting_seq3)); + if (ret) + return ret; + } + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq4, + ARRAY_SIZE(tune_dmd_setting_seq4)); + if (ret) + return ret; + } + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = maxclkcnt_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = maxclkcnt_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = maxclkcnt_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x68, data, 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + switch (bandwidth) { + case CXD2880_DTV_BW_8_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x00); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x35; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x34; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw8_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw8_notch[2], 2); + if (ret) + return ret; + break; + + case CXD2880_DTV_BW_7_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x02); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x2f; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x2e; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw7_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw7_notch[2], 2); + if (ret) + return ret; + break; + + case CXD2880_DTV_BW_6_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x04); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x29; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x2a; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw6_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw6_notch[2], 2); + if (ret) + return ret; + break; + + case CXD2880_DTV_BW_5_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, data, 5); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x06); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_gtdofst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_gtdofst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x7d, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_B: + sst_data = 0x24; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + sst_data = 0x23; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x71, sst_data); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, &data[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x51, &data[2], 3); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x72, &bw5_notch[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x6b, &bw5_notch[2], 2); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq5, + ARRAY_SIZE(tune_dmd_setting_seq5)); +} + +static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + sleep_dmd_setting_seq1, + ARRAY_SIZE(sleep_dmd_setting_seq1)); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + sleep_dmd_setting_seq2, + ARRAY_SIZE(sleep_dmd_setting_seq2)); + + return ret; +} + +static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt_profile profile) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x67, + (profile == CXD2880_DVBT_PROFILE_HP) + ? 0x00 : 0x01); +} + +int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT, + tune_param->center_freq_khz, + tune_param->bandwidth, 0, 0); + if (ret) + return ret; + + ret = + x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth, + tnr_dmd->clk_mode); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune_dvbt_demod_setting(tnr_dmd->diver_sub, + tune_param->bandwidth, + tnr_dmd->diver_sub->clk_mode); + if (ret) + return ret; + } + + return dvbt_set_profile(tnr_dmd, tune_param->profile); +} + +int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT, + 0); + if (ret) + return ret; + + tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->sys = CXD2880_DTV_SYS_DVBT; + tnr_dmd->bandwidth = tune_param->bandwidth; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT; + tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = x_sleep_dvbt_demod_setting(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub); + + return ret; +} + +int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; + } + + if (sync_stat == 6) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return 0; + } + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; +} + +int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (ts_lock) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; + } + + if (ts_lock) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return 0; + } else if (!unlock_detected) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + return 0; + } + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h new file mode 100644 index 0000000000..35d81ccc73 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control interface for DVB-T + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT_H +#define CXD2880_TNRDMD_DVBT_H + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" + +struct cxd2880_dvbt_tune_param { + u32 center_freq_khz; + enum cxd2880_dtv_bandwidth bandwidth; + enum cxd2880_dvbt_profile profile; +}; + +int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd + *tnr_dmd); + +int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c new file mode 100644 index 0000000000..a9ab983348 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c @@ -0,0 +1,1217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt2.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control functions for DVB-T2 + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include <media/dvb_frontend.h> + +#include "cxd2880_tnrdmd_dvbt2.h" +#include "cxd2880_tnrdmd_dvbt2_mon.h" + +static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = { + {0x00, 0x00}, {0x31, 0x02}, +}; + +static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = { + {0x00, 0x04}, {0x5d, 0x0b}, +}; + +static int x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dtv_bandwidth + bandwidth, + enum cxd2880_tnrdmd_clockmode + clk_mode) +{ + static const u8 tsif_settings[2] = { 0x01, 0x01 }; + static const u8 init_settings[14] = { + 0x07, 0x06, 0x01, 0xf0, 0x00, 0x00, 0x04, 0xb0, 0x00, 0x00, + 0x09, 0x9c, 0x0e, 0x4c + }; + static const u8 clk_mode_settings_a1[9] = { + 0x52, 0x49, 0x2c, 0x51, 0x51, 0x3d, 0x15, 0x29, 0x0c + }; + + static const u8 clk_mode_settings_b1[9] = { + 0x5d, 0x55, 0x32, 0x5c, 0x5c, 0x45, 0x17, 0x2e, 0x0d + }; + + static const u8 clk_mode_settings_c1[9] = { + 0x60, 0x00, 0x34, 0x5e, 0x5e, 0x47, 0x18, 0x2f, 0x0e + }; + + static const u8 clk_mode_settings_a2[13] = { + 0x04, 0xe7, 0x94, 0x92, 0x09, 0xcf, 0x7e, 0xd0, 0x49, + 0xcd, 0xcd, 0x1f, 0x5b + }; + + static const u8 clk_mode_settings_b2[13] = { + 0x05, 0x90, 0x27, 0x55, 0x0b, 0x20, 0x8f, 0xd6, 0xea, + 0xc8, 0xc8, 0x23, 0x91 + }; + + static const u8 clk_mode_settings_c2[13] = { + 0x05, 0xb8, 0xd8, 0x00, 0x0b, 0x72, 0x93, 0xf3, 0x00, + 0xcd, 0xcd, 0x24, 0x95 + }; + + static const u8 clk_mode_settings_a3[5] = { + 0x0b, 0x6a, 0xc9, 0x03, 0x33 + }; + static const u8 clk_mode_settings_b3[5] = { + 0x01, 0x02, 0xe4, 0x03, 0x39 + }; + static const u8 clk_mode_settings_c3[5] = { + 0x01, 0x02, 0xeb, 0x03, 0x3b + }; + + static const u8 gtdofst[2] = { 0x3f, 0xff }; + + static const u8 bw8_gtdofst_a[2] = { 0x19, 0xd2 }; + static const u8 bw8_nomi_ac[6] = { 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 bw8_nomi_b[6] = { 0x14, 0x6a, 0xaa, 0xaa, 0xab, 0x00 }; + static const u8 bw8_sst_a[2] = { 0x06, 0x2a }; + static const u8 bw8_sst_b[2] = { 0x06, 0x29 }; + static const u8 bw8_sst_c[2] = { 0x06, 0x28 }; + static const u8 bw8_mrc_a[9] = { + 0x28, 0x00, 0x50, 0x00, 0x60, 0x00, 0x00, 0x90, 0x00 + }; + static const u8 bw8_mrc_b[9] = { + 0x2d, 0x5e, 0x5a, 0xbd, 0x6c, 0xe3, 0x00, 0xa3, 0x55 + }; + static const u8 bw8_mrc_c[9] = { + 0x2e, 0xaa, 0x5d, 0x55, 0x70, 0x00, 0x00, 0xa8, 0x00 + }; + + static const u8 bw7_nomi_ac[6] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 bw7_nomi_b[6] = { 0x17, 0x55, 0x55, 0x55, 0x55, 0x00 }; + static const u8 bw7_sst_a[2] = { 0x06, 0x23 }; + static const u8 bw7_sst_b[2] = { 0x06, 0x22 }; + static const u8 bw7_sst_c[2] = { 0x06, 0x21 }; + static const u8 bw7_mrc_a[9] = { + 0x2d, 0xb6, 0x5b, 0x6d, 0x6d, 0xb6, 0x00, 0xa4, 0x92 + }; + static const u8 bw7_mrc_b[9] = { + 0x33, 0xda, 0x67, 0xb4, 0x7c, 0x71, 0x00, 0xba, 0xaa + }; + static const u8 bw7_mrc_c[9] = { + 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00 + }; + + static const u8 bw6_nomi_ac[6] = { 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const u8 bw6_nomi_b[6] = { 0x1b, 0x38, 0xe3, 0x8e, 0x39, 0x00 }; + static const u8 bw6_sst_a[2] = { 0x06, 0x1c }; + static const u8 bw6_sst_b[2] = { 0x06, 0x1b }; + static const u8 bw6_sst_c[2] = { 0x06, 0x1a }; + static const u8 bw6_mrc_a[9] = { + 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00 + }; + static const u8 bw6_mrc_b[9] = { + 0x3c, 0x7e, 0x78, 0xfc, 0x91, 0x2f, 0x00, 0xd9, 0xc7 + }; + static const u8 bw6_mrc_c[9] = { + 0x3e, 0x38, 0x7c, 0x71, 0x95, 0x55, 0x00, 0xdf, 0xff + }; + + static const u8 bw5_nomi_ac[6] = { 0x21, 0x99, 0x99, 0x99, 0x9a, 0x00 }; + static const u8 bw5_nomi_b[6] = { 0x20, 0xaa, 0xaa, 0xaa, 0xab, 0x00 }; + static const u8 bw5_sst_a[2] = { 0x06, 0x15 }; + static const u8 bw5_sst_b[2] = { 0x06, 0x15 }; + static const u8 bw5_sst_c[2] = { 0x06, 0x14 }; + static const u8 bw5_mrc_a[9] = { + 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xe6, 0x66 + }; + static const u8 bw5_mrc_b[9] = { + 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x01, 0x05, 0x55 + }; + static const u8 bw5_mrc_c[9] = { + 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x01, 0x0c, 0xcc + }; + + static const u8 bw1_7_nomi_a[6] = { + 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03 + }; + static const u8 bw1_7_nomi_c[6] = { + 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03 + }; + static const u8 bw1_7_nomi_b[6] = { + 0x65, 0x2b, 0xa4, 0xcd, 0xd8, 0x03 + }; + static const u8 bw1_7_sst_a[2] = { 0x06, 0x0c }; + static const u8 bw1_7_sst_b[2] = { 0x06, 0x0c }; + static const u8 bw1_7_sst_c[2] = { 0x06, 0x0b }; + static const u8 bw1_7_mrc_a[9] = { + 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x02, 0xc9, 0x8f + }; + static const u8 bw1_7_mrc_b[9] = { + 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x03, 0x29, 0x5d + }; + static const u8 bw1_7_mrc_c[9] = { + 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x03, 0x40, 0x7d + }; + + const u8 *data = NULL; + const u8 *data2 = NULL; + const u8 *data3 = NULL; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + tune_dmd_setting_seq1, + ARRAY_SIZE(tune_dmd_setting_seq1)); + if (ret) + return ret; + + ret = cxd2880_io_write_multi_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + tune_dmd_setting_seq2, + ARRAY_SIZE(tune_dmd_setting_seq2)); + if (ret) + return ret; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xce, tsif_settings, 2); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x20); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x8a, init_settings[0]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x90, init_settings[1]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x25); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xf0, &init_settings[2], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2a); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xdc, init_settings[4]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xde, init_settings[5]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2d); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x73, &init_settings[6], 4); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x8f, &init_settings[10], 4); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = clk_mode_settings_a1; + data2 = clk_mode_settings_a2; + data3 = clk_mode_settings_a3; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = clk_mode_settings_b1; + data2 = clk_mode_settings_b2; + data3 = clk_mode_settings_b3; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = clk_mode_settings_c1; + data2 = clk_mode_settings_c2; + data3 = clk_mode_settings_c3; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1d, &data[0], 3); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x22, data[3]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x24, data[4]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x26, data[5]); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x29, &data[6], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2d, data[8]); + if (ret) + return ret; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2e, &data2[0], 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x35, &data2[6], 7); + if (ret) + return ret; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x3c, &data3[0], 2); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x56, &data3[2], 3); + if (ret) + return ret; + + switch (bandwidth) { + case CXD2880_DTV_BW_8_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x00); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_gtdofst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = gtdofst; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, data, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw8_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw8_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw8_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_7_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x02); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw7_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw7_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw7_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_6_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x04); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw6_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw6_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw6_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_5_MHZ: + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_nomi_ac; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x06); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw5_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw5_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw5_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + case CXD2880_DTV_BW_1_7_MHZ: + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw1_7_nomi_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw1_7_nomi_c; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw1_7_nomi_b; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, data, 6); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4a, 0x03); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x19, gtdofst, 2); + if (ret) + return ret; + + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw1_7_sst_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw1_7_sst_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw1_7_sst_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, data, 2); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + switch (clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + data = bw1_7_mrc_a; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + data = bw1_7_mrc_b; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + data = bw1_7_mrc_c; + break; + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x4b, data, 9); + if (ret) + return ret; + } + break; + + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xfd, 0x01); +} + +static int x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + static const u8 difint_clip[] = { + 0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32 + }; + int ret = 0; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x1d); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x47, difint_clip, 12); + } + + return ret; +} + +static int dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt2_profile profile) +{ + u8 t2_mode_tune_mode = 0; + u8 seq_not2_dtime = 0; + u8 dtime1 = 0; + u8 dtime2 = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + switch (tnr_dmd->clk_mode) { + case CXD2880_TNRDMD_CLOCKMODE_A: + dtime1 = 0x27; + dtime2 = 0x0c; + break; + case CXD2880_TNRDMD_CLOCKMODE_B: + dtime1 = 0x2c; + dtime2 = 0x0d; + break; + case CXD2880_TNRDMD_CLOCKMODE_C: + dtime1 = 0x2e; + dtime2 = 0x0e; + break; + default: + return -EINVAL; + } + + switch (profile) { + case CXD2880_DVBT2_PROFILE_BASE: + t2_mode_tune_mode = 0x01; + seq_not2_dtime = dtime2; + break; + + case CXD2880_DVBT2_PROFILE_LITE: + t2_mode_tune_mode = 0x05; + seq_not2_dtime = dtime1; + break; + + case CXD2880_DVBT2_PROFILE_ANY: + t2_mode_tune_mode = 0x00; + seq_not2_dtime = dtime1; + break; + + default: + return -EINVAL; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x2e); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, t2_mode_tune_mode); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) + return ret; + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2c, seq_not2_dtime); +} + +int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN && + tune_param->profile == CXD2880_DVBT2_PROFILE_ANY) + return -ENOTTY; + + ret = + cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2, + tune_param->center_freq_khz, + tune_param->bandwidth, 0, 0); + if (ret) + return ret; + + ret = + x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth, + tnr_dmd->clk_mode); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub, + tune_param->bandwidth, + tnr_dmd->diver_sub->clk_mode); + if (ret) + return ret; + } + + ret = dvbt2_set_profile(tnr_dmd, tune_param->profile); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile); + if (ret) + return ret; + } + + if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO) + ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0); + else + ret = + cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0, + (u8)(tune_param->data_plp_id)); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param) +{ + u8 en_fef_intmtnt_ctrl = 1; + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + switch (tune_param->profile) { + case CXD2880_DVBT2_PROFILE_BASE: + en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base; + break; + case CXD2880_DVBT2_PROFILE_LITE: + en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite; + break; + case CXD2880_DVBT2_PROFILE_ANY: + if (tnr_dmd->en_fef_intmtnt_base && + tnr_dmd->en_fef_intmtnt_lite) + en_fef_intmtnt_ctrl = 1; + else + en_fef_intmtnt_ctrl = 0; + break; + default: + return -EINVAL; + } + + ret = + cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, + CXD2880_DTV_SYS_DVBT2, + en_fef_intmtnt_ctrl); + if (ret) + return ret; + + tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2; + tnr_dmd->bandwidth = tune_param->bandwidth; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { + tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE; + tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz; + tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2; + tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = x_sleep_dvbt2_demod_setting(tnr_dmd); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; + } + + if (sync_stat == 6) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return 0; + } + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (sync_stat == 6) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock) +{ + int ret; + + u8 sync_stat = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 unlock_detected_sub = 0; + + if (!tnr_dmd || !lock) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock, + &unlock_detected); + if (ret) + return ret; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + if (ts_lock) + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + else if (unlock_detected) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; + } + + if (ts_lock) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; + return 0; + } else if (!unlock_detected) { + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + return 0; + } + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat, + &unlock_detected_sub); + if (ret) + return ret; + + if (unlock_detected && unlock_detected_sub) + *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; + else + *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd + *tnr_dmd, u8 auto_plp, + u8 plp_id) +{ + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x23); + if (ret) + return ret; + + if (!auto_plp) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xaf, plp_id); + if (ret) + return ret; + } + + return tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xad, auto_plp ? 0x00 : 0x01); +} + +int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd + *tnr_dmd) +{ + struct cxd2880_dvbt2_ofdm ofdm; + static const u8 data[] = { 0, 8, 0, 16, 0, 32, 0, 64, 0, 128, 1, 0}; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) + return 0; + + ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm); + if (ret) + return ret; + + if (!ofdm.mixed) + return 0; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x1d); + if (ret) + return ret; + + return tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x47, data, 12); +} + +int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *l1_post_valid) +{ + int ret; + + u8 data; + + if (!tnr_dmd || !l1_post_valid) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &data, 1); + if (ret) + return ret; + + *l1_post_valid = data & 0x01; + + return ret; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h new file mode 100644 index 0000000000..7108e35400 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt2.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * control interface for DVB-T2 + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT2_H +#define CXD2880_TNRDMD_DVBT2_H + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" + +enum cxd2880_tnrdmd_dvbt2_tune_info { + CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK, + CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID +}; + +struct cxd2880_dvbt2_tune_param { + u32 center_freq_khz; + enum cxd2880_dtv_bandwidth bandwidth; + u16 data_plp_id; + enum cxd2880_dvbt2_profile profile; + enum cxd2880_tnrdmd_dvbt2_tune_info tune_info; +}; + +#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO 0xffff + +int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param); + +int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd + *tnr_dmd); + +int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_lock_result + *lock); + +int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd + *tnr_dmd, u8 auto_plp, + u8 plp_id); + +int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd + *tnr_dmd); + +int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *l1_post_valid); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c new file mode 100644 index 0000000000..4e173dd87e --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c @@ -0,0 +1,1878 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt2_mon.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T2 monitor functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt2.h" +#include "cxd2880_tnrdmd_dvbt2_mon.h" + +#include <linux/int_log.h> + +static const int ref_dbm_1000[4][8] = { + {-96000, -95000, -94000, -93000, -92000, -92000, -98000, -97000}, + {-91000, -89000, -88000, -87000, -86000, -86000, -93000, -92000}, + {-86000, -85000, -83000, -82000, -81000, -80000, -89000, -88000}, + {-82000, -80000, -78000, -76000, -75000, -74000, -86000, -84000}, +}; + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected) +{ + u8 data; + int ret; + + if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, &data, sizeof(data)); + if (ret) + return ret; + + *sync_stat = data & 0x07; + *ts_lock_stat = ((data & 0x20) ? 1 : 0); + *unlock_detected = ((data & 0x10) ? 1 : 0); + + if (*sync_stat == 0x07) + return -EAGAIN; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *sync_stat, + u8 *unlock_detected) +{ + u8 ts_lock_stat = 0; + + if (!tnr_dmd || !sync_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub, + sync_stat, + &ts_lock_stat, + unlock_detected); +} + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset) +{ + u8 data[4]; + u32 ctl_val = 0; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + int ret; + + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x30, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + ctl_val = + ((data[0] & 0x0f) << 24) | (data[1] << 16) | (data[2] << 8) + | (data[3]); + *offset = cxd2880_convert2s_complement(ctl_val, 28); + + switch (tnr_dmd->bandwidth) { + case CXD2880_DTV_BW_1_7_MHZ: + *offset = -1 * ((*offset) / 582); + break; + case CXD2880_DTV_BW_5_MHZ: + case CXD2880_DTV_BW_6_MHZ: + case CXD2880_DTV_BW_7_MHZ: + case CXD2880_DTV_BW_8_MHZ: + *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 940); + break; + default: + return -EINVAL; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset) +{ + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub, + offset); +} + +int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_l1pre + *l1_pre) +{ + u8 data[37]; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 version = 0; + enum cxd2880_dvbt2_profile profile; + int ret; + + if (!tnr_dmd || !l1_pre) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub + (tnr_dmd, &sync_state, &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } else { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + ret = cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd, &profile); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x61, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + slvt_unfreeze_reg(tnr_dmd); + + l1_pre->type = (enum cxd2880_dvbt2_l1pre_type)data[0]; + l1_pre->bw_ext = data[1] & 0x01; + l1_pre->s1 = (enum cxd2880_dvbt2_s1)(data[2] & 0x07); + l1_pre->s2 = data[3] & 0x0f; + l1_pre->l1_rep = data[4] & 0x01; + l1_pre->gi = (enum cxd2880_dvbt2_guard)(data[5] & 0x07); + l1_pre->papr = (enum cxd2880_dvbt2_papr)(data[6] & 0x0f); + l1_pre->mod = + (enum cxd2880_dvbt2_l1post_constell)(data[7] & 0x0f); + l1_pre->cr = (enum cxd2880_dvbt2_l1post_cr)(data[8] & 0x03); + l1_pre->fec = + (enum cxd2880_dvbt2_l1post_fec_type)(data[9] & 0x03); + l1_pre->l1_post_size = (data[10] & 0x03) << 16; + l1_pre->l1_post_size |= (data[11]) << 8; + l1_pre->l1_post_size |= (data[12]); + l1_pre->l1_post_info_size = (data[13] & 0x03) << 16; + l1_pre->l1_post_info_size |= (data[14]) << 8; + l1_pre->l1_post_info_size |= (data[15]); + l1_pre->pp = (enum cxd2880_dvbt2_pp)(data[16] & 0x0f); + l1_pre->tx_id_availability = data[17]; + l1_pre->cell_id = (data[18] << 8); + l1_pre->cell_id |= (data[19]); + l1_pre->network_id = (data[20] << 8); + l1_pre->network_id |= (data[21]); + l1_pre->sys_id = (data[22] << 8); + l1_pre->sys_id |= (data[23]); + l1_pre->num_frames = data[24]; + l1_pre->num_symbols = (data[25] & 0x0f) << 8; + l1_pre->num_symbols |= data[26]; + l1_pre->regen = data[27] & 0x07; + l1_pre->post_ext = data[28] & 0x01; + l1_pre->num_rf_freqs = data[29] & 0x07; + l1_pre->rf_idx = data[30] & 0x07; + version = (data[31] & 0x03) << 2; + version |= (data[32] & 0xc0) >> 6; + l1_pre->t2_version = (enum cxd2880_dvbt2_version)version; + l1_pre->l1_post_scrambled = (data[32] & 0x20) >> 5; + l1_pre->t2_base_lite = (data[32] & 0x10) >> 4; + l1_pre->crc32 = (data[33] << 24); + l1_pre->crc32 |= (data[34] << 16); + l1_pre->crc32 |= (data[35] << 8); + l1_pre->crc32 |= data[36]; + + if (profile == CXD2880_DVBT2_PROFILE_BASE) { + switch ((l1_pre->s2 >> 1)) { + case CXD2880_DVBT2_BASE_S2_M1K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M1K; + break; + case CXD2880_DVBT2_BASE_S2_M2K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M2K; + break; + case CXD2880_DVBT2_BASE_S2_M4K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M4K; + break; + case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT: + case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M8K; + break; + case CXD2880_DVBT2_BASE_S2_M16K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M16K; + break; + case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT: + case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M32K; + break; + default: + return -EAGAIN; + } + } else if (profile == CXD2880_DVBT2_PROFILE_LITE) { + switch ((l1_pre->s2 >> 1)) { + case CXD2880_DVBT2_LITE_S2_M2K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M2K; + break; + case CXD2880_DVBT2_LITE_S2_M4K_G_ANY: + l1_pre->fft_mode = CXD2880_DVBT2_M4K; + break; + case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT: + case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M8K; + break; + case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT: + case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2: + l1_pre->fft_mode = CXD2880_DVBT2_M16K; + break; + default: + return -EAGAIN; + } + } else { + return -EAGAIN; + } + + l1_pre->mixed = l1_pre->s2 & 0x01; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_version + *ver) +{ + u8 data[2]; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 version = 0; + int ret; + + if (!tnr_dmd || !ver) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub + (tnr_dmd, &sync_state, &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } else { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x80, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + version = ((data[0] & 0x03) << 2); + version |= ((data[1] & 0xc0) >> 6); + *ver = (enum cxd2880_dvbt2_version)version; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_ofdm *ofdm) +{ + u8 data[5]; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + int ret; + + if (!tnr_dmd || !ofdm) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + + ret = -EAGAIN; + + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd->diver_sub, + ofdm); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1d, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + ofdm->mixed = ((data[0] & 0x20) ? 1 : 0); + ofdm->is_miso = ((data[0] & 0x10) >> 4); + ofdm->mode = (enum cxd2880_dvbt2_mode)(data[0] & 0x07); + ofdm->gi = (enum cxd2880_dvbt2_guard)((data[1] & 0x70) >> 4); + ofdm->pp = (enum cxd2880_dvbt2_pp)(data[1] & 0x07); + ofdm->bw_ext = (data[2] & 0x10) >> 4; + ofdm->papr = (enum cxd2880_dvbt2_papr)(data[2] & 0x0f); + ofdm->num_symbols = (data[3] << 8) | data[4]; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd + *tnr_dmd, u8 *plp_ids, + u8 *num_plps) +{ + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !num_plps) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc1, num_plps, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (*num_plps == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EINVAL; + } + + if (!plp_ids) { + slvt_unfreeze_reg(tnr_dmd); + return 0; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc2, + plp_ids, + ((*num_plps > 62) ? + 62 : *num_plps)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (*num_plps > 62) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0c); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, plp_ids + 62, + *num_plps - 62); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + + slvt_unfreeze_reg(tnr_dmd); + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_plp + *plp_info) +{ + u8 data[20]; + u8 addr = 0; + u8 index = 0; + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !plp_info) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!l1_post_ok) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0xa9; + else + addr = 0x96; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + if (type == CXD2880_DVBT2_PLP_COMMON && !data[13]) + return -EAGAIN; + + plp_info->id = data[index++]; + plp_info->type = + (enum cxd2880_dvbt2_plp_type)(data[index++] & 0x07); + plp_info->payload = + (enum cxd2880_dvbt2_plp_payload)(data[index++] & 0x1f); + plp_info->ff = data[index++] & 0x01; + plp_info->first_rf_idx = data[index++] & 0x07; + plp_info->first_frm_idx = data[index++]; + plp_info->group_id = data[index++]; + plp_info->plp_cr = + (enum cxd2880_dvbt2_plp_code_rate)(data[index++] & 0x07); + plp_info->constell = + (enum cxd2880_dvbt2_plp_constell)(data[index++] & 0x07); + plp_info->rot = data[index++] & 0x01; + plp_info->fec = + (enum cxd2880_dvbt2_plp_fec)(data[index++] & 0x03); + plp_info->num_blocks_max = (data[index++] & 0x03) << 8; + plp_info->num_blocks_max |= data[index++]; + plp_info->frm_int = data[index++]; + plp_info->til_len = data[index++]; + plp_info->til_type = data[index++] & 0x01; + + plp_info->in_band_a_flag = data[index++] & 0x01; + plp_info->rsvd = data[index++] << 8; + plp_info->rsvd |= data[index++]; + + plp_info->in_band_b_flag = + (plp_info->rsvd & 0x8000) >> 15; + plp_info->plp_mode = + (enum cxd2880_dvbt2_plp_mode)((plp_info->rsvd & 0x000c) >> 2); + plp_info->static_flag = (plp_info->rsvd & 0x0002) >> 1; + plp_info->static_padding_flag = plp_info->rsvd & 0x0001; + plp_info->rsvd = (plp_info->rsvd & 0x7ff0) >> 4; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *plp_error) +{ + u8 data; + int ret; + + if (!tnr_dmd || !plp_error) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if ((data & 0x01) == 0x00) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xc0, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *plp_error = data & 0x01; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd + *tnr_dmd, u8 *l1_change) +{ + u8 data; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + int ret; + + if (!tnr_dmd || !l1_change) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) { + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub + (tnr_dmd, &sync_state, &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state < 5) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } else { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x5f, &data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + *l1_change = data & 0x01; + if (*l1_change) { + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x22); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x16, 0x01); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + slvt_unfreeze_reg(tnr_dmd); + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt2_l1post + *l1_post) +{ + u8 data[16]; + int ret; + + if (!tnr_dmd || !l1_post) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, data, sizeof(data)); + if (ret) + return ret; + + if (!(data[0] & 0x01)) + return -EAGAIN; + + l1_post->sub_slices_per_frame = (data[1] & 0x7f) << 8; + l1_post->sub_slices_per_frame |= data[2]; + l1_post->num_plps = data[3]; + l1_post->num_aux = data[4] & 0x0f; + l1_post->aux_cfg_rfu = data[5]; + l1_post->rf_idx = data[6] & 0x07; + l1_post->freq = data[7] << 24; + l1_post->freq |= data[8] << 16; + l1_post->freq |= data[9] << 8; + l1_post->freq |= data[10]; + l1_post->fef_type = data[11] & 0x0f; + l1_post->fef_length = data[12] << 16; + l1_post->fef_length |= data[13] << 8; + l1_post->fef_length |= data[14]; + l1_post->fef_intvl = data[15]; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_bbheader + *bbheader) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 data[14]; + u8 addr = 0; + int ret; + + if (!tnr_dmd || !bbheader) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!ts_lock) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) { + u8 l1_post_ok; + u8 data; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb6, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (data == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0x51; + else + addr = 0x42; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + bbheader->stream_input = + (enum cxd2880_dvbt2_stream)((data[0] >> 6) & 0x03); + bbheader->is_single_input_stream = (data[0] >> 5) & 0x01; + bbheader->is_constant_coding_modulation = + (data[0] >> 4) & 0x01; + bbheader->issy_indicator = (data[0] >> 3) & 0x01; + bbheader->null_packet_deletion = (data[0] >> 2) & 0x01; + bbheader->ext = data[0] & 0x03; + + bbheader->input_stream_identifier = data[1]; + bbheader->plp_mode = + (data[3] & 0x01) ? CXD2880_DVBT2_PLP_MODE_HEM : + CXD2880_DVBT2_PLP_MODE_NM; + bbheader->data_field_length = (data[4] << 8) | data[5]; + + if (bbheader->plp_mode == CXD2880_DVBT2_PLP_MODE_NM) { + bbheader->user_packet_length = + (data[6] << 8) | data[7]; + bbheader->sync_byte = data[8]; + bbheader->issy = 0; + } else { + bbheader->user_packet_length = 0; + bbheader->sync_byte = 0; + bbheader->issy = + (data[11] << 16) | (data[12] << 8) | data[13]; + } + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + u32 *ts_rate_bps) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 l1_post_ok = 0; + u8 data[4]; + u8 addr = 0; + + int ret; + + if (!tnr_dmd || !ts_rate_bps) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!ts_lock) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0xba; + else + addr = 0xa7; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, &data[0], 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if ((data[0] & 0x80) == 0x00) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x25); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) + addr = 0xa6; + else + addr = 0xaa; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + addr, &data[0], 4); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + *ts_rate_bps = ((data[0] & 0x07) << 24) | (data[1] << 16) | + (data[2] << 8) | data[3]; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 early_unlock = 0; + u8 data = 0; + int ret; + + if (!tnr_dmd || !sense) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, &ts_lock, + &early_unlock); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + + ret = -EAGAIN; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(tnr_dmd->diver_sub, + sense); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x2f, &data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *sense = + (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV : + CXD2880_TNRDMD_SPECTRUM_NORMAL; + + return 0; +} + +static int dvbt2_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd, + u16 *reg_value) +{ + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + u8 data[2]; + int ret; + + if (!tnr_dmd || !reg_value) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x13, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *reg_value = (data[0] << 8) | data[1]; + + return ret; +} + +static int dvbt2_calc_snr(struct cxd2880_tnrdmd *tnr_dmd, + u32 reg_value, int *snr) +{ + if (!tnr_dmd || !snr) + return -EINVAL; + + if (reg_value == 0) + return -EAGAIN; + + if (reg_value > 10876) + reg_value = 10876; + + *snr = intlog10(reg_value) - intlog10(12600 - reg_value); + *snr = (*snr + 839) / 1678 + 32000; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr) +{ + u16 reg_value = 0; + int ret; + + if (!tnr_dmd || !snr) + return -EINVAL; + + *snr = -1000 * 1000; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + ret = dvbt2_read_snr_reg(tnr_dmd, ®_value); + if (ret) + return ret; + + ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr); + } else { + int snr_main = 0; + int snr_sub = 0; + + ret = + cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main, + &snr_sub); + } + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, int *snr_sub) +{ + u16 reg_value = 0; + u32 reg_value_sum = 0; + int ret; + + if (!tnr_dmd || !snr || !snr_main || !snr_sub) + return -EINVAL; + + *snr = -1000 * 1000; + *snr_main = -1000 * 1000; + *snr_sub = -1000 * 1000; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = dvbt2_read_snr_reg(tnr_dmd, ®_value); + if (!ret) { + ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr_main); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + ret = dvbt2_read_snr_reg(tnr_dmd->diver_sub, ®_value); + if (!ret) { + ret = dvbt2_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + return dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr); +} + +int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen) +{ + int ret; + u8 data[3]; + + if (!tnr_dmd || !pen) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x39, data, sizeof(data)); + if (ret) + return ret; + + if (!(data[0] & 0x01)) + return -EAGAIN; + + *pen = ((data[1] << 8) | data[2]); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm) +{ + u8 ctl_val_reg[5]; + u8 nominal_rate_reg[5]; + u32 trl_ctl_val = 0; + u32 trcg_nominal_rate = 0; + int num; + int den; + int ret; + u8 sync_state = 0; + u8 ts_lock = 0; + u8 unlock_detected = 0; + s8 diff_upper = 0; + + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, + &ts_lock, + &unlock_detected); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (sync_state != 6) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x34, ctl_val_reg, + sizeof(ctl_val_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, nominal_rate_reg, + sizeof(nominal_rate_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + diff_upper = + (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f); + + if (diff_upper < -1 || diff_upper > 1) + return -EAGAIN; + + trl_ctl_val = ctl_val_reg[1] << 24; + trl_ctl_val |= ctl_val_reg[2] << 16; + trl_ctl_val |= ctl_val_reg[3] << 8; + trl_ctl_val |= ctl_val_reg[4]; + + trcg_nominal_rate = nominal_rate_reg[1] << 24; + trcg_nominal_rate |= nominal_rate_reg[2] << 16; + trcg_nominal_rate |= nominal_rate_reg[3] << 8; + trcg_nominal_rate |= nominal_rate_reg[4]; + + trl_ctl_val >>= 1; + trcg_nominal_rate >>= 1; + + if (diff_upper == 1) + num = + (int)((trl_ctl_val + 0x80000000u) - + trcg_nominal_rate); + else if (diff_upper == -1) + num = + -(int)((trcg_nominal_rate + 0x80000000u) - + trl_ctl_val); + else + num = (int)(trl_ctl_val - trcg_nominal_rate); + + den = (nominal_rate_reg[0] & 0x7f) << 24; + den |= nominal_rate_reg[1] << 16; + den |= nominal_rate_reg[2] << 8; + den |= nominal_rate_reg[3]; + den = (den + (390625 / 2)) / 390625; + + den >>= 1; + + if (num >= 0) + *ppm = (num + (den / 2)) / den; + else + *ppm = (num - (den / 2)) / den; + + return 0; +} + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *ppm) +{ + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub, + ppm); +} + +int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt2_plp_btype type, + enum cxd2880_dvbt2_plp_constell *qam) +{ + u8 data; + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !qam) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb6, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (data == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb1, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } else { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x9e, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + + slvt_unfreeze_reg(tnr_dmd); + + *qam = (enum cxd2880_dvbt2_plp_constell)(data & 0x07); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + enum + cxd2880_dvbt2_plp_code_rate + *code_rate) +{ + u8 data; + u8 l1_post_ok = 0; + int ret; + + if (!tnr_dmd || !code_rate) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x86, &l1_post_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (!(l1_post_ok & 0x01)) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + if (type == CXD2880_DVBT2_PLP_COMMON) { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb6, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + if (data == 0) { + slvt_unfreeze_reg(tnr_dmd); + return -EAGAIN; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xb0, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } else { + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x9d, &data, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + } + + slvt_unfreeze_reg(tnr_dmd); + + *code_rate = (enum cxd2880_dvbt2_plp_code_rate)(data & 0x07); + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_profile + *profile) +{ + u8 data; + int ret; + + if (!tnr_dmd || !profile) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x22, &data, sizeof(data)); + if (ret) + return ret; + + if (data & 0x02) { + if (data & 0x01) + *profile = CXD2880_DVBT2_PROFILE_LITE; + else + *profile = CXD2880_DVBT2_PROFILE_BASE; + } else { + ret = -EAGAIN; + if (tnr_dmd->diver_mode == + CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd->diver_sub, + profile); + + return ret; + } + + return 0; +} + +static int dvbt2_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd, + int rf_lvl, u8 *ssi) +{ + enum cxd2880_dvbt2_plp_constell qam; + enum cxd2880_dvbt2_plp_code_rate code_rate; + int prel; + int temp_ssi = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam); + if (ret) + return ret; + + ret = + cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA, + &code_rate); + if (ret) + return ret; + + if (code_rate > CXD2880_DVBT2_R2_5 || qam > CXD2880_DVBT2_QAM256) + return -EINVAL; + + prel = rf_lvl - ref_dbm_1000[qam][code_rate]; + + if (prel < -15000) + temp_ssi = 0; + else if (prel < 0) + temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000; + else if (prel < 20000) + temp_ssi = (((4 * prel) + 500) / 1000) + 10; + else if (prel < 35000) + temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90; + else + temp_ssi = 100; + + *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi; + + return ret; +} + +int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl); + if (ret) + return ret; + + return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi); +} + +int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl); + if (ret) + return ret; + + return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h new file mode 100644 index 0000000000..5b7adaceff --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt2_mon.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T2 monitor interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT2_MON_H +#define CXD2880_TNRDMD_DVBT2_MON_H + +#include "cxd2880_tnrdmd.h" +#include "cxd2880_dvbt2.h" + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *sync_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset); + +int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset); + +int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_l1pre + *l1_pre); + +int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_version + *ver); + +int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_ofdm *ofdm); + +int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd + *tnr_dmd, u8 *plp_ids, + u8 *num_plps); + +int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_plp + *plp_info); + +int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd + *tnr_dmd, + u8 *plp_error); + +int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd + *tnr_dmd, u8 *l1_change); + +int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt2_l1post + *l1_post); + +int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + struct cxd2880_dvbt2_bbheader + *bbheader); + +int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_dvbt2_plp_btype + type, + u32 *ts_rate_bps); + +int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense); + +int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr); + +int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, + int *snr_sub); + +int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen); + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm); + +int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *ppm); + +int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd, + enum cxd2880_dvbt2_plp_btype type, + enum cxd2880_dvbt2_plp_constell + *qam); + +int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_plp_btype + type, + enum + cxd2880_dvbt2_plp_code_rate + *code_rate); + +int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt2_profile + *profile); + +int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi); + +int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *ssi); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c new file mode 100644 index 0000000000..86d5a1e402 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_dvbt_mon.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T monitor functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_tnrdmd_dvbt_mon.h" + +#include <linux/int_log.h> + +static const int ref_dbm_1000[3][5] = { + {-93000, -91000, -90000, -89000, -88000}, + {-87000, -85000, -84000, -83000, -82000}, + {-82000, -80000, -78000, -77000, -76000}, +}; + +static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd); + +int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected) +{ + u8 rdata = 0x00; + int ret; + + if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, &rdata, 1); + if (ret) + return ret; + + *unlock_detected = (rdata & 0x10) ? 1 : 0; + *sync_stat = rdata & 0x07; + *ts_lock_stat = (rdata & 0x20) ? 1 : 0; + + if (*sync_stat == 0x07) + return -EAGAIN; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *unlock_detected) +{ + u8 ts_lock_stat = 0; + + if (!tnr_dmd || !sync_stat || !unlock_detected) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub, + sync_stat, + &ts_lock_stat, + unlock_detected); +} + +int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt_mode + *mode, + enum cxd2880_dvbt_guard + *guard) +{ + u8 rdata = 0x00; + int ret; + + if (!tnr_dmd || !mode || !guard) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt_mon_mode_guard(tnr_dmd->diver_sub, + mode, guard); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1b, &rdata, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03); + *guard = (enum cxd2880_dvbt_guard)(rdata & 0x03); + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset) +{ + u8 rdata[4]; + u32 ctl_val = 0; + int ret; + + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1d, rdata, 4); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + ctl_val = + ((rdata[0] & 0x1f) << 24) | (rdata[1] << 16) | (rdata[2] << 8) | + (rdata[3]); + *offset = cxd2880_convert2s_complement(ctl_val, 29); + *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 235); + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset) +{ + if (!tnr_dmd || !offset) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub, + offset); +} + +int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt_tpsinfo + *info) +{ + u8 rdata[7]; + u8 cell_id_ok = 0; + int ret; + + if (!tnr_dmd || !info) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = + cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub, + info); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x29, rdata, 7); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x11); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0xd5, &cell_id_ok, 1); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + info->constellation = + (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03); + info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07); + info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07); + info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07); + info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03); + info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03); + info->fnum = (rdata[2] >> 6) & 0x03; + info->length_indicator = rdata[2] & 0x3f; + info->cell_id = (rdata[3] << 8) | rdata[4]; + info->reserved_even = rdata[5] & 0x3f; + info->reserved_odd = rdata[6] & 0x3f; + + info->cell_id_ok = cell_id_ok & 0x01; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen) +{ + u8 rdata[3]; + int ret; + + if (!tnr_dmd || !pen) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x26, rdata, 3); + if (ret) + return ret; + + if (!(rdata[0] & 0x01)) + return -EAGAIN; + + *pen = (rdata[1] << 8) | rdata[2]; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense) +{ + u8 data = 0; + int ret; + + if (!tnr_dmd || !sense) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) + ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(tnr_dmd->diver_sub, + sense); + + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x1c, &data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *sense = + (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV : + CXD2880_TNRDMD_SPECTRUM_NORMAL; + + return ret; +} + +static int dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd, + u16 *reg_value) +{ + u8 rdata[2]; + int ret; + + if (!tnr_dmd || !reg_value) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x13, rdata, 2); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + *reg_value = (rdata[0] << 8) | rdata[1]; + + return ret; +} + +static int dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd, + u32 reg_value, int *snr) +{ + if (!tnr_dmd || !snr) + return -EINVAL; + + if (reg_value == 0) + return -EAGAIN; + + if (reg_value > 4996) + reg_value = 4996; + + *snr = intlog10(reg_value) - intlog10(5350 - reg_value); + *snr = (*snr + 839) / 1678 + 28500; + + return 0; +} + +int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr) +{ + u16 reg_value = 0; + int ret; + + if (!tnr_dmd || !snr) + return -EINVAL; + + *snr = -1000 * 1000; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { + ret = dvbt_read_snr_reg(tnr_dmd, ®_value); + if (ret) + return ret; + + ret = dvbt_calc_snr(tnr_dmd, reg_value, snr); + } else { + int snr_main = 0; + int snr_sub = 0; + + ret = + cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main, + &snr_sub); + } + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, int *snr_sub) +{ + u16 reg_value = 0; + u32 reg_value_sum = 0; + int ret; + + if (!tnr_dmd || !snr || !snr_main || !snr_sub) + return -EINVAL; + + *snr = -1000 * 1000; + *snr_main = -1000 * 1000; + *snr_sub = -1000 * 1000; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = dvbt_read_snr_reg(tnr_dmd, ®_value); + if (!ret) { + ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, ®_value); + if (!ret) { + ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub); + if (ret) + reg_value = 0; + } else if (ret == -EAGAIN) { + reg_value = 0; + } else { + return ret; + } + + reg_value_sum += reg_value; + + return dvbt_calc_snr(tnr_dmd, reg_value_sum, snr); +} + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm) +{ + u8 ctl_val_reg[5]; + u8 nominal_rate_reg[5]; + u32 trl_ctl_val = 0; + u32 trcg_nominal_rate = 0; + int num; + int den; + s8 diff_upper = 0; + int ret; + + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnr_dmd); + if (ret) + return ret; + + ret = is_tps_locked(tnr_dmd); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x21, ctl_val_reg, + sizeof(ctl_val_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x04); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x60, nominal_rate_reg, + sizeof(nominal_rate_reg)); + if (ret) { + slvt_unfreeze_reg(tnr_dmd); + return ret; + } + + slvt_unfreeze_reg(tnr_dmd); + + diff_upper = + (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f); + + if (diff_upper < -1 || diff_upper > 1) + return -EAGAIN; + + trl_ctl_val = ctl_val_reg[1] << 24; + trl_ctl_val |= ctl_val_reg[2] << 16; + trl_ctl_val |= ctl_val_reg[3] << 8; + trl_ctl_val |= ctl_val_reg[4]; + + trcg_nominal_rate = nominal_rate_reg[1] << 24; + trcg_nominal_rate |= nominal_rate_reg[2] << 16; + trcg_nominal_rate |= nominal_rate_reg[3] << 8; + trcg_nominal_rate |= nominal_rate_reg[4]; + + trl_ctl_val >>= 1; + trcg_nominal_rate >>= 1; + + if (diff_upper == 1) + num = + (int)((trl_ctl_val + 0x80000000u) - + trcg_nominal_rate); + else if (diff_upper == -1) + num = + -(int)((trcg_nominal_rate + 0x80000000u) - + trl_ctl_val); + else + num = (int)(trl_ctl_val - trcg_nominal_rate); + + den = (nominal_rate_reg[0] & 0x7f) << 24; + den |= nominal_rate_reg[1] << 16; + den |= nominal_rate_reg[2] << 8; + den |= nominal_rate_reg[3]; + den = (den + (390625 / 2)) / 390625; + + den >>= 1; + + if (num >= 0) + *ppm = (num + (den / 2)) / den; + else + *ppm = (num - (den / 2)) / den; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, int *ppm) +{ + if (!tnr_dmd || !ppm) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm); +} + +static int dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd, + int rf_lvl, u8 *ssi) +{ + struct cxd2880_dvbt_tpsinfo tps; + int prel; + int temp_ssi = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps); + if (ret) + return ret; + + if (tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3 || + tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5) + return -EINVAL; + + prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp]; + + if (prel < -15000) + temp_ssi = 0; + else if (prel < 0) + temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000; + else if (prel < 20000) + temp_ssi = (((4 * prel) + 500) / 1000) + 10; + else if (prel < 35000) + temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90; + else + temp_ssi = 100; + + *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi; + + return ret; +} + +int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl); + if (ret) + return ret; + + return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi); +} + +int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi) +{ + int rf_lvl = 0; + int ret; + + if (!tnr_dmd || !ssi) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl); + if (ret) + return ret; + + return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi); +} + +static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd) +{ + u8 sync = 0; + u8 tslock = 0; + u8 early_unlock = 0; + int ret; + + if (!tnr_dmd) + return -EINVAL; + + ret = + cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock, + &early_unlock); + if (ret) + return ret; + + if (sync != 6) + return -EAGAIN; + + return 0; +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h new file mode 100644 index 0000000000..f4c31725fa --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_dvbt_mon.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * DVB-T monitor interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_DVBT_MON_H +#define CXD2880_TNRDMD_DVBT_MON_H + +#include "cxd2880_tnrdmd.h" +#include "cxd2880_dvbt.h" + +int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *ts_lock_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd + *tnr_dmd, u8 *sync_stat, + u8 *unlock_detected); + +int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd + *tnr_dmd, + enum cxd2880_dvbt_mode + *mode, + enum cxd2880_dvbt_guard + *guard); + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *offset); + +int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *offset); + +int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd + *tnr_dmd, + struct cxd2880_dvbt_tpsinfo + *info); + +int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct + cxd2880_tnrdmd + *tnr_dmd, + u32 *pen); + +int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd + *tnr_dmd, + enum + cxd2880_tnrdmd_spectrum_sense + *sense); + +int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd, + int *snr); + +int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd + *tnr_dmd, int *snr, + int *snr_main, int *snr_sub); + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd + *tnr_dmd, int *ppm); + +int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + int *ppm); + +int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi); + +int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd, + u8 *ssi); + +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c new file mode 100644 index 0000000000..3d8012c18e --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_tnrdmd_mon.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common monitor functions + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd_mon.h" + +static const u8 rf_lvl_seq[2] = { + 0x80, 0x00, +}; + +int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db) +{ + u8 rdata[2]; + int ret; + + if (!tnr_dmd || !rf_lvl_db) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, 0x01); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnr_dmd->io->write_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x5b, rf_lvl_seq, 2); + if (ret) + return ret; + + usleep_range(2000, 3000); + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x1a); + if (ret) + return ret; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x15, rdata, 2); + if (ret) + return ret; + + if (rdata[0] || rdata[1]) + return -EINVAL; + + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x11, rdata, 2); + if (ret) + return ret; + + *rf_lvl_db = + cxd2880_convert2s_complement((rdata[0] << 3) | + ((rdata[1] & 0xe0) >> 5), 11); + + *rf_lvl_db *= 125; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x00); + if (ret) + return ret; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_DMD, + 0x10, 0x00); + if (ret) + return ret; + + if (tnr_dmd->rf_lvl_cmpstn) + ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db); + + return ret; +} + +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db) +{ + if (!tnr_dmd || !rf_lvl_db) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db); +} + +int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, u16 *status) +{ + u8 data[2] = { 0 }; + int ret; + + if (!tnr_dmd || !status) + return -EINVAL; + + ret = tnr_dmd->io->write_reg(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x00, 0x1a); + if (ret) + return ret; + ret = tnr_dmd->io->read_regs(tnr_dmd->io, + CXD2880_IO_TGT_SYS, + 0x15, data, 2); + if (ret) + return ret; + + *status = (data[0] << 8) | data[1]; + + return 0; +} + +int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + u16 *status) +{ + if (!tnr_dmd || !status) + return -EINVAL; + + if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN) + return -EINVAL; + + return cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, + status); +} diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h new file mode 100644 index 0000000000..570360925f --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cxd2880_tnrdmd_mon.h + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * common monitor interface + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#ifndef CXD2880_TNRDMD_MON_H +#define CXD2880_TNRDMD_MON_H + +#include "cxd2880_common.h" +#include "cxd2880_tnrdmd.h" + +int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db); + +int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd, + int *rf_lvl_db); + +int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd + *tnr_dmd, u16 *status); + +int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct + cxd2880_tnrdmd + *tnr_dmd, + u16 *status); +#endif diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c new file mode 100644 index 0000000000..a06d8368ca --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -0,0 +1,1957 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880_top.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include <linux/spi/spi.h> + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> + +#include "cxd2880.h" +#include "cxd2880_tnrdmd_mon.h" +#include "cxd2880_tnrdmd_dvbt2_mon.h" +#include "cxd2880_tnrdmd_dvbt_mon.h" +#include "cxd2880_integ.h" +#include "cxd2880_tnrdmd_dvbt2.h" +#include "cxd2880_tnrdmd_dvbt.h" +#include "cxd2880_devio_spi.h" +#include "cxd2880_spi_device.h" +#include "cxd2880_tnrdmd_driver_version.h" + +struct cxd2880_priv { + struct cxd2880_tnrdmd tnrdmd; + struct spi_device *spi; + struct cxd2880_io regio; + struct cxd2880_spi_device spi_device; + struct cxd2880_spi cxd2880_spi; + struct cxd2880_dvbt_tune_param dvbt_tune_param; + struct cxd2880_dvbt2_tune_param dvbt2_tune_param; + struct mutex *spi_mutex; /* For SPI access exclusive control */ + unsigned long pre_ber_update; + unsigned long pre_ber_interval; + unsigned long post_ber_update; + unsigned long post_ber_interval; + unsigned long ucblock_update; + unsigned long ucblock_interval; + enum fe_status s; +}; + +static int cxd2880_pre_bit_err_t(struct cxd2880_tnrdmd *tnrdmd, + u32 *pre_bit_err, u32 *pre_bit_count) +{ + u8 rdata[2]; + int ret; + + if (!tnrdmd || !pre_bit_err || !pre_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = slvt_freeze_reg(tnrdmd); + if (ret) + return ret; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x39, rdata, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if ((rdata[0] & 0x01) == 0) { + slvt_unfreeze_reg(tnrdmd); + return -EAGAIN; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x22, rdata, 2); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + *pre_bit_err = (rdata[0] << 8) | rdata[1]; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x6f, rdata, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + slvt_unfreeze_reg(tnrdmd); + + *pre_bit_count = ((rdata[0] & 0x07) == 0) ? + 256 : (0x1000 << (rdata[0] & 0x07)); + + return 0; +} + +static int cxd2880_pre_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd, + u32 *pre_bit_err, + u32 *pre_bit_count) +{ + u32 period_exp = 0; + u32 n_ldpc = 0; + u8 data[5]; + int ret; + + if (!tnrdmd || !pre_bit_err || !pre_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnrdmd); + if (ret) + return ret; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x3c, data, sizeof(data)); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if (!(data[0] & 0x01)) { + slvt_unfreeze_reg(tnrdmd); + return -EAGAIN; + } + *pre_bit_err = + ((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8) | data[4]; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0xa0, data, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) == + CXD2880_DVBT2_FEC_LDPC_16K) + n_ldpc = 16200; + else + n_ldpc = 64800; + slvt_unfreeze_reg(tnrdmd); + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x20); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x6f, data, 1); + if (ret) + return ret; + + period_exp = data[0] & 0x0f; + + *pre_bit_count = (1U << period_exp) * n_ldpc; + + return 0; +} + +static int cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd, + u32 *post_bit_err, + u32 *post_bit_count) +{ + u8 rdata[3]; + u32 bit_error = 0; + u32 period_exp = 0; + int ret; + + if (!tnrdmd || !post_bit_err || !post_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x15, rdata, 3); + if (ret) + return ret; + + if ((rdata[0] & 0x40) == 0) + return -EAGAIN; + + *post_bit_err = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2]; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x60, rdata, 1); + if (ret) + return ret; + + period_exp = (rdata[0] & 0x1f); + + if (period_exp <= 11 && (bit_error > (1U << period_exp) * 204 * 8)) + return -EAGAIN; + + *post_bit_count = (1U << period_exp) * 204 * 8; + + return 0; +} + +static int cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd, + u32 *post_bit_err, + u32 *post_bit_count) +{ + u32 period_exp = 0; + u32 n_bch = 0; + u8 data[3]; + enum cxd2880_dvbt2_plp_fec plp_fec_type = + CXD2880_DVBT2_FEC_LDPC_16K; + enum cxd2880_dvbt2_plp_code_rate plp_code_rate = + CXD2880_DVBT2_R1_2; + int ret; + static const u16 n_bch_bits_lookup[2][8] = { + {7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480}, + {32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920} + }; + + if (!tnrdmd || !post_bit_err || !post_bit_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = slvt_freeze_reg(tnrdmd); + if (ret) + return ret; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x15, data, 3); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + if (!(data[0] & 0x40)) { + slvt_unfreeze_reg(tnrdmd); + return -EAGAIN; + } + + *post_bit_err = + ((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2]; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x9d, data, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + plp_code_rate = + (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07); + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0xa0, data, 1); + if (ret) { + slvt_unfreeze_reg(tnrdmd); + return ret; + } + + plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03); + + slvt_unfreeze_reg(tnrdmd); + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x20); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x72, data, 1); + if (ret) + return ret; + + period_exp = data[0] & 0x0f; + + if (plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K || + plp_code_rate > CXD2880_DVBT2_R2_5) + return -EAGAIN; + + n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate]; + + if (*post_bit_err > ((1U << period_exp) * n_bch)) + return -EAGAIN; + + *post_bit_count = (1U << period_exp) * n_bch; + + return 0; +} + +static int cxd2880_read_block_err_t(struct cxd2880_tnrdmd *tnrdmd, + u32 *block_err, + u32 *block_count) +{ + u8 rdata[3]; + int ret; + + if (!tnrdmd || !block_err || !block_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT) + return -EINVAL; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0d); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x18, rdata, 3); + if (ret) + return ret; + + if ((rdata[0] & 0x01) == 0) + return -EAGAIN; + + *block_err = (rdata[1] << 8) | rdata[2]; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x5c, rdata, 1); + if (ret) + return ret; + + *block_count = 1U << (rdata[0] & 0x0f); + + if ((*block_count == 0) || (*block_err > *block_count)) + return -EAGAIN; + + return 0; +} + +static int cxd2880_read_block_err_t2(struct cxd2880_tnrdmd *tnrdmd, + u32 *block_err, + u32 *block_count) +{ + u8 rdata[3]; + int ret; + + if (!tnrdmd || !block_err || !block_count) + return -EINVAL; + + if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2) + return -EINVAL; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x0b); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x18, rdata, 3); + if (ret) + return ret; + + if ((rdata[0] & 0x01) == 0) + return -EAGAIN; + + *block_err = (rdata[1] << 8) | rdata[2]; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x24); + if (ret) + return ret; + + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0xdc, rdata, 1); + if (ret) + return ret; + + *block_count = 1U << (rdata[0] & 0x0f); + + if ((*block_count == 0) || (*block_err > *block_count)) + return -EAGAIN; + + return 0; +} + +static void cxd2880_release(struct dvb_frontend *fe) +{ + struct cxd2880_priv *priv = NULL; + + if (!fe) { + pr_err("invalid arg.\n"); + return; + } + priv = fe->demodulator_priv; + kfree(priv); +} + +static int cxd2880_init(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct cxd2880_tnrdmd_create_param create_param; + + if (!fe) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI; + create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE; + create_param.en_internal_ldo = 1; + create_param.xosc_cap = 18; + create_param.xosc_i = 8; + create_param.stationary_use = 1; + + mutex_lock(priv->spi_mutex); + if (priv->tnrdmd.io != &priv->regio) { + ret = cxd2880_tnrdmd_create(&priv->tnrdmd, + &priv->regio, &create_param); + if (ret) { + mutex_unlock(priv->spi_mutex); + pr_info("cxd2880 tnrdmd create failed %d\n", ret); + return ret; + } + } + ret = cxd2880_integ_init(&priv->tnrdmd); + if (ret) { + mutex_unlock(priv->spi_mutex); + pr_err("cxd2880 integ init failed %d\n", ret); + return ret; + } + + ret = cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_TSPIN_CURRENT, + 0x00); + if (ret) { + mutex_unlock(priv->spi_mutex); + pr_err("cxd2880 set config failed %d\n", ret); + return ret; + } + mutex_unlock(priv->spi_mutex); + + pr_debug("OK.\n"); + + return ret; +} + +static int cxd2880_sleep(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv = NULL; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd); + mutex_unlock(priv->spi_mutex); + + pr_debug("tnrdmd_sleep ret %d\n", ret); + + return ret; +} + +static int cxd2880_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + int level = 0; + + if (!fe || !strength) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT || + c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level); + } else { + pr_debug("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + level /= 125; + /* + * level should be between -105dBm and -30dBm. + * E.g. they should be between: + * -105000/125 = -840 and -30000/125 = -240 + */ + level = clamp(level, -840, -240); + /* scale value to 0x0000-0xffff */ + *strength = ((level + 840) * 0xffff) / (-240 + 840); + + if (ret) + pr_debug("ret = %d\n", ret); + + return ret; +} + +static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + int ret; + int snrvalue = 0; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + + if (!fe || !snr) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd, + &snrvalue); + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd, + &snrvalue); + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + if (snrvalue < 0) + snrvalue = 0; + *snr = snrvalue; + + if (ret) + pr_debug("ret = %d\n", ret); + + return ret; +} + +static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + + if (!fe || !ucblocks) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(&priv->tnrdmd, + ucblocks); + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(&priv->tnrdmd, + ucblocks); + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + if (ret) + pr_debug("ret = %d\n", ret); + + return ret; +} + +static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + +static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv; + struct cxd2880_dvbt_tpsinfo info; + enum cxd2880_dtv_bandwidth bw; + u32 pre_ber_rate = 0; + u32 post_ber_rate = 0; + u32 ucblock_rate = 0; + u32 mes_exp = 0; + static const int cr_table[5] = {31500, 42000, 47250, 52500, 55125}; + static const int denominator_tbl[4] = {125664, 129472, 137088, 152320}; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + bw = priv->dvbt_tune_param.bandwidth; + + ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, + &info); + if (ret) { + pr_err("tps monitor error ret = %d\n", ret); + info.hierarchy = CXD2880_DVBT_HIERARCHY_NON; + info.constellation = CXD2880_DVBT_CONSTELLATION_QPSK; + info.guard = CXD2880_DVBT_GUARD_1_4; + info.rate_hp = CXD2880_DVBT_CODERATE_1_2; + info.rate_lp = CXD2880_DVBT_CODERATE_1_2; + } + + if (info.hierarchy == CXD2880_DVBT_HIERARCHY_NON) { + pre_ber_rate = 63000000 * bw * (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * + (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + ucblock_rate = 875 * cr_table[info.rate_hp] * bw * + (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + } else { + u8 data = 0; + struct cxd2880_tnrdmd *tnrdmd = &priv->tnrdmd; + + ret = tnrdmd->io->write_reg(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x00, 0x10); + if (!ret) { + ret = tnrdmd->io->read_regs(tnrdmd->io, + CXD2880_IO_TGT_DMD, + 0x67, &data, 1); + if (ret) + data = 0x00; + } else { + data = 0x00; + } + + if (data & 0x01) { /* Low priority */ + pre_ber_rate = + 63000000 * bw * (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + post_ber_rate = 1000 * cr_table[info.rate_lp] * bw * + (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + + ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_lp] * + bw * (info.constellation * 2 + 2) / + denominator_tbl[info.guard]; + } else { /* High priority */ + pre_ber_rate = + 63000000 * bw * 2 / denominator_tbl[info.guard]; + + post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * 2 / + denominator_tbl[info.guard]; + + ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_hp] * + bw * 2 / denominator_tbl[info.guard]; + } + } + + mes_exp = pre_ber_rate < 8192 ? 8 : intlog2(pre_ber_rate) >> 24; + priv->pre_ber_interval = + ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) / + pre_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD, + mes_exp == 8 ? 0 : mes_exp - 12); + + mes_exp = intlog2(post_ber_rate) >> 24; + priv->post_ber_interval = + ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) / + post_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD, + mes_exp); + + mes_exp = intlog2(ucblock_rate) >> 24; + priv->ucblock_interval = + ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) / + ucblock_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT_PER_MES, + mes_exp); + + return 0; +} + +static int cxd2880_set_ber_per_period_t2(struct dvb_frontend *fe) +{ + int ret; + struct cxd2880_priv *priv; + struct cxd2880_dvbt2_l1pre l1pre; + struct cxd2880_dvbt2_l1post l1post; + struct cxd2880_dvbt2_plp plp; + struct cxd2880_dvbt2_bbheader bbheader; + enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ; + u32 pre_ber_rate = 0; + u32 post_ber_rate = 0; + u32 ucblock_rate = 0; + u32 mes_exp = 0; + u32 term_a = 0; + u32 term_b = 0; + u32 denominator = 0; + static const u32 gi_tbl[7] = {32, 64, 128, 256, 8, 152, 76}; + static const u8 n_tbl[6] = {8, 2, 4, 16, 1, 1}; + static const u8 mode_tbl[6] = {2, 8, 4, 1, 16, 32}; + static const u32 kbch_tbl[2][8] = { + {6952, 9472, 10552, 11632, 12352, 13072, 5152, 6232}, + {32128, 38608, 42960, 48328, 51568, 53760, 0, 0} + }; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + bw = priv->dvbt2_tune_param.bandwidth; + + ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre); + if (ret) { + pr_info("l1 pre error\n"); + goto error_ber_setting; + } + + ret = cxd2880_tnrdmd_dvbt2_mon_active_plp(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, &plp); + if (ret) { + pr_info("plp info error\n"); + goto error_ber_setting; + } + + ret = cxd2880_tnrdmd_dvbt2_mon_l1_post(&priv->tnrdmd, &l1post); + if (ret) { + pr_info("l1 post error\n"); + goto error_ber_setting; + } + + term_a = + (mode_tbl[l1pre.fft_mode] * (1024 + gi_tbl[l1pre.gi])) * + (l1pre.num_symbols + n_tbl[l1pre.fft_mode]) + 2048; + + if (l1pre.mixed && l1post.fef_intvl) { + term_b = (l1post.fef_length + (l1post.fef_intvl / 2)) / + l1post.fef_intvl; + } else { + term_b = 0; + } + + switch (bw) { + case CXD2880_DTV_BW_1_7_MHZ: + denominator = ((term_a + term_b) * 71 + (131 / 2)) / 131; + break; + case CXD2880_DTV_BW_5_MHZ: + denominator = ((term_a + term_b) * 7 + 20) / 40; + break; + case CXD2880_DTV_BW_6_MHZ: + denominator = ((term_a + term_b) * 7 + 24) / 48; + break; + case CXD2880_DTV_BW_7_MHZ: + denominator = ((term_a + term_b) + 4) / 8; + break; + case CXD2880_DTV_BW_8_MHZ: + default: + denominator = ((term_a + term_b) * 7 + 32) / 64; + break; + } + + if (plp.til_type && plp.til_len) { + pre_ber_rate = + (plp.num_blocks_max * 1000000 + (denominator / 2)) / + denominator; + pre_ber_rate = (pre_ber_rate + (plp.til_len / 2)) / + plp.til_len; + } else { + pre_ber_rate = + (plp.num_blocks_max * 1000000 + (denominator / 2)) / + denominator; + } + + post_ber_rate = pre_ber_rate; + + mes_exp = intlog2(pre_ber_rate) >> 24; + priv->pre_ber_interval = + ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) / + pre_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, + mes_exp); + + mes_exp = intlog2(post_ber_rate) >> 24; + priv->post_ber_interval = + ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) / + post_ber_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, + mes_exp); + + ret = cxd2880_tnrdmd_dvbt2_mon_bbheader(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, + &bbheader); + if (ret) { + pr_info("bb header error\n"); + goto error_ucblock_setting; + } + + if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_NM) { + if (!bbheader.issy_indicator) { + ucblock_rate = + (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + + 752) / 1504; + } else { + ucblock_rate = + (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + + 764) / 1528; + } + } else if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_HEM) { + ucblock_rate = + (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + 748) / + 1496; + } else { + pr_info("plp mode is not Normal or HEM\n"); + goto error_ucblock_setting; + } + + mes_exp = intlog2(ucblock_rate) >> 24; + priv->ucblock_interval = + ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) / + ucblock_rate; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_PER_MES, + mes_exp); + + return 0; + +error_ber_setting: + priv->pre_ber_interval = 1000; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, 0); + + priv->post_ber_interval = 1000; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, 0); + +error_ucblock_setting: + priv->ucblock_interval = 1000; + cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_DVBT2_PER_MES, 8); + + return 0; +} + +static int cxd2880_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + atomic_set(&tnr_dmd->cancel, 0); + + if (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) { + return -ENOTTY; + } + + ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param); + if (ret) + return ret; + + usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000, + CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000); + + return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param); +} + +static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd, + struct cxd2880_dvbt2_tune_param + *tune_param) +{ + int ret; + + if (!tnr_dmd || !tune_param) + return -EINVAL; + + if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) + return -EINVAL; + + if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && + tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) + return -EINVAL; + + atomic_set(&tnr_dmd->cancel, 0); + + if (tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ && + tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) { + return -ENOTTY; + } + + if (tune_param->profile != CXD2880_DVBT2_PROFILE_BASE && + tune_param->profile != CXD2880_DVBT2_PROFILE_LITE) + return -EINVAL; + + ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param); + if (ret) + return ret; + + usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000, + CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000); + + return cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param); +} + +static int cxd2880_set_frontend(struct dvb_frontend *fe) +{ + int ret; + struct dtv_frontend_properties *c; + struct cxd2880_priv *priv; + enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[0].uvalue = 0; + c->pre_bit_error.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].uvalue = 0; + c->pre_bit_count.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_error.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].uvalue = 0; + c->post_bit_count.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].uvalue = 0; + c->block_error.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].uvalue = 0; + c->block_count.len = 1; + + switch (c->bandwidth_hz) { + case 1712000: + bw = CXD2880_DTV_BW_1_7_MHZ; + break; + case 5000000: + bw = CXD2880_DTV_BW_5_MHZ; + break; + case 6000000: + bw = CXD2880_DTV_BW_6_MHZ; + break; + case 7000000: + bw = CXD2880_DTV_BW_7_MHZ; + break; + case 8000000: + bw = CXD2880_DTV_BW_8_MHZ; + break; + default: + return -EINVAL; + } + + priv->s = 0; + + pr_info("sys:%d freq:%d bw:%d\n", + c->delivery_system, c->frequency, bw); + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT; + priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000; + priv->dvbt_tune_param.bandwidth = bw; + priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP; + ret = cxd2880_dvbt_tune(&priv->tnrdmd, + &priv->dvbt_tune_param); + } else if (c->delivery_system == SYS_DVBT2) { + priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2; + priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000; + priv->dvbt2_tune_param.bandwidth = bw; + priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id; + priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE; + ret = cxd2880_dvbt2_tune(&priv->tnrdmd, + &priv->dvbt2_tune_param); + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + + pr_info("tune result %d\n", ret); + + return ret; +} + +static int cxd2880_get_stats(struct dvb_frontend *fe, + enum fe_status status) +{ + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + u32 pre_bit_err = 0, pre_bit_count = 0; + u32 post_bit_err = 0, post_bit_count = 0; + u32 block_err = 0, block_count = 0; + int ret; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + + if (!(status & FE_HAS_LOCK) || !(status & FE_HAS_CARRIER)) { + c->pre_bit_error.len = 1; + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; + } + + if (time_after(jiffies, priv->pre_ber_update)) { + priv->pre_ber_update = + jiffies + msecs_to_jiffies(priv->pre_ber_interval); + if (c->delivery_system == SYS_DVBT) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, + &pre_bit_err, + &pre_bit_count); + mutex_unlock(priv->spi_mutex); + } else if (c->delivery_system == SYS_DVBT2) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd, + &pre_bit_err, + &pre_bit_count); + mutex_unlock(priv->spi_mutex); + } else { + return -EINVAL; + } + + if (!ret) { + c->pre_bit_error.len = 1; + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue += pre_bit_err; + c->pre_bit_count.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue += pre_bit_count; + } else { + c->pre_bit_error.len = 1; + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("pre_bit_error_t failed %d\n", ret); + } + } + + if (time_after(jiffies, priv->post_ber_update)) { + priv->post_ber_update = + jiffies + msecs_to_jiffies(priv->post_ber_interval); + if (c->delivery_system == SYS_DVBT) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_post_bit_err_t(&priv->tnrdmd, + &post_bit_err, + &post_bit_count); + mutex_unlock(priv->spi_mutex); + } else if (c->delivery_system == SYS_DVBT2) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_post_bit_err_t2(&priv->tnrdmd, + &post_bit_err, + &post_bit_count); + mutex_unlock(priv->spi_mutex); + } else { + return -EINVAL; + } + + if (!ret) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += post_bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += post_bit_count; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = + FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = + FE_SCALE_NOT_AVAILABLE; + pr_debug("post_bit_err_t %d\n", ret); + } + } + + if (time_after(jiffies, priv->ucblock_update)) { + priv->ucblock_update = + jiffies + msecs_to_jiffies(priv->ucblock_interval); + if (c->delivery_system == SYS_DVBT) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_read_block_err_t(&priv->tnrdmd, + &block_err, + &block_count); + mutex_unlock(priv->spi_mutex); + } else if (c->delivery_system == SYS_DVBT2) { + mutex_lock(priv->spi_mutex); + ret = cxd2880_read_block_err_t2(&priv->tnrdmd, + &block_err, + &block_count); + mutex_unlock(priv->spi_mutex); + } else { + return -EINVAL; + } + if (!ret) { + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += block_err; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += block_count; + } else { + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("read_block_err_t %d\n", ret); + } + } + + return 0; +} + +static int cxd2880_check_l1post_plp(struct dvb_frontend *fe) +{ + u8 valid = 0; + u8 plp_not_found; + int ret; + struct cxd2880_priv *priv = NULL; + + if (!fe) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + ret = cxd2880_tnrdmd_dvbt2_check_l1post_valid(&priv->tnrdmd, + &valid); + if (ret) + return ret; + + if (!valid) + return -EAGAIN; + + ret = cxd2880_tnrdmd_dvbt2_mon_data_plp_error(&priv->tnrdmd, + &plp_not_found); + if (ret) + return ret; + + if (plp_not_found) { + priv->dvbt2_tune_param.tune_info = + CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID; + } else { + priv->dvbt2_tune_param.tune_info = + CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK; + } + + return 0; +} + +static int cxd2880_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + int ret; + u8 sync = 0; + u8 lock = 0; + u8 unlock = 0; + struct cxd2880_priv *priv = NULL; + struct dtv_frontend_properties *c = NULL; + + if (!fe || !status) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + c = &fe->dtv_property_cache; + *status = 0; + + if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) { + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(&priv->tnrdmd, + &sync, + &lock, + &unlock); + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(&priv->tnrdmd, + &sync, + &lock, + &unlock); + } else { + pr_err("invalid system"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + + mutex_unlock(priv->spi_mutex); + if (ret) { + pr_err("failed. sys = %d\n", priv->tnrdmd.sys); + return ret; + } + + if (sync == 6) { + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER; + } + if (lock) + *status |= FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + + pr_debug("status %d\n", *status); + + if (priv->s == 0 && (*status & FE_HAS_LOCK) && + (*status & FE_HAS_CARRIER)) { + mutex_lock(priv->spi_mutex); + if (c->delivery_system == SYS_DVBT) { + ret = cxd2880_set_ber_per_period_t(fe); + priv->s = *status; + } else if (c->delivery_system == SYS_DVBT2) { + ret = cxd2880_check_l1post_plp(fe); + if (!ret) { + ret = cxd2880_set_ber_per_period_t2(fe); + priv->s = *status; + } + } else { + pr_err("invalid system\n"); + mutex_unlock(priv->spi_mutex); + return -EINVAL; + } + mutex_unlock(priv->spi_mutex); + } + + cxd2880_get_stats(fe, *status); + return 0; +} + +static int cxd2880_tune(struct dvb_frontend *fe, + bool retune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret; + + if (!fe || !delay || !status) { + pr_err("invalid arg."); + return -EINVAL; + } + + if (retune) { + ret = cxd2880_set_frontend(fe); + if (ret) { + pr_err("cxd2880_set_frontend failed %d\n", ret); + return ret; + } + } + + *delay = HZ / 5; + + return cxd2880_read_status(fe, status); +} + +static int cxd2880_get_frontend_t(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + int ret; + struct cxd2880_priv *priv = NULL; + enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K; + enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32; + struct cxd2880_dvbt_tpsinfo tps; + enum cxd2880_tnrdmd_spectrum_sense sense; + u16 snr = 0; + int strength = 0; + + if (!fe || !c) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd, + &mode, &guard); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (mode) { + case CXD2880_DVBT_MODE_2K: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case CXD2880_DVBT_MODE_8K: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + c->transmission_mode = TRANSMISSION_MODE_2K; + pr_debug("transmission mode is invalid %d\n", mode); + break; + } + switch (guard) { + case CXD2880_DVBT_GUARD_1_32: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case CXD2880_DVBT_GUARD_1_16: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case CXD2880_DVBT_GUARD_1_8: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case CXD2880_DVBT_GUARD_1_4: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("guard interval is invalid %d\n", + guard); + break; + } + } else { + c->transmission_mode = TRANSMISSION_MODE_2K; + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("ModeGuard err %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (tps.hierarchy) { + case CXD2880_DVBT_HIERARCHY_NON: + c->hierarchy = HIERARCHY_NONE; + break; + case CXD2880_DVBT_HIERARCHY_1: + c->hierarchy = HIERARCHY_1; + break; + case CXD2880_DVBT_HIERARCHY_2: + c->hierarchy = HIERARCHY_2; + break; + case CXD2880_DVBT_HIERARCHY_4: + c->hierarchy = HIERARCHY_4; + break; + default: + c->hierarchy = HIERARCHY_NONE; + pr_debug("TPSInfo hierarchy is invalid %d\n", + tps.hierarchy); + break; + } + + switch (tps.rate_hp) { + case CXD2880_DVBT_CODERATE_1_2: + c->code_rate_HP = FEC_1_2; + break; + case CXD2880_DVBT_CODERATE_2_3: + c->code_rate_HP = FEC_2_3; + break; + case CXD2880_DVBT_CODERATE_3_4: + c->code_rate_HP = FEC_3_4; + break; + case CXD2880_DVBT_CODERATE_5_6: + c->code_rate_HP = FEC_5_6; + break; + case CXD2880_DVBT_CODERATE_7_8: + c->code_rate_HP = FEC_7_8; + break; + default: + c->code_rate_HP = FEC_NONE; + pr_debug("TPSInfo rateHP is invalid %d\n", + tps.rate_hp); + break; + } + switch (tps.rate_lp) { + case CXD2880_DVBT_CODERATE_1_2: + c->code_rate_LP = FEC_1_2; + break; + case CXD2880_DVBT_CODERATE_2_3: + c->code_rate_LP = FEC_2_3; + break; + case CXD2880_DVBT_CODERATE_3_4: + c->code_rate_LP = FEC_3_4; + break; + case CXD2880_DVBT_CODERATE_5_6: + c->code_rate_LP = FEC_5_6; + break; + case CXD2880_DVBT_CODERATE_7_8: + c->code_rate_LP = FEC_7_8; + break; + default: + c->code_rate_LP = FEC_NONE; + pr_debug("TPSInfo rateLP is invalid %d\n", + tps.rate_lp); + break; + } + switch (tps.constellation) { + case CXD2880_DVBT_CONSTELLATION_QPSK: + c->modulation = QPSK; + break; + case CXD2880_DVBT_CONSTELLATION_16QAM: + c->modulation = QAM_16; + break; + case CXD2880_DVBT_CONSTELLATION_64QAM: + c->modulation = QAM_64; + break; + default: + c->modulation = QPSK; + pr_debug("TPSInfo constellation is invalid %d\n", + tps.constellation); + break; + } + } else { + c->hierarchy = HIERARCHY_NONE; + c->code_rate_HP = FEC_NONE; + c->code_rate_LP = FEC_NONE; + c->modulation = QPSK; + pr_debug("TPS info err %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (sense) { + case CXD2880_TNRDMD_SPECTRUM_NORMAL: + c->inversion = INVERSION_OFF; + break; + case CXD2880_TNRDMD_SPECTRUM_INV: + c->inversion = INVERSION_ON; + break; + default: + c->inversion = INVERSION_OFF; + pr_debug("spectrum sense is invalid %d\n", sense); + break; + } + } else { + c->inversion = INVERSION_OFF; + pr_debug("spectrum_sense %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength); + mutex_unlock(priv->spi_mutex); + if (!ret) { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("mon_rf_lvl %d\n", ret); + } + + ret = cxd2880_read_snr(fe, &snr); + if (!ret) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = snr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("read_snr %d\n", ret); + } + + return 0; +} + +static int cxd2880_get_frontend_t2(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + int ret; + struct cxd2880_priv *priv = NULL; + struct cxd2880_dvbt2_l1pre l1pre; + enum cxd2880_dvbt2_plp_code_rate coderate; + enum cxd2880_dvbt2_plp_constell qam; + enum cxd2880_tnrdmd_spectrum_sense sense; + u16 snr = 0; + int strength = 0; + + if (!fe || !c) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + priv = fe->demodulator_priv; + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (l1pre.fft_mode) { + case CXD2880_DVBT2_M2K: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case CXD2880_DVBT2_M8K: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + case CXD2880_DVBT2_M4K: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case CXD2880_DVBT2_M1K: + c->transmission_mode = TRANSMISSION_MODE_1K; + break; + case CXD2880_DVBT2_M16K: + c->transmission_mode = TRANSMISSION_MODE_16K; + break; + case CXD2880_DVBT2_M32K: + c->transmission_mode = TRANSMISSION_MODE_32K; + break; + default: + c->transmission_mode = TRANSMISSION_MODE_2K; + pr_debug("L1Pre fft_mode is invalid %d\n", + l1pre.fft_mode); + break; + } + switch (l1pre.gi) { + case CXD2880_DVBT2_G1_32: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case CXD2880_DVBT2_G1_16: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case CXD2880_DVBT2_G1_8: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case CXD2880_DVBT2_G1_4: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case CXD2880_DVBT2_G1_128: + c->guard_interval = GUARD_INTERVAL_1_128; + break; + case CXD2880_DVBT2_G19_128: + c->guard_interval = GUARD_INTERVAL_19_128; + break; + case CXD2880_DVBT2_G19_256: + c->guard_interval = GUARD_INTERVAL_19_256; + break; + default: + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("L1Pre guard interval is invalid %d\n", + l1pre.gi); + break; + } + } else { + c->transmission_mode = TRANSMISSION_MODE_2K; + c->guard_interval = GUARD_INTERVAL_1_32; + pr_debug("L1Pre err %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, + &coderate); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (coderate) { + case CXD2880_DVBT2_R1_2: + c->fec_inner = FEC_1_2; + break; + case CXD2880_DVBT2_R3_5: + c->fec_inner = FEC_3_5; + break; + case CXD2880_DVBT2_R2_3: + c->fec_inner = FEC_2_3; + break; + case CXD2880_DVBT2_R3_4: + c->fec_inner = FEC_3_4; + break; + case CXD2880_DVBT2_R4_5: + c->fec_inner = FEC_4_5; + break; + case CXD2880_DVBT2_R5_6: + c->fec_inner = FEC_5_6; + break; + default: + c->fec_inner = FEC_NONE; + pr_debug("CodeRate is invalid %d\n", coderate); + break; + } + } else { + c->fec_inner = FEC_NONE; + pr_debug("CodeRate %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd, + CXD2880_DVBT2_PLP_DATA, + &qam); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (qam) { + case CXD2880_DVBT2_QPSK: + c->modulation = QPSK; + break; + case CXD2880_DVBT2_QAM16: + c->modulation = QAM_16; + break; + case CXD2880_DVBT2_QAM64: + c->modulation = QAM_64; + break; + case CXD2880_DVBT2_QAM256: + c->modulation = QAM_256; + break; + default: + c->modulation = QPSK; + pr_debug("QAM is invalid %d\n", qam); + break; + } + } else { + c->modulation = QPSK; + pr_debug("QAM %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense); + mutex_unlock(priv->spi_mutex); + if (!ret) { + switch (sense) { + case CXD2880_TNRDMD_SPECTRUM_NORMAL: + c->inversion = INVERSION_OFF; + break; + case CXD2880_TNRDMD_SPECTRUM_INV: + c->inversion = INVERSION_ON; + break; + default: + c->inversion = INVERSION_OFF; + pr_debug("spectrum sense is invalid %d\n", sense); + break; + } + } else { + c->inversion = INVERSION_OFF; + pr_debug("SpectrumSense %d\n", ret); + } + + mutex_lock(priv->spi_mutex); + ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength); + mutex_unlock(priv->spi_mutex); + if (!ret) { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("mon_rf_lvl %d\n", ret); + } + + ret = cxd2880_read_snr(fe, &snr); + if (!ret) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = snr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + pr_debug("read_snr %d\n", ret); + } + + return 0; +} + +static int cxd2880_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *props) +{ + int ret; + + if (!fe || !props) { + pr_err("invalid arg."); + return -EINVAL; + } + + pr_debug("system=%d\n", fe->dtv_property_cache.delivery_system); + switch (fe->dtv_property_cache.delivery_system) { + case SYS_DVBT: + ret = cxd2880_get_frontend_t(fe, props); + break; + case SYS_DVBT2: + ret = cxd2880_get_frontend_t2(fe, props); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = { + .info = { + .name = "Sony CXD2880", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 1 * kHz, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS, + }, + .delsys = { SYS_DVBT, SYS_DVBT2 }, + + .release = cxd2880_release, + .init = cxd2880_init, + .sleep = cxd2880_sleep, + .tune = cxd2880_tune, + .set_frontend = cxd2880_set_frontend, + .get_frontend = cxd2880_get_frontend, + .read_status = cxd2880_read_status, + .read_ber = cxd2880_read_ber, + .read_signal_strength = cxd2880_read_signal_strength, + .read_snr = cxd2880_read_snr, + .read_ucblocks = cxd2880_read_ucblocks, + .get_frontend_algo = cxd2880_get_frontend_algo, +}; + +struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, + struct cxd2880_config *cfg) +{ + int ret; + enum cxd2880_tnrdmd_chip_id chipid = + CXD2880_TNRDMD_CHIP_ID_UNKNOWN; + static struct cxd2880_priv *priv; + u8 data = 0; + + if (!fe) { + pr_err("invalid arg.\n"); + return NULL; + } + + priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->spi = cfg->spi; + priv->spi_mutex = cfg->spi_mutex; + priv->spi_device.spi = cfg->spi; + + memcpy(&fe->ops, &cxd2880_dvbt_t2_ops, + sizeof(struct dvb_frontend_ops)); + + ret = cxd2880_spi_device_initialize(&priv->spi_device, + CXD2880_SPI_MODE_0, + 55000000); + if (ret) { + pr_err("spi_device_initialize failed. %d\n", ret); + kfree(priv); + return NULL; + } + + ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi, + &priv->spi_device); + if (ret) { + pr_err("spi_device_create_spi failed. %d\n", ret); + kfree(priv); + return NULL; + } + + ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0); + if (ret) { + pr_err("io_spi_create failed. %d\n", ret); + kfree(priv); + return NULL; + } + ret = priv->regio.write_reg(&priv->regio, + CXD2880_IO_TGT_SYS, 0x00, 0x00); + if (ret) { + pr_err("set bank to 0x00 failed.\n"); + kfree(priv); + return NULL; + } + ret = priv->regio.read_regs(&priv->regio, + CXD2880_IO_TGT_SYS, 0xfd, &data, 1); + if (ret) { + pr_err("read chip id failed.\n"); + kfree(priv); + return NULL; + } + + chipid = (enum cxd2880_tnrdmd_chip_id)data; + if (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X && + chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11) { + pr_err("chip id invalid.\n"); + kfree(priv); + return NULL; + } + + fe->demodulator_priv = priv; + pr_info("CXD2880 driver version: Ver %s\n", + CXD2880_TNRDMD_DRIVER_VERSION); + + return fe; +} +EXPORT_SYMBOL_GPL(cxd2880_attach); + +MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver"); +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c new file mode 100644 index 0000000000..9a8e7cdd2a --- /dev/null +++ b/drivers/media/dvb-frontends/dib0070.c @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. + * + * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) + * + * This code is more or less generated from another driver, please + * excuse some codingstyle oddities. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +#include <media/dvb_frontend.h> + +#include "dib0070.h" +#include "dibx000_common.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +#define DIB0070_P1D 0x00 +#define DIB0070_P1F 0x01 +#define DIB0070_P1G 0x03 +#define DIB0070S_P1A 0x02 + +struct dib0070_state { + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + const struct dib0070_config *cfg; + u16 wbd_ff_offset; + u8 revision; + + enum frontend_tune_state tune_state; + u32 current_rf; + + /* for the captrim binary search */ + s8 step; + u16 adc_diff; + + s8 captrim; + s8 fcaptrim; + u16 lo4; + + const struct dib0070_tuning *current_tune_table_index; + const struct dib0070_lna_match *lna_match; + + u8 wbd_gain_current; + u16 wbd_offset_3_3[2]; + + /* for the I2C transfer */ + struct i2c_msg msg[2]; + u8 i2c_write_buffer[3]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; +}; + +static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + state->i2c_write_buffer[0] = reg; + + memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); + state->msg[0].addr = state->cfg->i2c_address; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 1; + state->msg[1].addr = state->cfg->i2c_address; + state->msg[1].flags = I2C_M_RD; + state->msg[1].buf = state->i2c_read_buffer; + state->msg[1].len = 2; + + if (i2c_transfer(state->i2c, state->msg, 2) != 2) { + pr_warn("DiB0070 I2C read failed\n"); + ret = 0; + } else + ret = (state->i2c_read_buffer[0] << 8) + | state->i2c_read_buffer[1]; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + state->i2c_write_buffer[0] = reg; + state->i2c_write_buffer[1] = val >> 8; + state->i2c_write_buffer[2] = val & 0xff; + + memset(state->msg, 0, sizeof(struct i2c_msg)); + state->msg[0].addr = state->cfg->i2c_address; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 3; + + if (i2c_transfer(state->i2c, state->msg, 1) != 1) { + pr_warn("DiB0070 I2C write failed\n"); + ret = -EREMOTEIO; + } else + ret = 0; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +#define HARD_RESET(state) do { \ + state->cfg->sleep(state->fe, 0); \ + if (state->cfg->reset) { \ + state->cfg->reset(state->fe,1); msleep(10); \ + state->cfg->reset(state->fe,0); msleep(10); \ + } \ +} while (0) + +static int dib0070_set_bandwidth(struct dvb_frontend *fe) + { + struct dib0070_state *state = fe->tuner_priv; + u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; + + if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000) + tmp |= (0 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000) + tmp |= (1 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000) + tmp |= (2 << 14); + else + tmp |= (3 << 14); + + dib0070_write_reg(state, 0x02, tmp); + + /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { + u16 value = dib0070_read_reg(state, 0x17); + + dib0070_write_reg(state, 0x17, value & 0xfffc); + tmp = dib0070_read_reg(state, 0x01) & 0x01ff; + dib0070_write_reg(state, 0x01, tmp | (60 << 9)); + + dib0070_write_reg(state, 0x17, value); + } + return 0; +} + +static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state) +{ + int8_t step_sign; + u16 adc; + int ret = 0; + + if (*tune_state == CT_TUNER_STEP_0) { + dib0070_write_reg(state, 0x0f, 0xed10); + dib0070_write_reg(state, 0x17, 0x0034); + + dib0070_write_reg(state, 0x18, 0x0032); + state->step = state->captrim = state->fcaptrim = 64; + state->adc_diff = 3000; + ret = 20; + + *tune_state = CT_TUNER_STEP_1; + } else if (*tune_state == CT_TUNER_STEP_1) { + state->step /= 2; + dib0070_write_reg(state, 0x14, state->lo4 | state->captrim); + ret = 15; + + *tune_state = CT_TUNER_STEP_2; + } else if (*tune_state == CT_TUNER_STEP_2) { + + adc = dib0070_read_reg(state, 0x19); + + dprintk("CAPTRIM=%d; ADC = %hd (ADC) & %dmV\n", state->captrim, + adc, (u32)adc * (u32)1800 / (u32)1024); + + if (adc >= 400) { + adc -= 400; + step_sign = -1; + } else { + adc = 400 - adc; + step_sign = 1; + } + + if (adc < state->adc_diff) { + dprintk("CAPTRIM=%d is closer to target (%hd/%hd)\n", + state->captrim, adc, state->adc_diff); + state->adc_diff = adc; + state->fcaptrim = state->captrim; + } + state->captrim += (step_sign * state->step); + + if (state->step >= 1) + *tune_state = CT_TUNER_STEP_1; + else + *tune_state = CT_TUNER_STEP_3; + + } else if (*tune_state == CT_TUNER_STEP_3) { + dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim); + dib0070_write_reg(state, 0x18, 0x07ff); + *tune_state = CT_TUNER_STEP_4; + } + + return ret; +} + +static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) +{ + struct dib0070_state *state = fe->tuner_priv; + u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + + dprintk("CTRL_LO5: 0x%x\n", lo5); + return dib0070_write_reg(state, 0x15, lo5); +} + +void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) +{ + struct dib0070_state *state = fe->tuner_priv; + + if (open) { + dib0070_write_reg(state, 0x1b, 0xff00); + dib0070_write_reg(state, 0x1a, 0x0000); + } else { + dib0070_write_reg(state, 0x1b, 0x4112); + if (state->cfg->vga_filter != 0) { + dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); + dprintk("vga filter register is set to %x\n", state->cfg->vga_filter); + } else + dib0070_write_reg(state, 0x1a, 0x0009); + } +} + +EXPORT_SYMBOL(dib0070_ctrl_agc_filter); +struct dib0070_tuning { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 vco_band; + u8 hfdiv; + u8 vco_multi; + u8 presc; + u8 wbdmux; + u16 tuner_enable; +}; + +struct dib0070_lna_match { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 lna_band; +}; + +static const struct dib0070_tuning dib0070s_tuning_table[] = { + { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ + { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ + { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ +}; + +static const struct dib0070_tuning dib0070_tuning_table[] = { + { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ + { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ + { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, + { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, + { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ + { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, + { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ +}; + +static const struct dib0070_lna_match dib0070_lna_flip_chip[] = { + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 0 }, /* UHF */ + { 590000, 1 }, + { 666000, 3 }, + { 864000, 5 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, +}; + +static const struct dib0070_lna_match dib0070_lna[] = { + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 2 }, /* UHF */ + { 650000, 3 }, + { 750000, 5 }, + { 850000, 6 }, + { 864000, 7 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, +}; + +#define LPF 100 +static int dib0070_tune_digital(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + + const struct dib0070_tuning *tune; + const struct dib0070_lna_match *lna_match; + + enum frontend_tune_state *tune_state = &state->tune_state; + int ret = 10; /* 1ms is the default delay most of the time */ + + u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000); + u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); + +#ifdef CONFIG_SYS_ISDBT + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) + if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) + freq += 850; +#endif + if (state->current_rf != freq) { + + switch (state->revision) { + case DIB0070S_P1A: + tune = dib0070s_tuning_table; + lna_match = dib0070_lna; + break; + default: + tune = dib0070_tuning_table; + if (state->cfg->flip_chip) + lna_match = dib0070_lna_flip_chip; + else + lna_match = dib0070_lna; + break; + } + while (freq > tune->max_freq) /* find the right one */ + tune++; + while (freq > lna_match->max_freq) /* find the right one */ + lna_match++; + + state->current_tune_table_index = tune; + state->lna_match = lna_match; + } + + if (*tune_state == CT_TUNER_START) { + dprintk("Tuning for Band: %d (%d kHz)\n", band, freq); + if (state->current_rf != freq) { + u8 REFDIV; + u32 FBDiv, Rest, FREF, VCOF_kHz; + u8 Den; + + state->current_rf = freq; + state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); + + + dib0070_write_reg(state, 0x17, 0x30); + + + VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; + + switch (band) { + case BAND_VHF: + REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); + break; + case BAND_FM: + REFDIV = (u8) ((state->cfg->clock_khz) / 1000); + break; + default: + REFDIV = (u8) (state->cfg->clock_khz / 10000); + break; + } + FREF = state->cfg->clock_khz / REFDIV; + + + + switch (state->revision) { + case DIB0070S_P1A: + FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); + Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; + break; + + case DIB0070_P1G: + case DIB0070_P1F: + default: + FBDiv = (freq / (FREF / 2)); + Rest = 2 * freq - FBDiv * FREF; + break; + } + + if (Rest < LPF) + Rest = 0; + else if (Rest < 2 * LPF) + Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { + Rest = 0; + FBDiv += 1; + } else if (Rest > (FREF - 2 * LPF)) + Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); + + Den = 1; + if (Rest > 0) { + state->lo4 |= (1 << 14) | (1 << 12); + Den = 255; + } + + + dib0070_write_reg(state, 0x11, (u16)FBDiv); + dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); + dib0070_write_reg(state, 0x13, (u16) Rest); + + if (state->revision == DIB0070S_P1A) { + + if (band == BAND_SBAND) { + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + dib0070_write_reg(state, 0x1d, 0xFFFF); + } else + dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + } + + dib0070_write_reg(state, 0x20, + 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); + + dprintk("REFDIV: %u, FREF: %d\n", REFDIV, FREF); + dprintk("FBDIV: %d, Rest: %d\n", FBDiv, Rest); + dprintk("Num: %u, Den: %u, SD: %d\n", (u16)Rest, Den, + (state->lo4 >> 12) & 0x1); + dprintk("HFDIV code: %u\n", + state->current_tune_table_index->hfdiv); + dprintk("VCO = %u\n", + state->current_tune_table_index->vco_band); + dprintk("VCOF: ((%u*%d) << 1))\n", + state->current_tune_table_index->vco_multi, + freq); + + *tune_state = CT_TUNER_STEP_0; + } else { /* we are already tuned to this frequency - the configuration is correct */ + ret = 50; /* wakeup time */ + *tune_state = CT_TUNER_STEP_5; + } + } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { + + ret = dib0070_captrim(state, tune_state); + + } else if (*tune_state == CT_TUNER_STEP_4) { + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + if (tmp != NULL) { + while (freq/1000 > tmp->freq) /* find the right one */ + tmp++; + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) + | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) + | (state->current_tune_table_index->wbdmux << 0)); + state->wbd_gain_current = tmp->wbd_gain_val; + } else { + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) + | (6 << 9) | (0 << 8) | (1 << 7) + | (state->current_tune_table_index->wbdmux << 0)); + state->wbd_gain_current = 6; + } + + dib0070_write_reg(state, 0x06, 0x3fff); + dib0070_write_reg(state, 0x07, + (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0)); + dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); + dib0070_write_reg(state, 0x0d, 0x0d80); + + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x17, 0x0033); + + + *tune_state = CT_TUNER_STEP_5; + } else if (*tune_state == CT_TUNER_STEP_5) { + dib0070_set_bandwidth(fe); + *tune_state = CT_TUNER_STOP; + } else { + ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ + } + return ret; +} + + +static int dib0070_tune(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + uint32_t ret; + + state->tune_state = CT_TUNER_START; + + do { + ret = dib0070_tune_digital(fe); + if (ret != FE_CALLBACK_TIME_NEVER) + msleep(ret/10); + else + break; + } while (state->tune_state != CT_TUNER_STOP); + + return 0; +} + +static int dib0070_wakeup(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + if (state->cfg->sleep) + state->cfg->sleep(fe, 0); + return 0; +} + +static int dib0070_sleep(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + if (state->cfg->sleep) + state->cfg->sleep(fe, 1); + return 0; +} + +u8 dib0070_get_rf_output(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + return (dib0070_read_reg(state, 0x07) >> 11) & 0x3; +} +EXPORT_SYMBOL(dib0070_get_rf_output); + +int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no) +{ + struct dib0070_state *state = fe->tuner_priv; + u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff; + if (no > 3) + no = 3; + if (no < 1) + no = 1; + return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11)); +} +EXPORT_SYMBOL(dib0070_set_rf_output); + +static const u16 dib0070_p1f_defaults[] = + +{ + 7, 0x02, + 0x0008, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0002, + 0x0100, + + 3, 0x0d, + 0x0d80, + 0x0001, + 0x0000, + + 4, 0x11, + 0x0000, + 0x0103, + 0x0000, + 0x0000, + + 3, 0x16, + 0x0004 | 0x0040, + 0x0030, + 0x07ff, + + 6, 0x1b, + 0x4112, + 0xff00, + 0xc07f, + 0x0000, + 0x0180, + 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, + + 0, +}; + +static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) +{ + u16 tuner_en = dib0070_read_reg(state, 0x20); + u16 offset; + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); + msleep(9); + offset = dib0070_read_reg(state, 0x19); + dib0070_write_reg(state, 0x20, tuner_en); + return offset; +} + +static void dib0070_wbd_offset_calibration(struct dib0070_state *state) +{ + u8 gain; + for (gain = 6; gain < 8; gain++) { + state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); + dprintk("Gain: %d, WBDOffset (3.3V) = %hd\n", gain, state->wbd_offset_3_3[gain-6]); + } +} + +u16 dib0070_wbd_offset(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + u32 freq = fe->dtv_property_cache.frequency/1000; + + if (tmp != NULL) { + while (freq/1000 > tmp->freq) /* find the right one */ + tmp++; + state->wbd_gain_current = tmp->wbd_gain_val; + } else + state->wbd_gain_current = 6; + + return state->wbd_offset_3_3[state->wbd_gain_current - 6]; +} +EXPORT_SYMBOL(dib0070_wbd_offset); + +#define pgm_read_word(w) (*w) +static int dib0070_reset(struct dvb_frontend *fe) +{ + struct dib0070_state *state = fe->tuner_priv; + u16 l, r, *n; + + HARD_RESET(state); + + +#ifndef FORCE_SBAND_TUNER + if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) + state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; + else +#else +#warning forcing SBAND +#endif + state->revision = DIB0070S_P1A; + + /* P1F or not */ + dprintk("Revision: %x\n", state->revision); + + if (state->revision == DIB0070_P1D) { + dprintk("Error: this driver is not to be used meant for P1D or earlier\n"); + return -EINVAL; + } + + n = (u16 *) dib0070_p1f_defaults; + l = pgm_read_word(n++); + while (l) { + r = pgm_read_word(n++); + do { + dib0070_write_reg(state, (u8)r, pgm_read_word(n++)); + r++; + } while (--l); + l = pgm_read_word(n++); + } + + if (state->cfg->force_crystal_mode != 0) + r = state->cfg->force_crystal_mode; + else if (state->cfg->clock_khz >= 24000) + r = 1; + else + r = 2; + + + r |= state->cfg->osc_buffer_state << 3; + + dib0070_write_reg(state, 0x10, r); + dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5)); + + if (state->cfg->invert_iq) { + r = dib0070_read_reg(state, 0x02) & 0xffdf; + dib0070_write_reg(state, 0x02, r | (1 << 5)); + } + + if (state->revision == DIB0070S_P1A) + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + else + dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, + state->cfg->enable_third_order_filter); + + dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); + + dib0070_wbd_offset_calibration(state); + + return 0; +} + +static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dib0070_state *state = fe->tuner_priv; + + *frequency = 1000 * state->current_rf; + return 0; +} + +static void dib0070_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static const struct dvb_tuner_ops dib0070_ops = { + .info = { + .name = "DiBcom DiB0070", + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, + }, + .release = dib0070_release, + + .init = dib0070_wakeup, + .sleep = dib0070_sleep, + .set_params = dib0070_tune, + + .get_frequency = dib0070_get_frequency, +// .get_bandwidth = dib0070_get_bandwidth +}; + +struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) +{ + struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->cfg = cfg; + state->i2c = i2c; + state->fe = fe; + mutex_init(&state->i2c_buffer_lock); + fe->tuner_priv = state; + + if (dib0070_reset(fe) != 0) + goto free_mem; + + pr_info("DiB0070: successfully identified\n"); + memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + return fe; + +free_mem: + kfree(state); + fe->tuner_priv = NULL; + return NULL; +} +EXPORT_SYMBOL_GPL(dib0070_attach); + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h new file mode 100644 index 0000000000..ae5c44e9b1 --- /dev/null +++ b/drivers/media/dvb-frontends/dib0070.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. + * + * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + */ +#ifndef DIB0070_H +#define DIB0070_H + +struct dvb_frontend; +struct i2c_adapter; + +#define DEFAULT_DIB0070_I2C_ADDRESS 0x60 + +struct dib0070_wbd_gain_cfg { + u16 freq; + u16 wbd_gain_val; +}; + +struct dib0070_config { + u8 i2c_address; + + /* tuner pins controlled externally */ + int (*reset) (struct dvb_frontend *, int); + int (*sleep) (struct dvb_frontend *, int); + + /* offset in kHz */ + int freq_offset_khz_uhf; + int freq_offset_khz_vhf; + + u8 osc_buffer_state; /* 0= normal, 1= tri-state */ + u32 clock_khz; + u8 clock_pad_drive; /* (Drive + 1) * 2mA */ + + u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ + + u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ + + u8 flip_chip; + u8 enable_third_order_filter; + u8 charge_pump; + + const struct dib0070_wbd_gain_cfg *wbd_gain; + + u8 vga_filter; +}; + +#if IS_REACHABLE(CONFIG_DVB_TUNER_DIB0070) +extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); +extern u16 dib0070_wbd_offset(struct dvb_frontend *); +extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); +extern u8 dib0070_get_rf_output(struct dvb_frontend *fe); +extern int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no); +#else +static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c new file mode 100644 index 0000000000..c958bcff02 --- /dev/null +++ b/drivers/media/dvb-frontends/dib0090.c @@ -0,0 +1,2668 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner. + * + * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) + * + * This code is more or less generated from another driver, please + * excuse some codingstyle oddities. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +#include <media/dvb_frontend.h> + +#include "dib0090.h" +#include "dibx000_common.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +#define CONFIG_SYS_DVBT +#define CONFIG_SYS_ISDBT +#define CONFIG_BAND_CBAND +#define CONFIG_BAND_VHF +#define CONFIG_BAND_UHF +#define CONFIG_DIB0090_USE_PWM_AGC + +#define EN_LNA0 0x8000 +#define EN_LNA1 0x4000 +#define EN_LNA2 0x2000 +#define EN_LNA3 0x1000 +#define EN_MIX0 0x0800 +#define EN_MIX1 0x0400 +#define EN_MIX2 0x0200 +#define EN_MIX3 0x0100 +#define EN_IQADC 0x0040 +#define EN_PLL 0x0020 +#define EN_TX 0x0010 +#define EN_BB 0x0008 +#define EN_LO 0x0004 +#define EN_BIAS 0x0001 + +#define EN_IQANA 0x0002 +#define EN_DIGCLK 0x0080 /* not in the 0x24 reg, only in 0x1b */ +#define EN_CRYSTAL 0x0002 + +#define EN_UHF 0x22E9 +#define EN_VHF 0x44E9 +#define EN_LBD 0x11E9 +#define EN_SBD 0x44E9 +#define EN_CAB 0x88E9 + +/* Calibration defines */ +#define DC_CAL 0x1 +#define WBD_CAL 0x2 +#define TEMP_CAL 0x4 +#define CAPTRIM_CAL 0x8 + +#define KROSUS_PLL_LOCKED 0x800 +#define KROSUS 0x2 + +/* Use those defines to identify SOC version */ +#define SOC 0x02 +#define SOC_7090_P1G_11R1 0x82 +#define SOC_7090_P1G_21R1 0x8a +#define SOC_8090_P1G_11R1 0x86 +#define SOC_8090_P1G_21R1 0x8e + +/* else use thos ones to check */ +#define P1A_B 0x0 +#define P1C 0x1 +#define P1D_E_F 0x3 +#define P1G 0x7 +#define P1G_21R2 0xf + +#define MP001 0x1 /* Single 9090/8096 */ +#define MP005 0x4 /* Single Sband */ +#define MP008 0x6 /* Dual diversity VHF-UHF-LBAND */ +#define MP009 0x7 /* Dual diversity 29098 CBAND-UHF-LBAND-SBAND */ + +#define pgm_read_word(w) (*w) + +struct dc_calibration; + +struct dib0090_tuning { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 lna_tune; + u16 lna_bias; + u16 v2i; + u16 mix; + u16 load; + u16 tuner_enable; +}; + +struct dib0090_pll { + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 vco_band; + u8 hfdiv_code; + u8 hfdiv; + u8 topresc; +}; + +struct dib0090_identity { + u8 version; + u8 product; + u8 p1g; + u8 in_soc; +}; + +struct dib0090_state { + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + const struct dib0090_config *config; + + u8 current_band; + enum frontend_tune_state tune_state; + u32 current_rf; + + u16 wbd_offset; + s16 wbd_target; /* in dB */ + + s16 rf_gain_limit; /* take-over-point: where to split between bb and rf gain */ + s16 current_gain; /* keeps the currently programmed gain */ + u8 agc_step; /* new binary search */ + + u16 gain[2]; /* for channel monitoring */ + + const u16 *rf_ramp; + const u16 *bb_ramp; + + /* for the software AGC ramps */ + u16 bb_1_def; + u16 rf_lt_def; + u16 gain_reg[4]; + + /* for the captrim/dc-offset search */ + s8 step; + s16 adc_diff; + s16 min_adc_diff; + + s8 captrim; + s8 fcaptrim; + + const struct dc_calibration *dc; + u16 bb6, bb7; + + const struct dib0090_tuning *current_tune_table_index; + const struct dib0090_pll *current_pll_table_index; + + u8 tuner_is_tuned; + u8 agc_freeze; + + struct dib0090_identity identity; + + u32 rf_request; + u8 current_standard; + + u8 calibrate; + u32 rest; + u16 bias; + s16 temperature; + + u8 wbd_calibration_gain; + const struct dib0090_wbd_slope *current_wbd_table; + u16 wbdmux; + + /* for the I2C transfer */ + struct i2c_msg msg[2]; + u8 i2c_write_buffer[3]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; +}; + +struct dib0090_fw_state { + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + struct dib0090_identity identity; + const struct dib0090_config *config; + + /* for the I2C transfer */ + struct i2c_msg msg; + u8 i2c_write_buffer[2]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; +}; + +static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + state->i2c_write_buffer[0] = reg; + + memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); + state->msg[0].addr = state->config->i2c_address; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 1; + state->msg[1].addr = state->config->i2c_address; + state->msg[1].flags = I2C_M_RD; + state->msg[1].buf = state->i2c_read_buffer; + state->msg[1].len = 2; + + if (i2c_transfer(state->i2c, state->msg, 2) != 2) { + pr_warn("DiB0090 I2C read failed\n"); + ret = 0; + } else + ret = (state->i2c_read_buffer[0] << 8) + | state->i2c_read_buffer[1]; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + state->i2c_write_buffer[0] = reg & 0xff; + state->i2c_write_buffer[1] = val >> 8; + state->i2c_write_buffer[2] = val & 0xff; + + memset(state->msg, 0, sizeof(struct i2c_msg)); + state->msg[0].addr = state->config->i2c_address; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 3; + + if (i2c_transfer(state->i2c, state->msg, 1) != 1) { + pr_warn("DiB0090 I2C write failed\n"); + ret = -EREMOTEIO; + } else + ret = 0; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + state->i2c_write_buffer[0] = reg; + + memset(&state->msg, 0, sizeof(struct i2c_msg)); + state->msg.addr = reg; + state->msg.flags = I2C_M_RD; + state->msg.buf = state->i2c_read_buffer; + state->msg.len = 2; + if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { + pr_warn("DiB0090 I2C read failed\n"); + ret = 0; + } else + ret = (state->i2c_read_buffer[0] << 8) + | state->i2c_read_buffer[1]; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + state->i2c_write_buffer[0] = val >> 8; + state->i2c_write_buffer[1] = val & 0xff; + + memset(&state->msg, 0, sizeof(struct i2c_msg)); + state->msg.addr = reg; + state->msg.flags = 0; + state->msg.buf = state->i2c_write_buffer; + state->msg.len = 2; + if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { + pr_warn("DiB0090 I2C write failed\n"); + ret = -EREMOTEIO; + } else + ret = 0; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0) +#define ADC_TARGET -220 +#define GAIN_ALPHA 5 +#define WBD_ALPHA 6 +#define LPF 100 +static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b, u8 c) +{ + do { + dib0090_write_reg(state, r++, *b++); + } while (--c); +} + +static int dib0090_identify(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + u16 v; + struct dib0090_identity *identity = &state->identity; + + v = dib0090_read_reg(state, 0x1a); + + identity->p1g = 0; + identity->in_soc = 0; + + dprintk("Tuner identification (Version = 0x%04x)\n", v); + + /* without PLL lock info */ + v &= ~KROSUS_PLL_LOCKED; + + identity->version = v & 0xff; + identity->product = (v >> 8) & 0xf; + + if (identity->product != KROSUS) + goto identification_error; + + if ((identity->version & 0x3) == SOC) { + identity->in_soc = 1; + switch (identity->version) { + case SOC_8090_P1G_11R1: + dprintk("SOC 8090 P1-G11R1 Has been detected\n"); + identity->p1g = 1; + break; + case SOC_8090_P1G_21R1: + dprintk("SOC 8090 P1-G21R1 Has been detected\n"); + identity->p1g = 1; + break; + case SOC_7090_P1G_11R1: + dprintk("SOC 7090 P1-G11R1 Has been detected\n"); + identity->p1g = 1; + break; + case SOC_7090_P1G_21R1: + dprintk("SOC 7090 P1-G21R1 Has been detected\n"); + identity->p1g = 1; + break; + default: + goto identification_error; + } + } else { + switch ((identity->version >> 5) & 0x7) { + case MP001: + dprintk("MP001 : 9090/8096\n"); + break; + case MP005: + dprintk("MP005 : Single Sband\n"); + break; + case MP008: + dprintk("MP008 : diversity VHF-UHF-LBAND\n"); + break; + case MP009: + dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND\n"); + break; + default: + goto identification_error; + } + + switch (identity->version & 0x1f) { + case P1G_21R2: + dprintk("P1G_21R2 detected\n"); + identity->p1g = 1; + break; + case P1G: + dprintk("P1G detected\n"); + identity->p1g = 1; + break; + case P1D_E_F: + dprintk("P1D/E/F detected\n"); + break; + case P1C: + dprintk("P1C detected\n"); + break; + case P1A_B: + dprintk("P1-A/B detected: driver is deactivated - not available\n"); + goto identification_error; + break; + default: + goto identification_error; + } + } + + return 0; + +identification_error: + return -EIO; +} + +static int dib0090_fw_identify(struct dvb_frontend *fe) +{ + struct dib0090_fw_state *state = fe->tuner_priv; + struct dib0090_identity *identity = &state->identity; + + u16 v = dib0090_fw_read_reg(state, 0x1a); + identity->p1g = 0; + identity->in_soc = 0; + + dprintk("FE: Tuner identification (Version = 0x%04x)\n", v); + + /* without PLL lock info */ + v &= ~KROSUS_PLL_LOCKED; + + identity->version = v & 0xff; + identity->product = (v >> 8) & 0xf; + + if (identity->product != KROSUS) + goto identification_error; + + if ((identity->version & 0x3) == SOC) { + identity->in_soc = 1; + switch (identity->version) { + case SOC_8090_P1G_11R1: + dprintk("SOC 8090 P1-G11R1 Has been detected\n"); + identity->p1g = 1; + break; + case SOC_8090_P1G_21R1: + dprintk("SOC 8090 P1-G21R1 Has been detected\n"); + identity->p1g = 1; + break; + case SOC_7090_P1G_11R1: + dprintk("SOC 7090 P1-G11R1 Has been detected\n"); + identity->p1g = 1; + break; + case SOC_7090_P1G_21R1: + dprintk("SOC 7090 P1-G21R1 Has been detected\n"); + identity->p1g = 1; + break; + default: + goto identification_error; + } + } else { + switch ((identity->version >> 5) & 0x7) { + case MP001: + dprintk("MP001 : 9090/8096\n"); + break; + case MP005: + dprintk("MP005 : Single Sband\n"); + break; + case MP008: + dprintk("MP008 : diversity VHF-UHF-LBAND\n"); + break; + case MP009: + dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND\n"); + break; + default: + goto identification_error; + } + + switch (identity->version & 0x1f) { + case P1G_21R2: + dprintk("P1G_21R2 detected\n"); + identity->p1g = 1; + break; + case P1G: + dprintk("P1G detected\n"); + identity->p1g = 1; + break; + case P1D_E_F: + dprintk("P1D/E/F detected\n"); + break; + case P1C: + dprintk("P1C detected\n"); + break; + case P1A_B: + dprintk("P1-A/B detected: driver is deactivated - not available\n"); + goto identification_error; + break; + default: + goto identification_error; + } + } + + return 0; + +identification_error: + return -EIO; +} + +static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) +{ + struct dib0090_state *state = fe->tuner_priv; + u16 PllCfg, i, v; + + HARD_RESET(state); + dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); + if (cfg->in_soc) + return; + + dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ + /* adcClkOutRatio=8->7, release reset */ + dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); + if (cfg->clkoutdrive != 0) + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + else + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + + /* Read Pll current config * */ + PllCfg = dib0090_read_reg(state, 0x21); + + /** Reconfigure PLL if current setting is different from default setting **/ + if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && (!cfg->in_soc) + && !cfg->io.pll_bypass) { + + /* Set Bypass mode */ + PllCfg |= (1 << 15); + dib0090_write_reg(state, 0x21, PllCfg); + + /* Set Reset Pll */ + PllCfg &= ~(1 << 13); + dib0090_write_reg(state, 0x21, PllCfg); + + /*** Set new Pll configuration in bypass and reset state ***/ + PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); + dib0090_write_reg(state, 0x21, PllCfg); + + /* Remove Reset Pll */ + PllCfg |= (1 << 13); + dib0090_write_reg(state, 0x21, PllCfg); + + /*** Wait for PLL lock ***/ + i = 100; + do { + v = !!(dib0090_read_reg(state, 0x1a) & 0x800); + if (v) + break; + } while (--i); + + if (i == 0) { + dprintk("Pll: Unable to lock Pll\n"); + return; + } + + /* Finally Remove Bypass mode */ + PllCfg &= ~(1 << 15); + dib0090_write_reg(state, 0x21, PllCfg); + } + + if (cfg->io.pll_bypass) { + PllCfg |= (cfg->io.pll_bypass << 15); + dib0090_write_reg(state, 0x21, PllCfg); + } +} + +static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) +{ + struct dib0090_fw_state *state = fe->tuner_priv; + u16 PllCfg; + u16 v; + int i; + + dprintk("fw reset digital\n"); + HARD_RESET(state); + + dib0090_fw_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); + dib0090_fw_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ + + dib0090_fw_write_reg(state, 0x20, + ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (cfg->data_tx_drv << 4) | cfg->ls_cfg_pad_drv); + + v = (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 9) | (0 << 8) | (cfg->clkouttobamse << 4) | (0 << 2) | (0); + if (cfg->clkoutdrive != 0) + v |= cfg->clkoutdrive << 5; + else + v |= 7 << 5; + + v |= 2 << 10; + dib0090_fw_write_reg(state, 0x23, v); + + /* Read Pll current config * */ + PllCfg = dib0090_fw_read_reg(state, 0x21); + + /** Reconfigure PLL if current setting is different from default setting **/ + if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && !cfg->io.pll_bypass) { + + /* Set Bypass mode */ + PllCfg |= (1 << 15); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /* Set Reset Pll */ + PllCfg &= ~(1 << 13); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /*** Set new Pll configuration in bypass and reset state ***/ + PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /* Remove Reset Pll */ + PllCfg |= (1 << 13); + dib0090_fw_write_reg(state, 0x21, PllCfg); + + /*** Wait for PLL lock ***/ + i = 100; + do { + v = !!(dib0090_fw_read_reg(state, 0x1a) & 0x800); + if (v) + break; + } while (--i); + + if (i == 0) { + dprintk("Pll: Unable to lock Pll\n"); + return -EIO; + } + + /* Finally Remove Bypass mode */ + PllCfg &= ~(1 << 15); + dib0090_fw_write_reg(state, 0x21, PllCfg); + } + + if (cfg->io.pll_bypass) { + PllCfg |= (cfg->io.pll_bypass << 15); + dib0090_fw_write_reg(state, 0x21, PllCfg); + } + + return dib0090_fw_identify(fe); +} + +static int dib0090_wakeup(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + if (state->config->sleep) + state->config->sleep(fe, 0); + + /* enable dataTX in case we have been restarted in the wrong moment */ + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); + return 0; +} + +static int dib0090_sleep(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + if (state->config->sleep) + state->config->sleep(fe, 1); + return 0; +} + +void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) +{ + struct dib0090_state *state = fe->tuner_priv; + if (fast) + dib0090_write_reg(state, 0x04, 0); + else + dib0090_write_reg(state, 0x04, 1); +} + +EXPORT_SYMBOL(dib0090_dcc_freq); + +static const u16 bb_ramp_pwm_normal_socs[] = { + 550, /* max BB gain in 10th of dB */ + (1<<9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 440, + (4 << 9) | 0, /* BB_RAMP3 = 26dB */ + (0 << 9) | 208, /* BB_RAMP4 */ + (4 << 9) | 208, /* BB_RAMP5 = 29dB */ + (0 << 9) | 440, /* BB_RAMP6 */ +}; + +static const u16 rf_ramp_pwm_cband_7090p[] = { + 280, /* max RF gain in 10th of dB */ + 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 504, /* ramp_max = maximum X used on the ramp */ + (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ + (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ + (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ + (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ + (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ + (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ + (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ + (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_cband_7090e_sensitivity[] = { + 186, /* max RF gain in 10th of dB */ + 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 746, /* ramp_max = maximum X used on the ramp */ + (10 << 10) | 345, /* RF_RAMP5, LNA 1 = 10dB */ + (0 << 10) | 746, /* RF_RAMP6, LNA 1 */ + (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */ + (0 << 10) | 0, /* RF_RAMP8, LNA 2 */ + (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */ + (0 << 10) | 345, /* GAIN_4_2, LNA 3 */ + (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */ + (0 << 10) | 200, /* RF_RAMP4, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_cband_7090e_aci[] = { + 86, /* max RF gain in 10th of dB */ + 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 345, /* ramp_max = maximum X used on the ramp */ + (0 << 10) | 0, /* RF_RAMP5, LNA 1 = 8dB */ /* 7.47 dB */ + (0 << 10) | 0, /* RF_RAMP6, LNA 1 */ + (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */ + (0 << 10) | 0, /* RF_RAMP8, LNA 2 */ + (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */ + (0 << 10) | 345, /* GAIN_4_2, LNA 3 */ + (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */ + (0 << 10) | 200, /* RF_RAMP4, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_cband_8090[] = { + 345, /* max RF gain in 10th of dB */ + 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1000, /* ramp_max = maximum X used on the ramp */ + (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ + (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ + (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ + (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ + (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ + (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ + (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_uhf_7090[] = { + 407, /* max RF gain in 10th of dB */ + 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 529, /* ramp_max = maximum X used on the ramp */ + (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ + (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ + (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ + (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ + (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_uhf_8090[] = { + 388, /* max RF gain in 10th of dB */ + 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1008, /* ramp_max = maximum X used on the ramp */ + (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ + (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ + (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ + (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ +}; + +/* GENERAL PWM ramp definition for all other Krosus */ +static const u16 bb_ramp_pwm_normal[] = { + 500, /* max BB gain in 10th of dB */ + 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 400, + (2 << 9) | 0, /* BB_RAMP3 = 21dB */ + (0 << 9) | 168, /* BB_RAMP4 */ + (2 << 9) | 168, /* BB_RAMP5 = 29dB */ + (0 << 9) | 400, /* BB_RAMP6 */ +}; + +#if 0 +/* Currently unused */ +static const u16 bb_ramp_pwm_boost[] = { + 550, /* max BB gain in 10th of dB */ + 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 440, + (2 << 9) | 0, /* BB_RAMP3 = 26dB */ + (0 << 9) | 208, /* BB_RAMP4 */ + (2 << 9) | 208, /* BB_RAMP5 = 29dB */ + (0 << 9) | 440, /* BB_RAMP6 */ +}; +#endif + +static const u16 rf_ramp_pwm_cband[] = { + 314, /* max RF gain in 10th of dB */ + 33, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1023, /* ramp_max = maximum X used on the ramp */ + (8 << 10) | 743, /* RF_RAMP3, LNA 1 = 0dB */ + (0 << 10) | 1023, /* RF_RAMP4, LNA 1 */ + (15 << 10) | 469, /* RF_RAMP5, LNA 2 = 0dB */ + (0 << 10) | 742, /* RF_RAMP6, LNA 2 */ + (9 << 10) | 234, /* RF_RAMP7, LNA 3 = 0dB */ + (0 << 10) | 468, /* RF_RAMP8, LNA 3 */ + (9 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ + (0 << 10) | 233, /* GAIN_4_2, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_vhf[] = { + 398, /* max RF gain in 10th of dB */ + 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 954, /* ramp_max = maximum X used on the ramp */ + (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */ + (0 << 10) | 290, /* RF_RAMP4, LNA 1 */ + (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */ + (0 << 10) | 954, /* RF_RAMP6, LNA 2 */ + (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */ + (0 << 10) | 699, /* RF_RAMP8, LNA 3 */ + (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */ + (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ +}; + +static const u16 rf_ramp_pwm_uhf[] = { + 398, /* max RF gain in 10th of dB */ + 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 954, /* ramp_max = maximum X used on the ramp */ + (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */ + (0 << 10) | 290, /* RF_RAMP4, LNA 1 */ + (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */ + (0 << 10) | 954, /* RF_RAMP6, LNA 2 */ + (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */ + (0 << 10) | 699, /* RF_RAMP8, LNA 3 */ + (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */ + (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ +}; + +#if 0 +/* Currently unused */ +static const u16 rf_ramp_pwm_sband[] = { + 253, /* max RF gain in 10th of dB */ + 38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 961, + (4 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.1dB */ + (0 << 10) | 508, /* RF_RAMP4, LNA 1 */ + (9 << 10) | 508, /* RF_RAMP5, LNA 2 = 11.2dB */ + (0 << 10) | 961, /* RF_RAMP6, LNA 2 */ + (0 << 10) | 0, /* RF_RAMP7, LNA 3 = 0dB */ + (0 << 10) | 0, /* RF_RAMP8, LNA 3 */ + (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ + (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ +}; +#endif + +struct slope { + s16 range; + s16 slope; +}; +static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val) +{ + u8 i; + u16 rest; + u16 ret = 0; + for (i = 0; i < num; i++) { + if (val > slopes[i].range) + rest = slopes[i].range; + else + rest = val; + ret += (rest * slopes[i].slope) / slopes[i].range; + val -= rest; + } + return ret; +} + +static const struct slope dib0090_wbd_slopes[3] = { + {66, 120}, /* -64,-52: offset - 65 */ + {600, 170}, /* -52,-35: 65 - 665 */ + {170, 250}, /* -45,-10: 665 - 835 */ +}; + +static s16 dib0090_wbd_to_db(struct dib0090_state *state, u16 wbd) +{ + wbd &= 0x3ff; + if (wbd < state->wbd_offset) + wbd = 0; + else + wbd -= state->wbd_offset; + /* -64dB is the floor */ + return -640 + (s16) slopes_to_scale(dib0090_wbd_slopes, ARRAY_SIZE(dib0090_wbd_slopes), wbd); +} + +static void dib0090_wbd_target(struct dib0090_state *state, u32 rf) +{ + u16 offset = 250; + + /* TODO : DAB digital N+/-1 interferer perfs : offset = 10 */ + + if (state->current_band == BAND_VHF) + offset = 650; +#ifndef FIRMWARE_FIREFLY + if (state->current_band == BAND_VHF) + offset = state->config->wbd_vhf_offset; + if (state->current_band == BAND_CBAND) + offset = state->config->wbd_cband_offset; +#endif + + state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + offset); + dprintk("wbd-target: %d dB\n", (u32) state->wbd_target); +} + +static const int gain_reg_addr[4] = { + 0x08, 0x0a, 0x0f, 0x01 +}; + +static void dib0090_gain_apply(struct dib0090_state *state, s16 gain_delta, s16 top_delta, u8 force) +{ + u16 rf, bb, ref; + u16 i, v, gain_reg[4] = { 0 }, gain; + const u16 *g; + + if (top_delta < -511) + top_delta = -511; + if (top_delta > 511) + top_delta = 511; + + if (force) { + top_delta *= (1 << WBD_ALPHA); + gain_delta *= (1 << GAIN_ALPHA); + } + + if (top_delta >= ((s16) (state->rf_ramp[0] << WBD_ALPHA) - state->rf_gain_limit)) /* overflow */ + state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA; + else + state->rf_gain_limit += top_delta; + + if (state->rf_gain_limit < 0) /*underflow */ + state->rf_gain_limit = 0; + + /* use gain as a temporary variable and correct current_gain */ + gain = ((state->rf_gain_limit >> WBD_ALPHA) + state->bb_ramp[0]) << GAIN_ALPHA; + if (gain_delta >= ((s16) gain - state->current_gain)) /* overflow */ + state->current_gain = gain; + else + state->current_gain += gain_delta; + /* cannot be less than 0 (only if gain_delta is less than 0 we can have current_gain < 0) */ + if (state->current_gain < 0) + state->current_gain = 0; + + /* now split total gain to rf and bb gain */ + gain = state->current_gain >> GAIN_ALPHA; + + /* requested gain is bigger than rf gain limit - ACI/WBD adjustment */ + if (gain > (state->rf_gain_limit >> WBD_ALPHA)) { + rf = state->rf_gain_limit >> WBD_ALPHA; + bb = gain - rf; + if (bb > state->bb_ramp[0]) + bb = state->bb_ramp[0]; + } else { /* high signal level -> all gains put on RF */ + rf = gain; + bb = 0; + } + + state->gain[0] = rf; + state->gain[1] = bb; + + /* software ramp */ + /* Start with RF gains */ + g = state->rf_ramp + 1; /* point on RF LNA1 max gain */ + ref = rf; + for (i = 0; i < 7; i++) { /* Go over all amplifiers => 5RF amps + 2 BB amps = 7 amps */ + if (g[0] == 0 || ref < (g[1] - g[0])) /* if total gain of the current amp is null or this amp is not concerned because it starts to work from an higher gain value */ + v = 0; /* force the gain to write for the current amp to be null */ + else if (ref >= g[1]) /* Gain to set is higher than the high working point of this amp */ + v = g[2]; /* force this amp to be full gain */ + else /* compute the value to set to this amp because we are somewhere in his range */ + v = ((ref - (g[1] - g[0])) * g[2]) / g[0]; + + if (i == 0) /* LNA 1 reg mapping */ + gain_reg[0] = v; + else if (i == 1) /* LNA 2 reg mapping */ + gain_reg[0] |= v << 7; + else if (i == 2) /* LNA 3 reg mapping */ + gain_reg[1] = v; + else if (i == 3) /* LNA 4 reg mapping */ + gain_reg[1] |= v << 7; + else if (i == 4) /* CBAND LNA reg mapping */ + gain_reg[2] = v | state->rf_lt_def; + else if (i == 5) /* BB gain 1 reg mapping */ + gain_reg[3] = v << 3; + else if (i == 6) /* BB gain 2 reg mapping */ + gain_reg[3] |= v << 8; + + g += 3; /* go to next gain bloc */ + + /* When RF is finished, start with BB */ + if (i == 4) { + g = state->bb_ramp + 1; /* point on BB gain 1 max gain */ + ref = bb; + } + } + gain_reg[3] |= state->bb_1_def; + gain_reg[3] |= ((bb % 10) * 100) / 125; + +#ifdef DEBUG_AGC + dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x\n", rf, bb, rf + bb, + gain_reg[0], gain_reg[1], gain_reg[2], gain_reg[3]); +#endif + + /* Write the amplifier regs */ + for (i = 0; i < 4; i++) { + v = gain_reg[i]; + if (force || state->gain_reg[i] != v) { + state->gain_reg[i] = v; + dib0090_write_reg(state, gain_reg_addr[i], v); + } + } +} + +static void dib0090_set_boost(struct dib0090_state *state, int onoff) +{ + state->bb_1_def &= 0xdfff; + state->bb_1_def |= onoff << 13; +} + +static void dib0090_set_rframp(struct dib0090_state *state, const u16 * cfg) +{ + state->rf_ramp = cfg; +} + +static void dib0090_set_rframp_pwm(struct dib0090_state *state, const u16 * cfg) +{ + state->rf_ramp = cfg; + + dib0090_write_reg(state, 0x2a, 0xffff); + + dprintk("total RF gain: %ddB, step: %d\n", (u32) cfg[0], dib0090_read_reg(state, 0x2a)); + + dib0090_write_regs(state, 0x2c, cfg + 3, 6); + dib0090_write_regs(state, 0x3e, cfg + 9, 2); +} + +static void dib0090_set_bbramp(struct dib0090_state *state, const u16 * cfg) +{ + state->bb_ramp = cfg; + dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */ +} + +static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg) +{ + state->bb_ramp = cfg; + + dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */ + + dib0090_write_reg(state, 0x33, 0xffff); + dprintk("total BB gain: %ddB, step: %d\n", (u32) cfg[0], dib0090_read_reg(state, 0x33)); + dib0090_write_regs(state, 0x35, cfg + 3, 4); +} + +void dib0090_pwm_gain_reset(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + const u16 *bb_ramp = bb_ramp_pwm_normal; /* default baseband config */ + const u16 *rf_ramp = NULL; + u8 en_pwm_rf_mux = 1; + + /* reset the AGC */ + if (state->config->use_pwm_agc) { + if (state->current_band == BAND_CBAND) { + if (state->identity.in_soc) { + bb_ramp = bb_ramp_pwm_normal_socs; + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + rf_ramp = rf_ramp_pwm_cband_8090; + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) { + if (state->config->is_dib7090e) { + if (state->rf_ramp == NULL) + rf_ramp = rf_ramp_pwm_cband_7090e_sensitivity; + else + rf_ramp = state->rf_ramp; + } else + rf_ramp = rf_ramp_pwm_cband_7090p; + } + } else + rf_ramp = rf_ramp_pwm_cband; + } else + + if (state->current_band == BAND_VHF) { + if (state->identity.in_soc) { + bb_ramp = bb_ramp_pwm_normal_socs; + /* rf_ramp = &rf_ramp_pwm_vhf_socs; */ /* TODO */ + } else + rf_ramp = rf_ramp_pwm_vhf; + } else if (state->current_band == BAND_UHF) { + if (state->identity.in_soc) { + bb_ramp = bb_ramp_pwm_normal_socs; + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + rf_ramp = rf_ramp_pwm_uhf_8090; + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + rf_ramp = rf_ramp_pwm_uhf_7090; + } else + rf_ramp = rf_ramp_pwm_uhf; + } + if (rf_ramp) + dib0090_set_rframp_pwm(state, rf_ramp); + dib0090_set_bbramp_pwm(state, bb_ramp); + + /* activate the ramp generator using PWM control */ + if (state->rf_ramp) + dprintk("ramp RF gain = %d BAND = %s version = %d\n", + state->rf_ramp[0], + (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", + state->identity.version & 0x1f); + + if (rf_ramp && ((state->rf_ramp && state->rf_ramp[0] == 0) || + (state->current_band == BAND_CBAND && + (state->identity.version & 0x1f) <= P1D_E_F))) { + dprintk("DE-Engage mux for direct gain reg control\n"); + en_pwm_rf_mux = 0; + } else + dprintk("Engage mux for PWM control\n"); + + dib0090_write_reg(state, 0x32, (en_pwm_rf_mux << 12) | (en_pwm_rf_mux << 11)); + + /* Set fast servo cutoff to start AGC; 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast*/ + if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + dib0090_write_reg(state, 0x04, 3); + else + dib0090_write_reg(state, 0x04, 1); + dib0090_write_reg(state, 0x39, (1 << 10)); /* 0 gain by default */ + } +} +EXPORT_SYMBOL(dib0090_pwm_gain_reset); + +void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) +{ + struct dib0090_state *state = fe->tuner_priv; + if (DC_servo_cutoff < 4) + dib0090_write_reg(state, 0x04, DC_servo_cutoff); +} +EXPORT_SYMBOL(dib0090_set_dc_servo); + +static u32 dib0090_get_slow_adc_val(struct dib0090_state *state) +{ + u16 adc_val = dib0090_read_reg(state, 0x1d); + if (state->identity.in_soc) + adc_val >>= 2; + return adc_val; +} + +int dib0090_gain_control(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + enum frontend_tune_state *tune_state = &state->tune_state; + int ret = 10; + + u16 wbd_val = 0; + u8 apply_gain_immediatly = 1; + s16 wbd_error = 0, adc_error = 0; + + if (*tune_state == CT_AGC_START) { + state->agc_freeze = 0; + dib0090_write_reg(state, 0x04, 0x0); + +#ifdef CONFIG_BAND_SBAND + if (state->current_band == BAND_SBAND) { + dib0090_set_rframp(state, rf_ramp_sband); + dib0090_set_bbramp(state, bb_ramp_boost); + } else +#endif +#ifdef CONFIG_BAND_VHF + if (state->current_band == BAND_VHF && !state->identity.p1g) { + dib0090_set_rframp(state, rf_ramp_pwm_vhf); + dib0090_set_bbramp(state, bb_ramp_pwm_normal); + } else +#endif +#ifdef CONFIG_BAND_CBAND + if (state->current_band == BAND_CBAND && !state->identity.p1g) { + dib0090_set_rframp(state, rf_ramp_pwm_cband); + dib0090_set_bbramp(state, bb_ramp_pwm_normal); + } else +#endif + if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) { + dib0090_set_rframp(state, rf_ramp_pwm_cband_7090p); + dib0090_set_bbramp(state, bb_ramp_pwm_normal_socs); + } else { + dib0090_set_rframp(state, rf_ramp_pwm_uhf); + dib0090_set_bbramp(state, bb_ramp_pwm_normal); + } + + dib0090_write_reg(state, 0x32, 0); + dib0090_write_reg(state, 0x39, 0); + + dib0090_wbd_target(state, state->current_rf); + + state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA; + state->current_gain = ((state->rf_ramp[0] + state->bb_ramp[0]) / 2) << GAIN_ALPHA; + + *tune_state = CT_AGC_STEP_0; + } else if (!state->agc_freeze) { + s16 wbd = 0, i, cnt; + + int adc; + wbd_val = dib0090_get_slow_adc_val(state); + + if (*tune_state == CT_AGC_STEP_0) + cnt = 5; + else + cnt = 1; + + for (i = 0; i < cnt; i++) { + wbd_val = dib0090_get_slow_adc_val(state); + wbd += dib0090_wbd_to_db(state, wbd_val); + } + wbd /= cnt; + wbd_error = state->wbd_target - wbd; + + if (*tune_state == CT_AGC_STEP_0) { + if (wbd_error < 0 && state->rf_gain_limit > 0 && !state->identity.p1g) { +#ifdef CONFIG_BAND_CBAND + /* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */ + u8 ltg2 = (state->rf_lt_def >> 10) & 0x7; + if (state->current_band == BAND_CBAND && ltg2) { + ltg2 >>= 1; + state->rf_lt_def &= ltg2 << 10; /* reduce in 3 steps from 7 to 0 */ + } +#endif + } else { + state->agc_step = 0; + *tune_state = CT_AGC_STEP_1; + } + } else { + /* calc the adc power */ + adc = state->config->get_adc_power(fe); + adc = (adc * ((s32) 355774) + (((s32) 1) << 20)) >> 21; /* included in [0:-700] */ + + adc_error = (s16) (((s32) ADC_TARGET) - adc); +#ifdef CONFIG_STANDARD_DAB + if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) + adc_error -= 10; +#endif +#ifdef CONFIG_STANDARD_DVBT + if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT && + (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16)) + adc_error += 60; +#endif +#ifdef CONFIG_SYS_ISDBT + if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count > + 0) + && + ((state->fe->dtv_property_cache.layer[0].modulation == + QAM_64) + || (state->fe->dtv_property_cache. + layer[0].modulation == QAM_16))) + || + ((state->fe->dtv_property_cache.layer[1].segment_count > + 0) + && + ((state->fe->dtv_property_cache.layer[1].modulation == + QAM_64) + || (state->fe->dtv_property_cache. + layer[1].modulation == QAM_16))) + || + ((state->fe->dtv_property_cache.layer[2].segment_count > + 0) + && + ((state->fe->dtv_property_cache.layer[2].modulation == + QAM_64) + || (state->fe->dtv_property_cache. + layer[2].modulation == QAM_16))) + ) + ) + adc_error += 60; +#endif + + if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */ + if (abs(adc_error) < 50 || state->agc_step++ > 5) { + +#ifdef CONFIG_STANDARD_DAB + if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) { + dib0090_write_reg(state, 0x02, (1 << 15) | (15 << 11) | (31 << 6) | (63)); /* cap value = 63 : narrow BB filter : Fc = 1.8MHz */ + dib0090_write_reg(state, 0x04, 0x0); + } else +#endif + { + dib0090_write_reg(state, 0x02, (1 << 15) | (3 << 11) | (6 << 6) | (32)); + dib0090_write_reg(state, 0x04, 0x01); /*0 = 1KHz ; 1 = 150Hz ; 2 = 50Hz ; 3 = 50KHz ; 4 = servo fast */ + } + + *tune_state = CT_AGC_STOP; + } + } else { + /* everything higher than or equal to CT_AGC_STOP means tracking */ + ret = 100; /* 10ms interval */ + apply_gain_immediatly = 0; + } + } +#ifdef DEBUG_AGC + dprintk + ("tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm", + (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, + (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA)); +#endif + } + + /* apply gain */ + if (!state->agc_freeze) + dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly); + return ret; +} + +EXPORT_SYMBOL(dib0090_gain_control); + +void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) +{ + struct dib0090_state *state = fe->tuner_priv; + if (rf) + *rf = state->gain[0]; + if (bb) + *bb = state->gain[1]; + if (rf_gain_limit) + *rf_gain_limit = state->rf_gain_limit; + if (rflt) + *rflt = (state->rf_lt_def >> 10) & 0x7; +} + +EXPORT_SYMBOL(dib0090_get_current_gain); + +u16 dib0090_get_wbd_target(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + u32 f_MHz = state->fe->dtv_property_cache.frequency / 1000000; + s32 current_temp = state->temperature; + s32 wbd_thot, wbd_tcold; + const struct dib0090_wbd_slope *wbd = state->current_wbd_table; + + while (f_MHz > wbd->max_freq) + wbd++; + + dprintk("using wbd-table-entry with max freq %d\n", wbd->max_freq); + + if (current_temp < 0) + current_temp = 0; + if (current_temp > 128) + current_temp = 128; + + state->wbdmux &= ~(7 << 13); + if (wbd->wbd_gain != 0) + state->wbdmux |= (wbd->wbd_gain << 13); + else + state->wbdmux |= (4 << 13); + + dib0090_write_reg(state, 0x10, state->wbdmux); + + wbd_thot = wbd->offset_hot - (((u32) wbd->slope_hot * f_MHz) >> 6); + wbd_tcold = wbd->offset_cold - (((u32) wbd->slope_cold * f_MHz) >> 6); + + wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7; + + state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold); + dprintk("wbd-target: %d dB\n", (u32) state->wbd_target); + dprintk("wbd offset applied is %d\n", wbd_tcold); + + return state->wbd_offset + wbd_tcold; +} +EXPORT_SYMBOL(dib0090_get_wbd_target); + +u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + return state->wbd_offset; +} +EXPORT_SYMBOL(dib0090_get_wbd_offset); + +int dib0090_set_switch(struct dvb_frontend *fe, u8 sw1, u8 sw2, u8 sw3) +{ + struct dib0090_state *state = fe->tuner_priv; + + dib0090_write_reg(state, 0x0b, (dib0090_read_reg(state, 0x0b) & 0xfff8) + | ((sw3 & 1) << 2) | ((sw2 & 1) << 1) | (sw1 & 1)); + + return 0; +} +EXPORT_SYMBOL(dib0090_set_switch); + +int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff) +{ + struct dib0090_state *state = fe->tuner_priv; + + dib0090_write_reg(state, 0x09, (dib0090_read_reg(state, 0x09) & 0x7fff) + | ((onoff & 1) << 15)); + return 0; +} +EXPORT_SYMBOL(dib0090_set_vga); + +int dib0090_update_rframp_7090(struct dvb_frontend *fe, u8 cfg_sensitivity) +{ + struct dib0090_state *state = fe->tuner_priv; + + if ((!state->identity.p1g) || (!state->identity.in_soc) + || ((state->identity.version != SOC_7090_P1G_21R1) + && (state->identity.version != SOC_7090_P1G_11R1))) { + dprintk("%s() function can only be used for dib7090P\n", __func__); + return -ENODEV; + } + + if (cfg_sensitivity) + state->rf_ramp = rf_ramp_pwm_cband_7090e_sensitivity; + else + state->rf_ramp = rf_ramp_pwm_cband_7090e_aci; + dib0090_pwm_gain_reset(fe); + + return 0; +} +EXPORT_SYMBOL(dib0090_update_rframp_7090); + +static const u16 dib0090_defaults[] = { + + 25, 0x01, + 0x0000, + 0x99a0, + 0x6008, + 0x0000, + 0x8bcb, + 0x0000, + 0x0405, + 0x0000, + 0x0000, + 0x0000, + 0xb802, + 0x0300, + 0x2d12, + 0xbac0, + 0x7c00, + 0xdbb9, + 0x0954, + 0x0743, + 0x8000, + 0x0001, + 0x0040, + 0x0100, + 0x0000, + 0xe910, + 0x149e, + + 1, 0x1c, + 0xff2d, + + 1, 0x39, + 0x0000, + + 2, 0x1e, + 0x07FF, + 0x0007, + + 1, 0x24, + EN_UHF | EN_CRYSTAL, + + 2, 0x3c, + 0x3ff, + 0x111, + 0 +}; + +static const u16 dib0090_p1g_additionnal_defaults[] = { + 1, 0x05, + 0xabcd, + + 1, 0x11, + 0x00b4, + + 1, 0x1c, + 0xfffd, + + 1, 0x40, + 0x108, + 0 +}; + +static void dib0090_set_default_config(struct dib0090_state *state, const u16 * n) +{ + u16 l, r; + + l = pgm_read_word(n++); + while (l) { + r = pgm_read_word(n++); + do { + dib0090_write_reg(state, r, pgm_read_word(n++)); + r++; + } while (--l); + l = pgm_read_word(n++); + } +} + +#define CAP_VALUE_MIN (u8) 9 +#define CAP_VALUE_MAX (u8) 40 +#define HR_MIN (u8) 25 +#define HR_MAX (u8) 40 +#define POLY_MIN (u8) 0 +#define POLY_MAX (u8) 8 + +static void dib0090_set_EFUSE(struct dib0090_state *state) +{ + u8 c, h, n; + u16 e2, e4; + u16 cal; + + e2 = dib0090_read_reg(state, 0x26); + e4 = dib0090_read_reg(state, 0x28); + + if ((state->identity.version == P1D_E_F) || + (state->identity.version == P1G) || (e2 == 0xffff)) { + + dib0090_write_reg(state, 0x22, 0x10); + cal = (dib0090_read_reg(state, 0x22) >> 6) & 0x3ff; + + if ((cal < 670) || (cal == 1023)) + cal = 850; + n = 165 - ((cal * 10)>>6) ; + e2 = e4 = (3<<12) | (34<<6) | (n); + } + + if (e2 != e4) + e2 &= e4; /* Remove the redundancy */ + + if (e2 != 0xffff) { + c = e2 & 0x3f; + n = (e2 >> 12) & 0xf; + h = (e2 >> 6) & 0x3f; + + if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) + c = 32; + else + c += 14; + if ((h >= HR_MAX) || (h <= HR_MIN)) + h = 34; + if ((n >= POLY_MAX) || (n <= POLY_MIN)) + n = 3; + + dib0090_write_reg(state, 0x13, (h << 10)); + e2 = (n << 11) | ((h >> 2)<<6) | c; + dib0090_write_reg(state, 0x2, e2); /* Load the BB_2 */ + } +} + +static int dib0090_reset(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + + dib0090_reset_digital(fe, state->config); + if (dib0090_identify(fe) < 0) + return -EIO; + +#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT + if (!(state->identity.version & 0x1)) /* it is P1B - reset is already done */ + return 0; +#endif + + if (!state->identity.in_soc) { + if ((dib0090_read_reg(state, 0x1a) >> 5) & 0x2) + dib0090_write_reg(state, 0x1b, (EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL)); + else + dib0090_write_reg(state, 0x1b, (EN_DIGCLK | EN_PLL | EN_CRYSTAL)); + } + + dib0090_set_default_config(state, dib0090_defaults); + + if (state->identity.in_soc) + dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */ + + if (state->identity.p1g) + dib0090_set_default_config(state, dib0090_p1g_additionnal_defaults); + + /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/ + if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc)) + dib0090_set_EFUSE(state); + + /* Congigure in function of the crystal */ + if (state->config->force_crystal_mode != 0) + dib0090_write_reg(state, 0x14, + state->config->force_crystal_mode & 3); + else if (state->config->io.clock_khz >= 24000) + dib0090_write_reg(state, 0x14, 1); + else + dib0090_write_reg(state, 0x14, 2); + dprintk("Pll lock : %d\n", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1); + + state->calibrate = DC_CAL | WBD_CAL | TEMP_CAL; /* enable iq-offset-calibration and wbd-calibration when tuning next time */ + + return 0; +} + +#define steps(u) (((u) > 15) ? ((u)-16) : (u)) +#define INTERN_WAIT 10 +static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + int ret = INTERN_WAIT * 10; + + switch (*tune_state) { + case CT_TUNER_STEP_2: + /* Turns to positive */ + dib0090_write_reg(state, 0x1f, 0x7); + *tune_state = CT_TUNER_STEP_3; + break; + + case CT_TUNER_STEP_3: + state->adc_diff = dib0090_read_reg(state, 0x1d); + + /* Turns to negative */ + dib0090_write_reg(state, 0x1f, 0x4); + *tune_state = CT_TUNER_STEP_4; + break; + + case CT_TUNER_STEP_4: + state->adc_diff -= dib0090_read_reg(state, 0x1d); + *tune_state = CT_TUNER_STEP_5; + ret = 0; + break; + + default: + break; + } + + return ret; +} + +struct dc_calibration { + u8 addr; + u8 offset; + u8 pga:1; + u16 bb1; + u8 i:1; +}; + +static const struct dc_calibration dc_table[] = { + /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ + {0x06, 5, 1, (1 << 13) | (0 << 8) | (26 << 3), 1}, + {0x07, 11, 1, (1 << 13) | (0 << 8) | (26 << 3), 0}, + /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ + {0x06, 0, 0, (1 << 13) | (29 << 8) | (26 << 3), 1}, + {0x06, 10, 0, (1 << 13) | (29 << 8) | (26 << 3), 0}, + {0}, +}; + +static const struct dc_calibration dc_p1g_table[] = { + /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ + /* addr ; trim reg offset ; pga ; CTRL_BB1 value ; i or q */ + {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1}, + {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0}, + /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ + {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1}, + {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0}, + {0}, +}; + +static void dib0090_set_trim(struct dib0090_state *state) +{ + u16 *val; + + if (state->dc->addr == 0x07) + val = &state->bb7; + else + val = &state->bb6; + + *val &= ~(0x1f << state->dc->offset); + *val |= state->step << state->dc->offset; + + dib0090_write_reg(state, state->dc->addr, *val); +} + +static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + int ret = 0; + u16 reg; + + switch (*tune_state) { + case CT_TUNER_START: + dprintk("Start DC offset calibration"); + + /* force vcm2 = 0.8V */ + state->bb6 = 0; + state->bb7 = 0x040d; + + /* the LNA AND LO are off */ + reg = dib0090_read_reg(state, 0x24) & 0x0ffb; /* shutdown lna and lo */ + dib0090_write_reg(state, 0x24, reg); + + state->wbdmux = dib0090_read_reg(state, 0x10); + dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3); + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); + + state->dc = dc_table; + + if (state->identity.p1g) + state->dc = dc_p1g_table; + + fallthrough; + case CT_TUNER_STEP_0: + dprintk("Start/continue DC calibration for %s path\n", + (state->dc->i == 1) ? "I" : "Q"); + dib0090_write_reg(state, 0x01, state->dc->bb1); + dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7)); + + state->step = 0; + state->min_adc_diff = 1023; + *tune_state = CT_TUNER_STEP_1; + ret = 50; + break; + + case CT_TUNER_STEP_1: + dib0090_set_trim(state); + *tune_state = CT_TUNER_STEP_2; + break; + + case CT_TUNER_STEP_2: + case CT_TUNER_STEP_3: + case CT_TUNER_STEP_4: + ret = dib0090_get_offset(state, tune_state); + break; + + case CT_TUNER_STEP_5: /* found an offset */ + dprintk("adc_diff = %d, current step= %d\n", (u32) state->adc_diff, state->step); + if (state->step == 0 && state->adc_diff < 0) { + state->min_adc_diff = -1023; + dprintk("Change of sign of the minimum adc diff\n"); + } + + dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d\n", state->adc_diff, state->min_adc_diff, state->step); + + /* first turn for this frequency */ + if (state->step == 0) { + if (state->dc->pga && state->adc_diff < 0) + state->step = 0x10; + if (state->dc->pga == 0 && state->adc_diff > 0) + state->step = 0x10; + } + + /* Look for a change of Sign in the Adc_diff.min_adc_diff is used to STORE the setp N-1 */ + if ((state->adc_diff & 0x8000) == (state->min_adc_diff & 0x8000) && steps(state->step) < 15) { + /* stop search when the delta the sign is changing and Steps =15 and Step=0 is force for continuance */ + state->step++; + state->min_adc_diff = state->adc_diff; + *tune_state = CT_TUNER_STEP_1; + } else { + /* the minimum was what we have seen in the step before */ + if (abs(state->adc_diff) > abs(state->min_adc_diff)) { + dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step\n", state->adc_diff, state->min_adc_diff); + state->step--; + } + + dib0090_set_trim(state); + dprintk("BB Offset Cal, BBreg=%u,Offset=%d,Value Set=%d\n", + state->dc->addr, state->adc_diff, state->step); + + state->dc++; + if (state->dc->addr == 0) /* done */ + *tune_state = CT_TUNER_STEP_6; + else + *tune_state = CT_TUNER_STEP_0; + + } + break; + + case CT_TUNER_STEP_6: + dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); + dib0090_write_reg(state, 0x1f, 0x7); + *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ + state->calibrate &= ~DC_CAL; + break; + + default: + break; + } + return ret; +} + +static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + u8 wbd_gain; + const struct dib0090_wbd_slope *wbd = state->current_wbd_table; + + switch (*tune_state) { + case CT_TUNER_START: + while (state->current_rf / 1000 > wbd->max_freq) + wbd++; + if (wbd->wbd_gain != 0) + wbd_gain = wbd->wbd_gain; + else { + wbd_gain = 4; +#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND) + if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) + wbd_gain = 2; +#endif + } + + if (wbd_gain == state->wbd_calibration_gain) { /* the WBD calibration has already been done */ + *tune_state = CT_TUNER_START; + state->calibrate &= ~WBD_CAL; + return 0; + } + + dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3)); + + dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1))); + *tune_state = CT_TUNER_STEP_0; + state->wbd_calibration_gain = wbd_gain; + return 90; /* wait for the WBDMUX to switch and for the ADC to sample */ + + case CT_TUNER_STEP_0: + state->wbd_offset = dib0090_get_slow_adc_val(state); + dprintk("WBD calibration offset = %d\n", state->wbd_offset); + *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ + state->calibrate &= ~WBD_CAL; + break; + + default: + break; + } + return 0; +} + +static void dib0090_set_bandwidth(struct dib0090_state *state) +{ + u16 tmp; + + if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 5000) + tmp = (3 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 6000) + tmp = (2 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 7000) + tmp = (1 << 14); + else + tmp = (0 << 14); + + state->bb_1_def &= 0x3fff; + state->bb_1_def |= tmp; + + dib0090_write_reg(state, 0x01, state->bb_1_def); /* be sure that we have the right bb-filter */ + + dib0090_write_reg(state, 0x03, 0x6008); /* = 0x6008 : vcm3_trim = 1 ; filter2_gm1_trim = 8 ; filter2_cutoff_freq = 0 */ + dib0090_write_reg(state, 0x04, 0x1); /* 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast */ + if (state->identity.in_soc) { + dib0090_write_reg(state, 0x05, 0x9bcf); /* attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 1 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 15 */ + } else { + dib0090_write_reg(state, 0x02, (5 << 11) | (8 << 6) | (22 & 0x3f)); /* 22 = cap_value */ + dib0090_write_reg(state, 0x05, 0xabcd); /* = 0xabcd : attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 2 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 13 */ + } +} + +static const struct dib0090_pll dib0090_pll_table[] = { +#ifdef CONFIG_BAND_CBAND + {56000, 0, 9, 48, 6}, + {70000, 1, 9, 48, 6}, + {87000, 0, 8, 32, 4}, + {105000, 1, 8, 32, 4}, + {115000, 0, 7, 24, 6}, + {140000, 1, 7, 24, 6}, + {170000, 0, 6, 16, 4}, +#endif +#ifdef CONFIG_BAND_VHF + {200000, 1, 6, 16, 4}, + {230000, 0, 5, 12, 6}, + {280000, 1, 5, 12, 6}, + {340000, 0, 4, 8, 4}, + {380000, 1, 4, 8, 4}, + {450000, 0, 3, 6, 6}, +#endif +#ifdef CONFIG_BAND_UHF + {580000, 1, 3, 6, 6}, + {700000, 0, 2, 4, 4}, + {860000, 1, 2, 4, 4}, +#endif +#ifdef CONFIG_BAND_LBAND + {1800000, 1, 0, 2, 4}, +#endif +#ifdef CONFIG_BAND_SBAND + {2900000, 0, 14, 1, 4}, +#endif +}; + +static const struct dib0090_tuning dib0090_tuning_table_fm_vhf_on_cband[] = { + +#ifdef CONFIG_BAND_CBAND + {184000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, + {227000, 4, 3, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, + {380000, 4, 7, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, +#endif +#ifdef CONFIG_BAND_UHF + {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +#endif +#ifdef CONFIG_BAND_LBAND + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +#endif +#ifdef CONFIG_BAND_SBAND + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +#endif +}; + +static const struct dib0090_tuning dib0090_tuning_table[] = { + +#ifdef CONFIG_BAND_CBAND + {170000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, +#endif +#ifdef CONFIG_BAND_VHF + {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, + {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, + {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +#endif +#ifdef CONFIG_BAND_UHF + {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +#endif +#ifdef CONFIG_BAND_LBAND + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +#endif +#ifdef CONFIG_BAND_SBAND + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +#endif +}; + +static const struct dib0090_tuning dib0090_p1g_tuning_table[] = { +#ifdef CONFIG_BAND_CBAND + {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB}, +#endif +#ifdef CONFIG_BAND_VHF + {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, + {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, + {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +#endif +#ifdef CONFIG_BAND_UHF + {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +#endif +#ifdef CONFIG_BAND_LBAND + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +#endif +#ifdef CONFIG_BAND_SBAND + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +#endif +}; + +static const struct dib0090_pll dib0090_p1g_pll_table[] = { +#ifdef CONFIG_BAND_CBAND + {57000, 0, 11, 48, 6}, + {70000, 1, 11, 48, 6}, + {86000, 0, 10, 32, 4}, + {105000, 1, 10, 32, 4}, + {115000, 0, 9, 24, 6}, + {140000, 1, 9, 24, 6}, + {170000, 0, 8, 16, 4}, +#endif +#ifdef CONFIG_BAND_VHF + {200000, 1, 8, 16, 4}, + {230000, 0, 7, 12, 6}, + {280000, 1, 7, 12, 6}, + {340000, 0, 6, 8, 4}, + {380000, 1, 6, 8, 4}, + {455000, 0, 5, 6, 6}, +#endif +#ifdef CONFIG_BAND_UHF + {580000, 1, 5, 6, 6}, + {680000, 0, 4, 4, 4}, + {860000, 1, 4, 4, 4}, +#endif +#ifdef CONFIG_BAND_LBAND + {1800000, 1, 2, 2, 4}, +#endif +#ifdef CONFIG_BAND_SBAND + {2900000, 0, 1, 1, 6}, +#endif +}; + +static const struct dib0090_tuning dib0090_p1g_tuning_table_fm_vhf_on_cband[] = { +#ifdef CONFIG_BAND_CBAND + {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, + {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, + {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, +#endif +#ifdef CONFIG_BAND_UHF + {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, + {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +#endif +#ifdef CONFIG_BAND_LBAND + {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, + {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +#endif +#ifdef CONFIG_BAND_SBAND + {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, + {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +#endif +}; + +static const struct dib0090_tuning dib0090_tuning_table_cband_7090[] = { +#ifdef CONFIG_BAND_CBAND + {300000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, + {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, + {570000, 4, 10, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, + {858000, 4, 5, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, +#endif +}; + +static const struct dib0090_tuning dib0090_tuning_table_cband_7090e_sensitivity[] = { +#ifdef CONFIG_BAND_CBAND + { 300000, 0 , 3, 0x8105, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, + { 380000, 0 , 10, 0x810F, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, + { 600000, 0 , 10, 0x815E, 0x280, 0x2d12, 0xb84e, EN_CAB }, + { 660000, 0 , 5, 0x85E3, 0x280, 0x2d12, 0xb84e, EN_CAB }, + { 720000, 0 , 5, 0x852E, 0x280, 0x2d12, 0xb84e, EN_CAB }, + { 860000, 0 , 4, 0x85E5, 0x280, 0x2d12, 0xb84e, EN_CAB }, +#endif +}; + +int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, + u8 cfg_sensitivity) +{ + struct dib0090_state *state = fe->tuner_priv; + const struct dib0090_tuning *tune = + dib0090_tuning_table_cband_7090e_sensitivity; + static const struct dib0090_tuning dib0090_tuning_table_cband_7090e_aci[] = { + { 300000, 0 , 3, 0x8165, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, + { 650000, 0 , 4, 0x815B, 0x280, 0x2d12, 0xb84e, EN_CAB }, + { 860000, 0 , 5, 0x84EF, 0x280, 0x2d12, 0xb84e, EN_CAB }, + }; + + if ((!state->identity.p1g) || (!state->identity.in_soc) + || ((state->identity.version != SOC_7090_P1G_21R1) + && (state->identity.version != SOC_7090_P1G_11R1))) { + dprintk("%s() function can only be used for dib7090\n", __func__); + return -ENODEV; + } + + if (cfg_sensitivity) + tune = dib0090_tuning_table_cband_7090e_sensitivity; + else + tune = dib0090_tuning_table_cband_7090e_aci; + + while (state->rf_request > tune->max_freq) + tune++; + + dib0090_write_reg(state, 0x09, (dib0090_read_reg(state, 0x09) & 0x8000) + | (tune->lna_bias & 0x7fff)); + dib0090_write_reg(state, 0x0b, (dib0090_read_reg(state, 0x0b) & 0xf83f) + | ((tune->lna_tune << 6) & 0x07c0)); + return 0; +} +EXPORT_SYMBOL(dib0090_update_tuning_table_7090); + +static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + int ret = 0; + u16 lo4 = 0xe900; + + s16 adc_target; + u16 adc; + s8 step_sign; + u8 force_soft_search = 0; + + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + force_soft_search = 1; + + if (*tune_state == CT_TUNER_START) { + dprintk("Start Captrim search : %s\n", + (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO"); + dib0090_write_reg(state, 0x10, 0x2B1); + dib0090_write_reg(state, 0x1e, 0x0032); + + if (!state->tuner_is_tuned) { + /* prepare a complete captrim */ + if (!state->identity.p1g || force_soft_search) + state->step = state->captrim = state->fcaptrim = 64; + + state->current_rf = state->rf_request; + } else { /* we are already tuned to this frequency - the configuration is correct */ + if (!state->identity.p1g || force_soft_search) { + /* do a minimal captrim even if the frequency has not changed */ + state->step = 4; + state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f; + } + } + state->adc_diff = 3000; + *tune_state = CT_TUNER_STEP_0; + + } else if (*tune_state == CT_TUNER_STEP_0) { + if (state->identity.p1g && !force_soft_search) { + u8 ratio = 31; + + dib0090_write_reg(state, 0x40, (3 << 7) | (ratio << 2) | (1 << 1) | 1); + dib0090_read_reg(state, 0x40); + ret = 50; + } else { + state->step /= 2; + dib0090_write_reg(state, 0x18, lo4 | state->captrim); + + if (state->identity.in_soc) + ret = 25; + } + *tune_state = CT_TUNER_STEP_1; + + } else if (*tune_state == CT_TUNER_STEP_1) { + if (state->identity.p1g && !force_soft_search) { + dib0090_write_reg(state, 0x40, 0x18c | (0 << 1) | 0); + dib0090_read_reg(state, 0x40); + + state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7F; + dprintk("***Final Captrim= 0x%x\n", state->fcaptrim); + *tune_state = CT_TUNER_STEP_3; + + } else { + /* MERGE for all krosus before P1G */ + adc = dib0090_get_slow_adc_val(state); + dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV\n", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024); + + if (state->rest == 0 || state->identity.in_soc) { /* Just for 8090P SOCS where auto captrim HW bug : TO CHECK IN ACI for SOCS !!! if 400 for 8090p SOC => tune issue !!! */ + adc_target = 200; + } else + adc_target = 400; + + if (adc >= adc_target) { + adc -= adc_target; + step_sign = -1; + } else { + adc = adc_target - adc; + step_sign = 1; + } + + if (adc < state->adc_diff) { + dprintk("CAPTRIM=%d is closer to target (%d/%d)\n", (u32) state->captrim, (u32) adc, (u32) state->adc_diff); + state->adc_diff = adc; + state->fcaptrim = state->captrim; + } + + state->captrim += step_sign * state->step; + if (state->step >= 1) + *tune_state = CT_TUNER_STEP_0; + else + *tune_state = CT_TUNER_STEP_2; + + ret = 25; + } + } else if (*tune_state == CT_TUNER_STEP_2) { /* this step is only used by krosus < P1G */ + /*write the final cptrim config */ + dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim); + + *tune_state = CT_TUNER_STEP_3; + + } else if (*tune_state == CT_TUNER_STEP_3) { + state->calibrate &= ~CAPTRIM_CAL; + *tune_state = CT_TUNER_STEP_0; + } + + return ret; +} + +static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tune_state *tune_state) +{ + int ret = 15; + s16 val; + + switch (*tune_state) { + case CT_TUNER_START: + state->wbdmux = dib0090_read_reg(state, 0x10); + dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3)); + + state->bias = dib0090_read_reg(state, 0x13); + dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8)); + + *tune_state = CT_TUNER_STEP_0; + /* wait for the WBDMUX to switch and for the ADC to sample */ + break; + + case CT_TUNER_STEP_0: + state->adc_diff = dib0090_get_slow_adc_val(state); + dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8)); + *tune_state = CT_TUNER_STEP_1; + break; + + case CT_TUNER_STEP_1: + val = dib0090_get_slow_adc_val(state); + state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55; + + dprintk("temperature: %d C\n", state->temperature - 30); + + *tune_state = CT_TUNER_STEP_2; + break; + + case CT_TUNER_STEP_2: + dib0090_write_reg(state, 0x13, state->bias); + dib0090_write_reg(state, 0x10, state->wbdmux); /* write back original WBDMUX */ + + *tune_state = CT_TUNER_START; + state->calibrate &= ~TEMP_CAL; + if (state->config->analog_output == 0) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); + + break; + + default: + ret = 0; + break; + } + return ret; +} + +#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ +static int dib0090_tune(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + const struct dib0090_tuning *tune = state->current_tune_table_index; + const struct dib0090_pll *pll = state->current_pll_table_index; + enum frontend_tune_state *tune_state = &state->tune_state; + + u16 lo5, lo6, Den, tmp; + u32 FBDiv, Rest, FREF, VCOF_kHz = 0; + int ret = 10; /* 1ms is the default delay most of the time */ + u8 c, i; + + /************************* VCO ***************************/ + /* Default values for FG */ + /* from these are needed : */ + /* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv */ + + /* in any case we first need to do a calibration if needed */ + if (*tune_state == CT_TUNER_START) { + /* deactivate DataTX before some calibrations */ + if (state->calibrate & (DC_CAL | TEMP_CAL | WBD_CAL)) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); + else + /* Activate DataTX in case a calibration has been done before */ + if (state->config->analog_output == 0) + dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); + } + + if (state->calibrate & DC_CAL) + return dib0090_dc_offset_calibration(state, tune_state); + else if (state->calibrate & WBD_CAL) { + if (state->current_rf == 0) + state->current_rf = state->fe->dtv_property_cache.frequency / 1000; + return dib0090_wbd_calibration(state, tune_state); + } else if (state->calibrate & TEMP_CAL) + return dib0090_get_temperature(state, tune_state); + else if (state->calibrate & CAPTRIM_CAL) + return dib0090_captrim_search(state, tune_state); + + if (*tune_state == CT_TUNER_START) { + /* if soc and AGC pwm control, disengage mux to be able to R/W access to 0x01 register to set the right filter (cutoff_freq_select) during the tune sequence, otherwise, SOC SERPAR error when accessing to 0x01 */ + if (state->config->use_pwm_agc && state->identity.in_soc) { + tmp = dib0090_read_reg(state, 0x39); + if ((tmp >> 10) & 0x1) + dib0090_write_reg(state, 0x39, tmp & ~(1 << 10)); + } + + state->current_band = (u8) BAND_OF_FREQUENCY(state->fe->dtv_property_cache.frequency / 1000); + state->rf_request = + state->fe->dtv_property_cache.frequency / 1000 + (state->current_band == + BAND_UHF ? state->config->freq_offset_khz_uhf : state->config-> + freq_offset_khz_vhf); + + /* in ISDB-T 1seg we shift tuning frequency */ + if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1 + && state->fe->dtv_property_cache.isdbt_partial_reception == 0)) { + const struct dib0090_low_if_offset_table *LUT_offset = state->config->low_if; + u8 found_offset = 0; + u32 margin_khz = 100; + + if (LUT_offset != NULL) { + while (LUT_offset->RF_freq != 0xffff) { + if (((state->rf_request > (LUT_offset->RF_freq - margin_khz)) + && (state->rf_request < (LUT_offset->RF_freq + margin_khz))) + && LUT_offset->std == state->fe->dtv_property_cache.delivery_system) { + state->rf_request += LUT_offset->offset_khz; + found_offset = 1; + break; + } + LUT_offset++; + } + } + + if (found_offset == 0) + state->rf_request += 400; + } + if (state->current_rf != state->rf_request || (state->current_standard != state->fe->dtv_property_cache.delivery_system)) { + state->tuner_is_tuned = 0; + state->current_rf = 0; + state->current_standard = 0; + + tune = dib0090_tuning_table; + if (state->identity.p1g) + tune = dib0090_p1g_tuning_table; + + tmp = (state->identity.version >> 5) & 0x7; + + if (state->identity.in_soc) { + if (state->config->force_cband_input) { /* Use the CBAND input for all band */ + if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF + || state->current_band & BAND_UHF) { + state->current_band = BAND_CBAND; + if (state->config->is_dib7090e) + tune = dib0090_tuning_table_cband_7090e_sensitivity; + else + tune = dib0090_tuning_table_cband_7090; + } + } else { /* Use the CBAND input for all band under UHF */ + if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF) { + state->current_band = BAND_CBAND; + if (state->config->is_dib7090e) + tune = dib0090_tuning_table_cband_7090e_sensitivity; + else + tune = dib0090_tuning_table_cband_7090; + } + } + } else + if (tmp == 0x4 || tmp == 0x7) { + /* CBAND tuner version for VHF */ + if (state->current_band == BAND_FM || state->current_band == BAND_CBAND || state->current_band == BAND_VHF) { + state->current_band = BAND_CBAND; /* Force CBAND */ + + tune = dib0090_tuning_table_fm_vhf_on_cband; + if (state->identity.p1g) + tune = dib0090_p1g_tuning_table_fm_vhf_on_cband; + } + } + + pll = dib0090_pll_table; + if (state->identity.p1g) + pll = dib0090_p1g_pll_table; + + /* Look for the interval */ + while (state->rf_request > tune->max_freq) + tune++; + while (state->rf_request > pll->max_freq) + pll++; + + state->current_tune_table_index = tune; + state->current_pll_table_index = pll; + + dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim)); + + VCOF_kHz = (pll->hfdiv * state->rf_request) * 2; + + FREF = state->config->io.clock_khz; + if (state->config->fref_clock_ratio != 0) + FREF /= state->config->fref_clock_ratio; + + FBDiv = (VCOF_kHz / pll->topresc / FREF); + Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; + + if (Rest < LPF) + Rest = 0; + else if (Rest < 2 * LPF) + Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { + Rest = 0; + FBDiv += 1; + } else if (Rest > (FREF - 2 * LPF)) + Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); + state->rest = Rest; + + /* external loop filter, otherwise: + * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4; + * lo6 = 0x0e34 */ + + if (Rest == 0) { + if (pll->vco_band) + lo5 = 0x049f; + else + lo5 = 0x041f; + } else { + if (pll->vco_band) + lo5 = 0x049e; + else if (state->config->analog_output) + lo5 = 0x041d; + else + lo5 = 0x041c; + } + + if (state->identity.p1g) { /* Bias is done automatically in P1G */ + if (state->identity.in_soc) { + if (state->identity.version == SOC_8090_P1G_11R1) + lo5 = 0x46f; + else + lo5 = 0x42f; + } else + lo5 = 0x42c; + } + + lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */ + + if (!state->config->io.pll_int_loop_filt) { + if (state->identity.in_soc) + lo6 = 0xff98; + else if (state->identity.p1g || (Rest == 0)) + lo6 = 0xfff8; + else + lo6 = 0xff28; + } else + lo6 = (state->config->io.pll_int_loop_filt << 3); + + Den = 1; + + if (Rest > 0) { + lo6 |= (1 << 2) | 2; + Den = 255; + } + dib0090_write_reg(state, 0x15, (u16) FBDiv); + if (state->config->fref_clock_ratio != 0) + dib0090_write_reg(state, 0x16, (Den << 8) | state->config->fref_clock_ratio); + else + dib0090_write_reg(state, 0x16, (Den << 8) | 1); + dib0090_write_reg(state, 0x17, (u16) Rest); + dib0090_write_reg(state, 0x19, lo5); + dib0090_write_reg(state, 0x1c, lo6); + + lo6 = tune->tuner_enable; + if (state->config->analog_output) + lo6 = (lo6 & 0xff9f) | 0x2; + + dib0090_write_reg(state, 0x24, lo6 | EN_LO | state->config->use_pwm_agc * EN_CRYSTAL); + + } + + state->current_rf = state->rf_request; + state->current_standard = state->fe->dtv_property_cache.delivery_system; + + ret = 20; + state->calibrate = CAPTRIM_CAL; /* captrim search now */ + } + + else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */ + const struct dib0090_wbd_slope *wbd = state->current_wbd_table; + + while (state->current_rf / 1000 > wbd->max_freq) + wbd++; + + dib0090_write_reg(state, 0x1e, 0x07ff); + dprintk("Final Captrim: %d\n", (u32) state->fcaptrim); + dprintk("HFDIV code: %d\n", (u32) pll->hfdiv_code); + dprintk("VCO = %d\n", (u32) pll->vco_band); + dprintk("VCOF in kHz: %d ((%d*%d) << 1))\n", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request); + dprintk("REFDIV: %d, FREF: %d\n", (u32) 1, (u32) state->config->io.clock_khz); + dprintk("FBDIV: %d, Rest: %d\n", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17)); + dprintk("Num: %d, Den: %d, SD: %d\n", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8), + (u32) dib0090_read_reg(state, 0x1c) & 0x3); + +#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ + c = 4; + i = 3; + + if (wbd->wbd_gain != 0) + c = wbd->wbd_gain; + + state->wbdmux = (c << 13) | (i << 11) | (WBD | (state->config->use_pwm_agc << 1)); + dib0090_write_reg(state, 0x10, state->wbdmux); + + if ((tune->tuner_enable == EN_CAB) && state->identity.p1g) { + dprintk("P1G : The cable band is selected and lna_tune = %d\n", tune->lna_tune); + dib0090_write_reg(state, 0x09, tune->lna_bias); + dib0090_write_reg(state, 0x0b, 0xb800 | (tune->lna_tune << 6) | (tune->switch_trim)); + } else + dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | tune->lna_bias); + + dib0090_write_reg(state, 0x0c, tune->v2i); + dib0090_write_reg(state, 0x0d, tune->mix); + dib0090_write_reg(state, 0x0e, tune->load); + *tune_state = CT_TUNER_STEP_1; + + } else if (*tune_state == CT_TUNER_STEP_1) { + /* initialize the lt gain register */ + state->rf_lt_def = 0x7c00; + + dib0090_set_bandwidth(state); + state->tuner_is_tuned = 1; + + state->calibrate |= WBD_CAL; + state->calibrate |= TEMP_CAL; + *tune_state = CT_TUNER_STOP; + } else + ret = FE_CALLBACK_TIME_NEVER; + return ret; +} + +static void dib0090_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + + return state->tune_state; +} + +EXPORT_SYMBOL(dib0090_get_tune_state); + +int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + struct dib0090_state *state = fe->tuner_priv; + + state->tune_state = tune_state; + return 0; +} + +EXPORT_SYMBOL(dib0090_set_tune_state); + +static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency) +{ + struct dib0090_state *state = fe->tuner_priv; + + *frequency = 1000 * state->current_rf; + return 0; +} + +static int dib0090_set_params(struct dvb_frontend *fe) +{ + struct dib0090_state *state = fe->tuner_priv; + u32 ret; + + state->tune_state = CT_TUNER_START; + + do { + ret = dib0090_tune(fe); + if (ret == FE_CALLBACK_TIME_NEVER) + break; + + /* + * Despite dib0090_tune returns time at a 0.1 ms range, + * the actual sleep time depends on CONFIG_HZ. The worse case + * is when CONFIG_HZ=100. In such case, the minimum granularity + * is 10ms. On some real field tests, the tuner sometimes don't + * lock when this timer is lower than 10ms. So, enforce a 10ms + * granularity and use usleep_range() instead of msleep(). + */ + ret = 10 * (ret + 99)/100; + usleep_range(ret * 1000, (ret + 1) * 1000); + } while (state->tune_state != CT_TUNER_STOP); + + return 0; +} + +static const struct dvb_tuner_ops dib0090_ops = { + .info = { + .name = "DiBcom DiB0090", + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, + }, + .release = dib0090_release, + + .init = dib0090_wakeup, + .sleep = dib0090_sleep, + .set_params = dib0090_set_params, + .get_frequency = dib0090_get_frequency, +}; + +static const struct dvb_tuner_ops dib0090_fw_ops = { + .info = { + .name = "DiBcom DiB0090", + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, + }, + .release = dib0090_release, + + .init = NULL, + .sleep = NULL, + .set_params = NULL, + .get_frequency = NULL, +}; + +static const struct dib0090_wbd_slope dib0090_wbd_table_default[] = { + {470, 0, 250, 0, 100, 4}, + {860, 51, 866, 21, 375, 4}, + {1700, 0, 800, 0, 850, 4}, + {2900, 0, 250, 0, 100, 6}, + {0xFFFF, 0, 0, 0, 0, 0}, +}; + +struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +{ + struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + st->config = config; + st->i2c = i2c; + st->fe = fe; + mutex_init(&st->i2c_buffer_lock); + fe->tuner_priv = st; + + if (config->wbd == NULL) + st->current_wbd_table = dib0090_wbd_table_default; + else + st->current_wbd_table = config->wbd; + + if (dib0090_reset(fe) != 0) + goto free_mem; + + pr_info("DiB0090: successfully identified\n"); + memcpy(&fe->ops.tuner_ops, &dib0090_ops, sizeof(struct dvb_tuner_ops)); + + return fe; + free_mem: + kfree(st); + fe->tuner_priv = NULL; + return NULL; +} + +EXPORT_SYMBOL_GPL(dib0090_register); + +struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +{ + struct dib0090_fw_state *st = kzalloc(sizeof(struct dib0090_fw_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + st->config = config; + st->i2c = i2c; + st->fe = fe; + mutex_init(&st->i2c_buffer_lock); + fe->tuner_priv = st; + + if (dib0090_fw_reset_digital(fe, st->config) != 0) + goto free_mem; + + dprintk("DiB0090 FW: successfully identified\n"); + memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops)); + + return fe; +free_mem: + kfree(st); + fe->tuner_priv = NULL; + return NULL; +} +EXPORT_SYMBOL_GPL(dib0090_fw_register); + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>"); +MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h new file mode 100644 index 0000000000..e5cb311694 --- /dev/null +++ b/drivers/media/dvb-frontends/dib0090.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner. + * + * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + */ +#ifndef DIB0090_H +#define DIB0090_H + +struct dvb_frontend; +struct i2c_adapter; + +#define DEFAULT_DIB0090_I2C_ADDRESS 0x60 + +struct dib0090_io_config { + u32 clock_khz; + + u8 pll_bypass:1; + u8 pll_range:1; + u8 pll_prediv:6; + u8 pll_loopdiv:6; + + u8 adc_clock_ratio; /* valid is 8, 7 ,6 */ + u16 pll_int_loop_filt; +}; + +struct dib0090_wbd_slope { + u16 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u16 slope_cold; + u16 offset_cold; + u16 slope_hot; + u16 offset_hot; + u8 wbd_gain; +}; + +struct dib0090_low_if_offset_table { + int std; + u32 RF_freq; + s32 offset_khz; +}; + +struct dib0090_config { + struct dib0090_io_config io; + int (*reset) (struct dvb_frontend *, int); + int (*sleep) (struct dvb_frontend *, int); + + /* offset in kHz */ + int freq_offset_khz_uhf; + int freq_offset_khz_vhf; + + int (*get_adc_power) (struct dvb_frontend *); + + u8 clkouttobamse:1; /* activate or deactivate clock output */ + u8 analog_output; + + u8 i2c_address; + /* add drives and other things if necessary */ + u16 wbd_vhf_offset; + u16 wbd_cband_offset; + u8 use_pwm_agc; + u8 clkoutdrive; + + u8 ls_cfg_pad_drv; + u8 data_tx_drv; + + u8 in_soc; + const struct dib0090_low_if_offset_table *low_if; + u8 fref_clock_ratio; + u16 force_cband_input; + struct dib0090_wbd_slope *wbd; + u8 is_dib7090e; + u8 force_crystal_mode; +}; + +#if IS_REACHABLE(CONFIG_DVB_TUNER_DIB0090) +extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); +extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); +extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast); +extern void dib0090_pwm_gain_reset(struct dvb_frontend *fe); +extern u16 dib0090_get_wbd_target(struct dvb_frontend *tuner); +extern u16 dib0090_get_wbd_offset(struct dvb_frontend *fe); +extern int dib0090_gain_control(struct dvb_frontend *fe); +extern enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe); +extern int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state); +extern void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt); +extern void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff); +extern int dib0090_set_switch(struct dvb_frontend *fe, u8 sw1, u8 sw2, u8 sw3); +extern int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff); +extern int dib0090_update_rframp_7090(struct dvb_frontend *fe, + u8 cfg_sensitivity); +extern int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, + u8 cfg_sensitivity); +#else +static inline struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0090_config *config) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} + +static inline void dib0090_pwm_gain_reset(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} + +static inline u16 dib0090_get_wbd_target(struct dvb_frontend *tuner) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; +} + +static inline int dib0090_gain_control(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return CT_DONE; +} + +static inline int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} + +static inline void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} + +static inline int dib0090_set_switch(struct dvb_frontend *fe, + u8 sw1, u8 sw2, u8 sw3) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib0090_update_rframp_7090(struct dvb_frontend *fe, + u8 cfg_sensitivity) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, + u8 cfg_sensitivity) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h new file mode 100644 index 0000000000..3f129efa21 --- /dev/null +++ b/drivers/media/dvb-frontends/dib3000.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * public header file of the frontend drivers for mobile DVB-T demodulators + * DiBcom 3000M-B and DiBcom 3000P/M-C (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom + * + * Acknowledgements + * + * Amaury Demol from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information + */ + +#ifndef DIB3000_H +#define DIB3000_H + +#include <linux/dvb/frontend.h> + +struct dib3000_config +{ + /* the demodulator's i2c address */ + u8 demod_address; +}; + +struct dib_fe_xfer_ops +{ + /* pid and transfer handling is done in the demodulator */ + int (*pid_parse)(struct dvb_frontend *fe, int onoff); + int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff); + int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff); + int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); +}; + +#if IS_REACHABLE(CONFIG_DVB_DIB3000MB) +extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); +#else +static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_DIB3000MB + +#endif // DIB3000_H diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c new file mode 100644 index 0000000000..c598b2a633 --- /dev/null +++ b/drivers/media/dvb-frontends/dib3000mb.c @@ -0,0 +1,818 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B + * DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom + * + * Acknowledgements + * + * Amaury Demol from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> + +#include "dib3000.h" +#include "dib3000mb_priv.h" + +/* Version information */ +#define DRIVER_VERSION "0.1" +#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator" +#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@posteo.de" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); + +#define deb_info(args...) dprintk(0x01, args) +#define deb_i2c(args...) dprintk(0x02, args) +#define deb_srch(args...) dprintk(0x04, args) +#define deb_info(args...) dprintk(0x01, args) +#define deb_xfer(args...) dprintk(0x02, args) +#define deb_setf(args...) dprintk(0x04, args) +#define deb_getf(args...) dprintk(0x08, args) + +static int dib3000_read_reg(struct dib3000_state *state, u16 reg) +{ + u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) + deb_i2c("i2c read error\n"); + + deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg, + (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]); + + return (rb[0] << 8) | rb[1]; +} + +static int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val) +{ + u8 b[] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg[] = { + { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 } + }; + deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val); + + return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; +} + +static int dib3000_search_status(u16 irq,u16 lock) +{ + if (irq & 0x02) { + if (lock & 0x01) { + deb_srch("auto search succeeded\n"); + return 1; // auto search succeeded + } else { + deb_srch("auto search not successful\n"); + return 0; // auto search failed + } + } else if (irq & 0x01) { + deb_srch("auto search failed\n"); + return 0; // auto search failed + } + return -1; // try again +} + +/* for auto search */ +static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ + { /* fft */ + { /* gua */ + { 0, 1 }, /* 0 0 { 0,1 } */ + { 3, 9 }, /* 0 1 { 0,1 } */ + }, + { + { 2, 5 }, /* 1 0 { 0,1 } */ + { 6, 11 }, /* 1 1 { 0,1 } */ + } + }; + +static int dib3000mb_get_frontend(struct dvb_frontend* fe, + struct dtv_frontend_properties *c); + +static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner) +{ + struct dib3000_state* state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + enum fe_code_rate fe_cr = FEC_NONE; + int search_state, seq; + + if (tuner && fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + + switch (c->bandwidth_hz) { + case 8000000: + wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]); + wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz); + break; + case 7000000: + wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[1]); + wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_7mhz); + break; + case 6000000: + wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[0]); + wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_6mhz); + break; + case 0: + return -EOPNOTSUPP; + default: + pr_err("unknown bandwidth value.\n"); + return -EINVAL; + } + deb_setf("bandwidth: %d MHZ\n", c->bandwidth_hz / 1000000); + } + wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4); + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + deb_setf("transmission mode: 2k\n"); + wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K); + break; + case TRANSMISSION_MODE_8K: + deb_setf("transmission mode: 8k\n"); + wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K); + break; + case TRANSMISSION_MODE_AUTO: + deb_setf("transmission mode: auto\n"); + break; + default: + return -EINVAL; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_32: + deb_setf("guard 1_32\n"); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32); + break; + case GUARD_INTERVAL_1_16: + deb_setf("guard 1_16\n"); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16); + break; + case GUARD_INTERVAL_1_8: + deb_setf("guard 1_8\n"); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8); + break; + case GUARD_INTERVAL_1_4: + deb_setf("guard 1_4\n"); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4); + break; + case GUARD_INTERVAL_AUTO: + deb_setf("guard auto\n"); + break; + default: + return -EINVAL; + } + + switch (c->inversion) { + case INVERSION_OFF: + deb_setf("inversion off\n"); + wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF); + break; + case INVERSION_AUTO: + deb_setf("inversion auto\n"); + break; + case INVERSION_ON: + deb_setf("inversion on\n"); + wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON); + break; + default: + return -EINVAL; + } + + switch (c->modulation) { + case QPSK: + deb_setf("modulation: qpsk\n"); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK); + break; + case QAM_16: + deb_setf("modulation: qam16\n"); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM); + break; + case QAM_64: + deb_setf("modulation: qam64\n"); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM); + break; + case QAM_AUTO: + break; + default: + return -EINVAL; + } + switch (c->hierarchy) { + case HIERARCHY_NONE: + deb_setf("hierarchy: none\n"); + fallthrough; + case HIERARCHY_1: + deb_setf("hierarchy: alpha=1\n"); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1); + break; + case HIERARCHY_2: + deb_setf("hierarchy: alpha=2\n"); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2); + break; + case HIERARCHY_4: + deb_setf("hierarchy: alpha=4\n"); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4); + break; + case HIERARCHY_AUTO: + deb_setf("hierarchy: alpha=auto\n"); + break; + default: + return -EINVAL; + } + + if (c->hierarchy == HIERARCHY_NONE) { + wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF); + wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP); + fe_cr = c->code_rate_HP; + } else if (c->hierarchy != HIERARCHY_AUTO) { + wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON); + wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP); + fe_cr = c->code_rate_LP; + } + switch (fe_cr) { + case FEC_1_2: + deb_setf("fec: 1_2\n"); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2); + break; + case FEC_2_3: + deb_setf("fec: 2_3\n"); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3); + break; + case FEC_3_4: + deb_setf("fec: 3_4\n"); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4); + break; + case FEC_5_6: + deb_setf("fec: 5_6\n"); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6); + break; + case FEC_7_8: + deb_setf("fec: 7_8\n"); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8); + break; + case FEC_NONE: + deb_setf("fec: none\n"); + break; + case FEC_AUTO: + deb_setf("fec: auto\n"); + break; + default: + return -EINVAL; + } + + seq = dib3000_seq + [c->transmission_mode == TRANSMISSION_MODE_AUTO] + [c->guard_interval == GUARD_INTERVAL_AUTO] + [c->inversion == INVERSION_AUTO]; + + deb_setf("seq? %d\n", seq); + + wr(DIB3000MB_REG_SEQ, seq); + + wr(DIB3000MB_REG_ISI, seq ? DIB3000MB_ISI_INHIBIT : DIB3000MB_ISI_ACTIVATE); + + if (c->transmission_mode == TRANSMISSION_MODE_2K) { + if (c->guard_interval == GUARD_INTERVAL_1_8) { + wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_2K_1_8); + } else { + wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_DEFAULT); + } + + wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_2K); + } else { + wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_DEFAULT); + } + + wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_OFF); + wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF); + wr(DIB3000MB_REG_MOBILE_MODE, DIB3000MB_MOBILE_MODE_OFF); + + wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_high); + + wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_ACTIVATE); + + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC + DIB3000MB_RESTART_CTRL); + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); + + /* wait for AGC lock */ + msleep(70); + + wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low); + + /* something has to be auto searched */ + if (c->modulation == QAM_AUTO || + c->hierarchy == HIERARCHY_AUTO || + fe_cr == FEC_AUTO || + c->inversion == INVERSION_AUTO) { + int as_count=0; + + deb_setf("autosearch enabled.\n"); + + wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT); + + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AUTO_SEARCH); + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); + + while ((search_state = + dib3000_search_status( + rd(DIB3000MB_REG_AS_IRQ_PENDING), + rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100) + msleep(1); + + deb_setf("search_state after autosearch %d after %d checks\n", + search_state, as_count); + + if (search_state == 1) { + if (dib3000mb_get_frontend(fe, c) == 0) { + deb_setf("reading tuning data from frontend succeeded.\n"); + return dib3000mb_set_frontend(fe, 0); + } + } + + } else { + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_CTRL); + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); + } + + return 0; +} + +static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) +{ + struct dib3000_state* state = fe->demodulator_priv; + + deb_info("dib3000mb is getting up.\n"); + wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_UP); + + wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC); + + wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE); + wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE_RST); + + wr(DIB3000MB_REG_CLOCK, DIB3000MB_CLOCK_DEFAULT); + + wr(DIB3000MB_REG_ELECT_OUT_MODE, DIB3000MB_ELECT_OUT_MODE_ON); + + wr(DIB3000MB_REG_DDS_FREQ_MSB, DIB3000MB_DDS_FREQ_MSB); + wr(DIB3000MB_REG_DDS_FREQ_LSB, DIB3000MB_DDS_FREQ_LSB); + + wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]); + + wr_foreach(dib3000mb_reg_impulse_noise, + dib3000mb_impulse_noise_values[DIB3000MB_IMPNOISE_OFF]); + + wr_foreach(dib3000mb_reg_agc_gain, dib3000mb_default_agc_gain); + + wr(DIB3000MB_REG_PHASE_NOISE, DIB3000MB_PHASE_NOISE_DEFAULT); + + wr_foreach(dib3000mb_reg_phase_noise, dib3000mb_default_noise_phase); + + wr_foreach(dib3000mb_reg_lock_duration, dib3000mb_default_lock_duration); + + wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low); + + wr(DIB3000MB_REG_LOCK0_MASK, DIB3000MB_LOCK0_DEFAULT); + wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4); + wr(DIB3000MB_REG_LOCK2_MASK, DIB3000MB_LOCK2_DEFAULT); + wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]); + + wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz); + + wr(DIB3000MB_REG_UNK_68, DIB3000MB_UNK_68); + wr(DIB3000MB_REG_UNK_69, DIB3000MB_UNK_69); + wr(DIB3000MB_REG_UNK_71, DIB3000MB_UNK_71); + wr(DIB3000MB_REG_UNK_77, DIB3000MB_UNK_77); + wr(DIB3000MB_REG_UNK_78, DIB3000MB_UNK_78); + wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT); + wr(DIB3000MB_REG_UNK_92, DIB3000MB_UNK_92); + wr(DIB3000MB_REG_UNK_96, DIB3000MB_UNK_96); + wr(DIB3000MB_REG_UNK_97, DIB3000MB_UNK_97); + wr(DIB3000MB_REG_UNK_106, DIB3000MB_UNK_106); + wr(DIB3000MB_REG_UNK_107, DIB3000MB_UNK_107); + wr(DIB3000MB_REG_UNK_108, DIB3000MB_UNK_108); + wr(DIB3000MB_REG_UNK_122, DIB3000MB_UNK_122); + wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF); + wr(DIB3000MB_REG_BERLEN, DIB3000MB_BERLEN_DEFAULT); + + wr_foreach(dib3000mb_reg_filter_coeffs, dib3000mb_filter_coeffs); + + wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_ON); + wr(DIB3000MB_REG_MULTI_DEMOD_MSB, DIB3000MB_MULTI_DEMOD_MSB); + wr(DIB3000MB_REG_MULTI_DEMOD_LSB, DIB3000MB_MULTI_DEMOD_LSB); + + wr(DIB3000MB_REG_OUTPUT_MODE, DIB3000MB_OUTPUT_MODE_SLAVE); + + wr(DIB3000MB_REG_FIFO_142, DIB3000MB_FIFO_142); + wr(DIB3000MB_REG_MPEG2_OUT_MODE, DIB3000MB_MPEG2_OUT_MODE_188); + wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE); + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); + wr(DIB3000MB_REG_FIFO_146, DIB3000MB_FIFO_146); + wr(DIB3000MB_REG_FIFO_147, DIB3000MB_FIFO_147); + + wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF); + + return 0; +} + +static int dib3000mb_get_frontend(struct dvb_frontend* fe, + struct dtv_frontend_properties *c) +{ + struct dib3000_state* state = fe->demodulator_priv; + enum fe_code_rate *cr; + u16 tps_val; + int inv_test1,inv_test2; + u32 dds_val, threshold = 0x800000; + + if (!rd(DIB3000MB_REG_TPS_LOCK)) + return 0; + + dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); + deb_getf("DDS_VAL: %x %x %x\n", dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB)); + if (dds_val < threshold) + inv_test1 = 0; + else if (dds_val == threshold) + inv_test1 = 1; + else + inv_test1 = 2; + + dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); + deb_getf("DDS_FREQ: %x %x %x\n", dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB)); + if (dds_val < threshold) + inv_test2 = 0; + else if (dds_val == threshold) + inv_test2 = 1; + else + inv_test2 = 2; + + c->inversion = + ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || + ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ? + INVERSION_ON : INVERSION_OFF; + + deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, c->inversion); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { + case DIB3000_CONSTELLATION_QPSK: + deb_getf("QPSK\n"); + c->modulation = QPSK; + break; + case DIB3000_CONSTELLATION_16QAM: + deb_getf("QAM16\n"); + c->modulation = QAM_16; + break; + case DIB3000_CONSTELLATION_64QAM: + deb_getf("QAM64\n"); + c->modulation = QAM_64; + break; + default: + pr_err("Unexpected constellation returned by TPS (%d)\n", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + if (rd(DIB3000MB_REG_TPS_HRCH)) { + deb_getf("HRCH ON\n"); + cr = &c->code_rate_LP; + c->code_rate_HP = FEC_NONE; + switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { + case DIB3000_ALPHA_0: + deb_getf("HIERARCHY_NONE\n"); + c->hierarchy = HIERARCHY_NONE; + break; + case DIB3000_ALPHA_1: + deb_getf("HIERARCHY_1\n"); + c->hierarchy = HIERARCHY_1; + break; + case DIB3000_ALPHA_2: + deb_getf("HIERARCHY_2\n"); + c->hierarchy = HIERARCHY_2; + break; + case DIB3000_ALPHA_4: + deb_getf("HIERARCHY_4\n"); + c->hierarchy = HIERARCHY_4; + break; + default: + pr_err("Unexpected ALPHA value returned by TPS (%d)\n", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); + } else { + deb_getf("HRCH OFF\n"); + cr = &c->code_rate_HP; + c->code_rate_LP = FEC_NONE; + c->hierarchy = HIERARCHY_NONE; + + tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); + } + + switch (tps_val) { + case DIB3000_FEC_1_2: + deb_getf("FEC_1_2\n"); + *cr = FEC_1_2; + break; + case DIB3000_FEC_2_3: + deb_getf("FEC_2_3\n"); + *cr = FEC_2_3; + break; + case DIB3000_FEC_3_4: + deb_getf("FEC_3_4\n"); + *cr = FEC_3_4; + break; + case DIB3000_FEC_5_6: + deb_getf("FEC_5_6\n"); + *cr = FEC_4_5; + break; + case DIB3000_FEC_7_8: + deb_getf("FEC_7_8\n"); + *cr = FEC_7_8; + break; + default: + pr_err("Unexpected FEC returned by TPS (%d)\n", tps_val); + break; + } + deb_getf("TPS: %d\n",tps_val); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { + case DIB3000_GUARD_TIME_1_32: + deb_getf("GUARD_INTERVAL_1_32\n"); + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case DIB3000_GUARD_TIME_1_16: + deb_getf("GUARD_INTERVAL_1_16\n"); + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case DIB3000_GUARD_TIME_1_8: + deb_getf("GUARD_INTERVAL_1_8\n"); + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case DIB3000_GUARD_TIME_1_4: + deb_getf("GUARD_INTERVAL_1_4\n"); + c->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + pr_err("Unexpected Guard Time returned by TPS (%d)\n", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { + case DIB3000_TRANSMISSION_MODE_2K: + deb_getf("TRANSMISSION_MODE_2K\n"); + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case DIB3000_TRANSMISSION_MODE_8K: + deb_getf("TRANSMISSION_MODE_8K\n"); + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + pr_err("unexpected transmission mode return by TPS (%d)\n", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + return 0; +} + +static int dib3000mb_read_status(struct dvb_frontend *fe, + enum fe_status *stat) +{ + struct dib3000_state* state = fe->demodulator_priv; + + *stat = 0; + + if (rd(DIB3000MB_REG_AGC_LOCK)) + *stat |= FE_HAS_SIGNAL; + if (rd(DIB3000MB_REG_CARRIER_LOCK)) + *stat |= FE_HAS_CARRIER; + if (rd(DIB3000MB_REG_VIT_LCK)) + *stat |= FE_HAS_VITERBI; + if (rd(DIB3000MB_REG_TS_SYNC_LOCK)) + *stat |= (FE_HAS_SYNC | FE_HAS_LOCK); + + deb_getf("actual status is %2x\n",*stat); + + deb_getf("autoval: tps: %d, qam: %d, hrch: %d, alpha: %d, hp: %d, lp: %d, guard: %d, fft: %d cell: %d\n", + rd(DIB3000MB_REG_TPS_LOCK), + rd(DIB3000MB_REG_TPS_QAM), + rd(DIB3000MB_REG_TPS_HRCH), + rd(DIB3000MB_REG_TPS_VIT_ALPHA), + rd(DIB3000MB_REG_TPS_CODE_RATE_HP), + rd(DIB3000MB_REG_TPS_CODE_RATE_LP), + rd(DIB3000MB_REG_TPS_GUARD_TIME), + rd(DIB3000MB_REG_TPS_FFT), + rd(DIB3000MB_REG_TPS_CELL_ID)); + + //*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; +} + +static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct dib3000_state* state = fe->demodulator_priv; + + *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB)); + return 0; +} + +/* see dib3000-watch dvb-apps for exact calcuations of signal_strength and snr */ +static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct dib3000_state* state = fe->demodulator_priv; + + *strength = rd(DIB3000MB_REG_SIGNAL_POWER) * 0xffff / 0x170; + return 0; +} + +static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct dib3000_state* state = fe->demodulator_priv; + short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER); + int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) | + rd(DIB3000MB_REG_NOISE_POWER_LSB); + *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1); + return 0; +} + +static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct dib3000_state* state = fe->demodulator_priv; + + *unc = rd(DIB3000MB_REG_PACKET_ERROR_RATE); + return 0; +} + +static int dib3000mb_sleep(struct dvb_frontend* fe) +{ + struct dib3000_state* state = fe->demodulator_priv; + deb_info("dib3000mb is going to bed.\n"); + wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_DOWN); + return 0; +} + +static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe) +{ + return dib3000mb_fe_init(fe, 0); +} + +static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend *fe) +{ + return dib3000mb_set_frontend(fe, 1); +} + +static void dib3000mb_release(struct dvb_frontend* fe) +{ + struct dib3000_state *state = fe->demodulator_priv; + kfree(state); +} + +/* pid filter and transfer stuff */ +static int dib3000mb_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff) +{ + struct dib3000_state *state = fe->demodulator_priv; + pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); + wr(index+DIB3000MB_REG_FIRST_PID,pid); + return 0; +} + +static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff) +{ + struct dib3000_state *state = fe->demodulator_priv; + + deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling"); + if (onoff) { + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE); + } else { + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); + } + return 0; +} + +static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff) +{ + struct dib3000_state *state = fe->demodulator_priv; + deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling"); + wr(DIB3000MB_REG_PID_PARSE,onoff); + return 0; +} + +static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr) +{ + struct dib3000_state *state = fe->demodulator_priv; + if (onoff) { + wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr)); + } else { + wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr)); + } + return 0; +} + +static const struct dvb_frontend_ops dib3000mb_ops; + +struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +{ + struct dib3000_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct dib3000_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config,config,sizeof(struct dib3000_config)); + + /* check for the correct demod */ + if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) + goto error; + + if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* set the xfer operations */ + xfer_ops->pid_parse = dib3000mb_pid_parse; + xfer_ops->fifo_ctrl = dib3000mb_fifo_control; + xfer_ops->pid_ctrl = dib3000mb_pid_control; + xfer_ops->tuner_pass_ctrl = dib3000mb_tuner_pass_ctrl; + + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops dib3000mb_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "DiBcom 3000M-B DVB-T", + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib3000mb_release, + + .init = dib3000mb_fe_init_nonmobile, + .sleep = dib3000mb_sleep, + + .set_frontend = dib3000mb_set_frontend_and_tuner, + .get_frontend = dib3000mb_get_frontend, + .get_tune_settings = dib3000mb_fe_get_tune_settings, + + .read_status = dib3000mb_read_status, + .read_ber = dib3000mb_read_ber, + .read_signal_strength = dib3000mb_read_signal_strength, + .read_snr = dib3000mb_read_snr, + .read_ucblocks = dib3000mb_read_unc_blocks, +}; + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(dib3000mb_attach); diff --git a/drivers/media/dvb-frontends/dib3000mb_priv.h b/drivers/media/dvb-frontends/dib3000mb_priv.h new file mode 100644 index 0000000000..2503966fb8 --- /dev/null +++ b/drivers/media/dvb-frontends/dib3000mb_priv.h @@ -0,0 +1,551 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * dib3000mb_priv.h + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@posteo.de) + * + * for more information see dib3000mb.c . + */ + +#ifndef __DIB3000MB_PRIV_H_INCLUDED__ +#define __DIB3000MB_PRIV_H_INCLUDED__ + +/* handy shortcuts */ +#define rd(reg) dib3000_read_reg(state,reg) + +#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \ + { pr_err("while sending 0x%04x to 0x%04x.", val, reg); return -EREMOTEIO; } + +#define wr_foreach(a,v) { int i; \ + if (sizeof(a) != sizeof(v)) \ + pr_err("sizeof: %zu %zu is different", sizeof(a), sizeof(v));\ + for (i=0; i < sizeof(a)/sizeof(u16); i++) \ + wr(a[i],v[i]); \ + } + +#define set_or(reg,val) wr(reg,rd(reg) | val) + +#define set_and(reg,val) wr(reg,rd(reg) & val) + +/* debug */ + +#define dprintk(level, fmt, arg...) do { \ + if (debug & level) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +/* mask for enabling a specific pid for the pid_filter */ +#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) + +/* common values for tuning */ +#define DIB3000_ALPHA_0 ( 0) +#define DIB3000_ALPHA_1 ( 1) +#define DIB3000_ALPHA_2 ( 2) +#define DIB3000_ALPHA_4 ( 4) + +#define DIB3000_CONSTELLATION_QPSK ( 0) +#define DIB3000_CONSTELLATION_16QAM ( 1) +#define DIB3000_CONSTELLATION_64QAM ( 2) + +#define DIB3000_GUARD_TIME_1_32 ( 0) +#define DIB3000_GUARD_TIME_1_16 ( 1) +#define DIB3000_GUARD_TIME_1_8 ( 2) +#define DIB3000_GUARD_TIME_1_4 ( 3) + +#define DIB3000_TRANSMISSION_MODE_2K ( 0) +#define DIB3000_TRANSMISSION_MODE_8K ( 1) + +#define DIB3000_SELECT_LP ( 0) +#define DIB3000_SELECT_HP ( 1) + +#define DIB3000_FEC_1_2 ( 1) +#define DIB3000_FEC_2_3 ( 2) +#define DIB3000_FEC_3_4 ( 3) +#define DIB3000_FEC_5_6 ( 5) +#define DIB3000_FEC_7_8 ( 7) + +#define DIB3000_HRCH_OFF ( 0) +#define DIB3000_HRCH_ON ( 1) + +#define DIB3000_DDS_INVERSION_OFF ( 0) +#define DIB3000_DDS_INVERSION_ON ( 1) + +#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8)) +#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7))) + +#define DIB3000_REG_MANUFACTOR_ID ( 1025) +#define DIB3000_I2C_ID_DIBCOM (0x01b3) + +#define DIB3000_REG_DEVICE_ID ( 1026) +#define DIB3000MB_DEVICE_ID (0x3000) +#define DIB3000MC_DEVICE_ID (0x3001) +#define DIB3000P_DEVICE_ID (0x3002) + +/* frontend state */ +struct dib3000_state { + struct i2c_adapter* i2c; + +/* configuration settings */ + struct dib3000_config config; + + struct dvb_frontend frontend; + int timing_offset; + int timing_offset_comp_done; + + u32 last_tuned_bw; + u32 last_tuned_freq; +}; + +/* register addresses and some of their default values */ + +/* restart subsystems */ +#define DIB3000MB_REG_RESTART ( 0) + +#define DIB3000MB_RESTART_OFF ( 0) +#define DIB3000MB_RESTART_AUTO_SEARCH (1 << 1) +#define DIB3000MB_RESTART_CTRL (1 << 2) +#define DIB3000MB_RESTART_AGC (1 << 3) + +/* FFT size */ +#define DIB3000MB_REG_FFT ( 1) + +/* Guard time */ +#define DIB3000MB_REG_GUARD_TIME ( 2) + +/* QAM */ +#define DIB3000MB_REG_QAM ( 3) + +/* Alpha coefficient high priority Viterbi algorithm */ +#define DIB3000MB_REG_VIT_ALPHA ( 4) + +/* spectrum inversion */ +#define DIB3000MB_REG_DDS_INV ( 5) + +/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */ +#define DIB3000MB_REG_DDS_FREQ_MSB ( 6) +#define DIB3000MB_REG_DDS_FREQ_LSB ( 7) +#define DIB3000MB_DDS_FREQ_MSB ( 178) +#define DIB3000MB_DDS_FREQ_LSB ( 8990) + +/* timing frequency (carrier spacing) */ +static u16 dib3000mb_reg_timing_freq[] = { 8,9 }; +static u16 dib3000mb_timing_freq[][2] = { + { 126 , 48873 }, /* 6 MHz */ + { 147 , 57019 }, /* 7 MHz */ + { 168 , 65164 }, /* 8 MHz */ +}; + +/* impulse noise parameter */ +/* 36 ??? */ + +static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 }; + +enum dib3000mb_impulse_noise_type { + DIB3000MB_IMPNOISE_OFF, + DIB3000MB_IMPNOISE_MOBILE, + DIB3000MB_IMPNOISE_FIXED, + DIB3000MB_IMPNOISE_DEFAULT +}; + +static u16 dib3000mb_impulse_noise_values[][5] = { + { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */ + { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */ + { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */ + { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */ +}; + +/* + * Dual Automatic-Gain-Control + * - gains RF in tuner (AGC1) + * - gains IF after filtering (AGC2) + */ + +/* also from 16 to 18 */ +static u16 dib3000mb_reg_agc_gain[] = { + 19,20,21,22,23,24,25,26,27,28,29,30,31,32 +}; + +static u16 dib3000mb_default_agc_gain[] = + { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */ + 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */ + +/* phase noise */ +/* 36 is set when setting the impulse noise */ +static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 }; + +static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 }; + +/* lock duration */ +static u16 dib3000mb_reg_lock_duration[] = { 39,40 }; +static u16 dib3000mb_default_lock_duration[] = { 135, 135 }; + +/* AGC loop bandwidth */ +static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 }; + +static u16 dib3000mb_agc_bandwidth_low[] = + { 2088, 10, 2088, 10, 3448, 5, 3448, 5 }; +static u16 dib3000mb_agc_bandwidth_high[] = + { 2349, 5, 2349, 5, 2586, 2, 2586, 2 }; + +/* + * lock0 definition (coff_lock) + */ +#define DIB3000MB_REG_LOCK0_MASK ( 51) +#define DIB3000MB_LOCK0_DEFAULT ( 4) + +/* + * lock1 definition (cpil_lock) + * for auto search + * which values hide behind the lock masks + */ +#define DIB3000MB_REG_LOCK1_MASK ( 52) +#define DIB3000MB_LOCK1_SEARCH_4 (0x0004) +#define DIB3000MB_LOCK1_SEARCH_2048 (0x0800) +#define DIB3000MB_LOCK1_DEFAULT (0x0001) + +/* + * lock2 definition (fec_lock) */ +#define DIB3000MB_REG_LOCK2_MASK ( 53) +#define DIB3000MB_LOCK2_DEFAULT (0x0080) + +/* + * SEQ ? what was that again ... :) + * changes when, inversion, guard time and fft is + * either automatically detected or not + */ +#define DIB3000MB_REG_SEQ ( 54) + +/* bandwidth */ +static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; +static u16 dib3000mb_bandwidth_6mhz[] = + { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 }; + +static u16 dib3000mb_bandwidth_7mhz[] = + { 0, 28, 64421, 96, 39973, 483, 3255, 0, 1000, 0, 1010, 1, 45264 }; + +static u16 dib3000mb_bandwidth_8mhz[] = + { 0, 25, 23600, 84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 }; + +#define DIB3000MB_REG_UNK_68 ( 68) +#define DIB3000MB_UNK_68 ( 0) + +#define DIB3000MB_REG_UNK_69 ( 69) +#define DIB3000MB_UNK_69 ( 0) + +#define DIB3000MB_REG_UNK_71 ( 71) +#define DIB3000MB_UNK_71 ( 0) + +#define DIB3000MB_REG_UNK_77 ( 77) +#define DIB3000MB_UNK_77 ( 6) + +#define DIB3000MB_REG_UNK_78 ( 78) +#define DIB3000MB_UNK_78 (0x0080) + +/* isi */ +#define DIB3000MB_REG_ISI ( 79) +#define DIB3000MB_ISI_ACTIVATE ( 0) +#define DIB3000MB_ISI_INHIBIT ( 1) + +/* sync impovement */ +#define DIB3000MB_REG_SYNC_IMPROVEMENT ( 84) +#define DIB3000MB_SYNC_IMPROVE_2K_1_8 ( 3) +#define DIB3000MB_SYNC_IMPROVE_DEFAULT ( 0) + +/* phase noise compensation inhibition */ +#define DIB3000MB_REG_PHASE_NOISE ( 87) +#define DIB3000MB_PHASE_NOISE_DEFAULT ( 0) + +#define DIB3000MB_REG_UNK_92 ( 92) +#define DIB3000MB_UNK_92 (0x0080) + +#define DIB3000MB_REG_UNK_96 ( 96) +#define DIB3000MB_UNK_96 (0x0010) + +#define DIB3000MB_REG_UNK_97 ( 97) +#define DIB3000MB_UNK_97 (0x0009) + +/* mobile mode ??? */ +#define DIB3000MB_REG_MOBILE_MODE ( 101) +#define DIB3000MB_MOBILE_MODE_ON ( 1) +#define DIB3000MB_MOBILE_MODE_OFF ( 0) + +#define DIB3000MB_REG_UNK_106 ( 106) +#define DIB3000MB_UNK_106 (0x0080) + +#define DIB3000MB_REG_UNK_107 ( 107) +#define DIB3000MB_UNK_107 (0x0080) + +#define DIB3000MB_REG_UNK_108 ( 108) +#define DIB3000MB_UNK_108 (0x0080) + +/* fft */ +#define DIB3000MB_REG_UNK_121 ( 121) +#define DIB3000MB_UNK_121_2K ( 7) +#define DIB3000MB_UNK_121_DEFAULT ( 5) + +#define DIB3000MB_REG_UNK_122 ( 122) +#define DIB3000MB_UNK_122 ( 2867) + +/* QAM for mobile mode */ +#define DIB3000MB_REG_MOBILE_MODE_QAM ( 126) +#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) +#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1) +#define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0) + +/* + * data diversity when having more than one chip on-board + * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY + */ +#define DIB3000MB_REG_DATA_IN_DIVERSITY ( 127) +#define DIB3000MB_DATA_DIVERSITY_IN_OFF ( 0) +#define DIB3000MB_DATA_DIVERSITY_IN_ON ( 2) + +/* vit hrch */ +#define DIB3000MB_REG_VIT_HRCH ( 128) + +/* vit code rate */ +#define DIB3000MB_REG_VIT_CODE_RATE ( 129) + +/* vit select hp */ +#define DIB3000MB_REG_VIT_HP ( 130) + +/* time frame for Bit-Error-Rate calculation */ +#define DIB3000MB_REG_BERLEN ( 135) +#define DIB3000MB_BERLEN_LONG ( 0) +#define DIB3000MB_BERLEN_DEFAULT ( 1) +#define DIB3000MB_BERLEN_MEDIUM ( 2) +#define DIB3000MB_BERLEN_SHORT ( 3) + +/* 142 - 152 FIFO parameters + * which is what ? + */ + +#define DIB3000MB_REG_FIFO_142 ( 142) +#define DIB3000MB_FIFO_142 ( 0) + +/* MPEG2 TS output mode */ +#define DIB3000MB_REG_MPEG2_OUT_MODE ( 143) +#define DIB3000MB_MPEG2_OUT_MODE_204 ( 0) +#define DIB3000MB_MPEG2_OUT_MODE_188 ( 1) + +#define DIB3000MB_REG_PID_PARSE ( 144) +#define DIB3000MB_PID_PARSE_INHIBIT ( 0) +#define DIB3000MB_PID_PARSE_ACTIVATE ( 1) + +#define DIB3000MB_REG_FIFO ( 145) +#define DIB3000MB_FIFO_INHIBIT ( 1) +#define DIB3000MB_FIFO_ACTIVATE ( 0) + +#define DIB3000MB_REG_FIFO_146 ( 146) +#define DIB3000MB_FIFO_146 ( 3) + +#define DIB3000MB_REG_FIFO_147 ( 147) +#define DIB3000MB_FIFO_147 (0x0100) + +/* + * pidfilter + * it is not a hardware pidfilter but a filter which drops all pids + * except the ones set. Necessary because of the limited USB1.1 bandwidth. + * regs 153-168 + */ + +#define DIB3000MB_REG_FIRST_PID ( 153) +#define DIB3000MB_NUM_PIDS ( 16) + +/* + * output mode + * USB devices have to use 'slave'-mode + * see also DIB3000MB_REG_ELECT_OUT_MODE + */ +#define DIB3000MB_REG_OUTPUT_MODE ( 169) +#define DIB3000MB_OUTPUT_MODE_GATED_CLK ( 0) +#define DIB3000MB_OUTPUT_MODE_CONT_CLK ( 1) +#define DIB3000MB_OUTPUT_MODE_SERIAL ( 2) +#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ( 5) +#define DIB3000MB_OUTPUT_MODE_SLAVE ( 6) + +/* irq event mask */ +#define DIB3000MB_REG_IRQ_EVENT_MASK ( 170) +#define DIB3000MB_IRQ_EVENT_MASK ( 0) + +/* filter coefficients */ +static u16 dib3000mb_reg_filter_coeffs[] = { + 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, + 188, 189, 190, 191, 192, 194 +}; + +static u16 dib3000mb_filter_coeffs[] = { + 226, 160, 29, + 979, 998, 19, + 22, 1019, 1006, + 1022, 12, 6, + 1017, 1017, 3, + 6, 1019, + 1021, 2, 3, + 1, 0, +}; + +/* + * mobile algorithm (when you are moving with your device) + * but not faster than 90 km/h + */ +#define DIB3000MB_REG_MOBILE_ALGO ( 195) +#define DIB3000MB_MOBILE_ALGO_ON ( 0) +#define DIB3000MB_MOBILE_ALGO_OFF ( 1) + +/* multiple demodulators algorithm */ +#define DIB3000MB_REG_MULTI_DEMOD_MSB ( 206) +#define DIB3000MB_REG_MULTI_DEMOD_LSB ( 207) + +/* terminator, no more demods */ +#define DIB3000MB_MULTI_DEMOD_MSB ( 32767) +#define DIB3000MB_MULTI_DEMOD_LSB ( 4095) + +/* bring the device into a known */ +#define DIB3000MB_REG_RESET_DEVICE ( 1024) +#define DIB3000MB_RESET_DEVICE (0x812c) +#define DIB3000MB_RESET_DEVICE_RST ( 0) + +/* hardware clock configuration */ +#define DIB3000MB_REG_CLOCK ( 1027) +#define DIB3000MB_CLOCK_DEFAULT (0x9000) +#define DIB3000MB_CLOCK_DIVERSITY (0x92b0) + +/* power down config */ +#define DIB3000MB_REG_POWER_CONTROL ( 1028) +#define DIB3000MB_POWER_DOWN ( 1) +#define DIB3000MB_POWER_UP ( 0) + +/* electrical output mode */ +#define DIB3000MB_REG_ELECT_OUT_MODE ( 1029) +#define DIB3000MB_ELECT_OUT_MODE_OFF ( 0) +#define DIB3000MB_ELECT_OUT_MODE_ON ( 1) + +/* set the tuner i2c address */ +#define DIB3000MB_REG_TUNER ( 1089) + +/* monitoring registers (read only) */ + +/* agc loop locked (size: 1) */ +#define DIB3000MB_REG_AGC_LOCK ( 324) + +/* agc power (size: 16) */ +#define DIB3000MB_REG_AGC_POWER ( 325) + +/* agc1 value (16) */ +#define DIB3000MB_REG_AGC1_VALUE ( 326) + +/* agc2 value (16) */ +#define DIB3000MB_REG_AGC2_VALUE ( 327) + +/* total RF power (16), can be used for signal strength */ +#define DIB3000MB_REG_RF_POWER ( 328) + +/* dds_frequency with offset (24) */ +#define DIB3000MB_REG_DDS_VALUE_MSB ( 339) +#define DIB3000MB_REG_DDS_VALUE_LSB ( 340) + +/* timing offset signed (24) */ +#define DIB3000MB_REG_TIMING_OFFSET_MSB ( 341) +#define DIB3000MB_REG_TIMING_OFFSET_LSB ( 342) + +/* fft start position (13) */ +#define DIB3000MB_REG_FFT_WINDOW_POS ( 353) + +/* carriers locked (1) */ +#define DIB3000MB_REG_CARRIER_LOCK ( 355) + +/* noise power (24) */ +#define DIB3000MB_REG_NOISE_POWER_MSB ( 372) +#define DIB3000MB_REG_NOISE_POWER_LSB ( 373) + +#define DIB3000MB_REG_MOBILE_NOISE_MSB ( 374) +#define DIB3000MB_REG_MOBILE_NOISE_LSB ( 375) + +/* + * signal power (16), this and the above can be + * used to calculate the signal/noise - ratio + */ +#define DIB3000MB_REG_SIGNAL_POWER ( 380) + +/* mer (24) */ +#define DIB3000MB_REG_MER_MSB ( 381) +#define DIB3000MB_REG_MER_LSB ( 382) + +/* + * Transmission Parameter Signalling (TPS) + * the following registers can be used to get TPS-information. + * The values are according to the DVB-T standard. + */ + +/* TPS locked (1) */ +#define DIB3000MB_REG_TPS_LOCK ( 394) + +/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */ +#define DIB3000MB_REG_TPS_QAM ( 398) + +/* hierarchy from TPS (1) */ +#define DIB3000MB_REG_TPS_HRCH ( 399) + +/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */ +#define DIB3000MB_REG_TPS_VIT_ALPHA ( 400) + +/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */ +#define DIB3000MB_REG_TPS_CODE_RATE_HP ( 401) + +/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */ +#define DIB3000MB_REG_TPS_CODE_RATE_LP ( 402) + +/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */ +#define DIB3000MB_REG_TPS_GUARD_TIME ( 403) + +/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */ +#define DIB3000MB_REG_TPS_FFT ( 404) + +/* cell id from TPS (16) */ +#define DIB3000MB_REG_TPS_CELL_ID ( 406) + +/* TPS (68) */ +#define DIB3000MB_REG_TPS_1 ( 408) +#define DIB3000MB_REG_TPS_2 ( 409) +#define DIB3000MB_REG_TPS_3 ( 410) +#define DIB3000MB_REG_TPS_4 ( 411) +#define DIB3000MB_REG_TPS_5 ( 412) + +/* bit error rate (before RS correction) (21) */ +#define DIB3000MB_REG_BER_MSB ( 414) +#define DIB3000MB_REG_BER_LSB ( 415) + +/* packet error rate (uncorrected TS packets) (16) */ +#define DIB3000MB_REG_PACKET_ERROR_RATE ( 417) + +/* uncorrected packet count (16) */ +#define DIB3000MB_REG_UNC ( 420) + +/* viterbi locked (1) */ +#define DIB3000MB_REG_VIT_LCK ( 421) + +/* viterbi inidcator (16) */ +#define DIB3000MB_REG_VIT_INDICATOR ( 422) + +/* transport stream sync lock (1) */ +#define DIB3000MB_REG_TS_SYNC_LOCK ( 423) + +/* transport stream RS lock (1) */ +#define DIB3000MB_REG_TS_RS_LOCK ( 424) + +/* lock mask 0 value (1) */ +#define DIB3000MB_REG_LOCK0_VALUE ( 425) + +/* lock mask 1 value (1) */ +#define DIB3000MB_REG_LOCK1_VALUE ( 426) + +/* lock mask 2 value (1) */ +#define DIB3000MB_REG_LOCK2_VALUE ( 427) + +/* interrupt pending for auto search */ +#define DIB3000MB_REG_AS_IRQ_PENDING ( 434) + +#endif diff --git a/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c new file mode 100644 index 0000000000..c2fca8289a --- /dev/null +++ b/drivers/media/dvb-frontends/dib3000mc.c @@ -0,0 +1,975 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for DiBcom DiB3000MC/P-demodulator. + * + * Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/) + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) + * + * This code is partially based on the previous dib3000mc.c . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> + +#include <media/dvb_frontend.h> + +#include "dib3000mc.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +static int buggy_sfn_workaround; +module_param(buggy_sfn_workaround, int, 0644); +MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +struct dib3000mc_state { + struct dvb_frontend demod; + struct dib3000mc_config *cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + + u32 timf; + + u32 current_bandwidth; + + u16 dev_id; + + u8 sfn_workaround_active :1; +}; + +static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg) +{ + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr >> 1, .flags = 0, .len = 2 }, + { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .len = 2 }, + }; + u16 word; + u8 *b; + + b = kmalloc(4, GFP_KERNEL); + if (!b) + return 0; + + b[0] = (reg >> 8) | 0x80; + b[1] = reg; + b[2] = 0; + b[3] = 0; + + msg[0].buf = b; + msg[1].buf = b + 2; + + if (i2c_transfer(state->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d\n",reg); + + word = (b[2] << 8) | b[3]; + kfree(b); + + return word; +} + +static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val) +{ + struct i2c_msg msg = { + .addr = state->i2c_addr >> 1, .flags = 0, .len = 4 + }; + int rc; + u8 *b; + + b = kmalloc(4, GFP_KERNEL); + if (!b) + return -ENOMEM; + + b[0] = reg >> 8; + b[1] = reg; + b[2] = val >> 8; + b[3] = val; + + msg.buf = b; + + rc = i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; + kfree(b); + + return rc; +} + +static int dib3000mc_identify(struct dib3000mc_state *state) +{ + u16 value; + if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) { + dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value); + return -EREMOTEIO; + } + + value = dib3000mc_read_word(state, 1026); + if (value != 0x3001 && value != 0x3002) { + dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value); + return -EREMOTEIO; + } + state->dev_id = value; + + dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id); + + return 0; +} + +static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset) +{ + u32 timf; + + if (state->timf == 0) { + timf = 1384402; // default value for 8MHz + if (update_offset) + msleep(200); // first time we do an update + } else + timf = state->timf; + + timf *= (bw / 1000); + + if (update_offset) { + s16 tim_offs = dib3000mc_read_word(state, 416); + + if (tim_offs & 0x2000) + tim_offs -= 0x4000; + + if (nfft == TRANSMISSION_MODE_2K) + tim_offs *= 4; + + timf += tim_offs; + state->timf = timf / (bw / 1000); + } + + dprintk("timf: %d\n", timf); + + dib3000mc_write_word(state, 23, (u16) (timf >> 16)); + dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff); + + return 0; +} + +static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state) +{ + u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb; + if (state->cfg->pwm3_inversion) { + reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0); + reg_52 |= (1 << 2); + } else { + reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0); + reg_52 |= (1 << 8); + } + dib3000mc_write_word(state, 51, reg_51); + dib3000mc_write_word(state, 52, reg_52); + + if (state->cfg->use_pwm3) + dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0)); + else + dib3000mc_write_word(state, 245, 0); + + dib3000mc_write_word(state, 1040, 0x3); + return 0; +} + +static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode) +{ + int ret = 0; + u16 fifo_threshold = 1792; + u16 outreg = 0; + u16 outmode = 0; + u16 elecout = 1; + u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */ + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->demod, mode); + + switch (mode) { + case OUTMODE_HIGH_Z: // disable + elecout = 0; + break; + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outmode = 0; + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outmode = 1; + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outmode = 2; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + elecout = 3; + /*ADDR @ 206 : + P_smo_error_discard [1;6:6] = 0 + P_smo_rs_discard [1;5:5] = 0 + P_smo_pid_parse [1;4:4] = 0 + P_smo_fifo_flush [1;3:3] = 0 + P_smo_mode [2;2:1] = 11 + P_smo_ovf_prot [1;0:0] = 0 + */ + smo_reg |= 3 << 1; + fifo_threshold = 512; + outmode = 5; + break; + case OUTMODE_DIVERSITY: + outmode = 4; + elecout = 1; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); + outmode = 0; + break; + } + + if ((state->cfg->output_mpeg2_in_188_bytes)) + smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1 + + outreg = dib3000mc_read_word(state, 244) & 0x07FF; + outreg |= (outmode << 11); + ret |= dib3000mc_write_word(state, 244, outreg); + ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/ + ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */ + ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */ + return ret; +} + +static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw) +{ + u16 bw_cfg[6] = { 0 }; + u16 imp_bw_cfg[3] = { 0 }; + u16 reg; + +/* settings here are for 27.7MHz */ + switch (bw) { + case 8000: + bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20; + imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7; + break; + + case 7000: + bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7; + imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0; + break; + + case 6000: + bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5; + imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089; + break; + + case 5000: + bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500; + imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072; + break; + + default: return -EINVAL; + } + + for (reg = 6; reg < 12; reg++) + dib3000mc_write_word(state, reg, bw_cfg[reg - 6]); + dib3000mc_write_word(state, 12, 0x0000); + dib3000mc_write_word(state, 13, 0x03e8); + dib3000mc_write_word(state, 14, 0x0000); + dib3000mc_write_word(state, 15, 0x03f2); + dib3000mc_write_word(state, 16, 0x0001); + dib3000mc_write_word(state, 17, 0xb0d0); + // P_sec_len + dib3000mc_write_word(state, 18, 0x0393); + dib3000mc_write_word(state, 19, 0x8700); + + for (reg = 55; reg < 58; reg++) + dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]); + + // Timing configuration + dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0); + + return 0; +} + +static u16 impulse_noise_val[29] = + +{ + 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3, + 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2, + 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd +}; + +static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft) +{ + u16 i; + for (i = 58; i < 87; i++) + dib3000mc_write_word(state, i, impulse_noise_val[i-58]); + + if (nfft == TRANSMISSION_MODE_8K) { + dib3000mc_write_word(state, 58, 0x3b); + dib3000mc_write_word(state, 84, 0x00); + dib3000mc_write_word(state, 85, 0x8200); + } + + dib3000mc_write_word(state, 34, 0x1294); + dib3000mc_write_word(state, 35, 0x1ff8); + if (mode == 1) + dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10)); +} + +static int dib3000mc_init(struct dvb_frontend *demod) +{ + struct dib3000mc_state *state = demod->demodulator_priv; + struct dibx000_agc_config *agc = state->cfg->agc; + + // Restart Configuration + dib3000mc_write_word(state, 1027, 0x8000); + dib3000mc_write_word(state, 1027, 0x0000); + + // power up the demod + mobility configuration + dib3000mc_write_word(state, 140, 0x0000); + dib3000mc_write_word(state, 1031, 0); + + if (state->cfg->mobile_mode) { + dib3000mc_write_word(state, 139, 0x0000); + dib3000mc_write_word(state, 141, 0x0000); + dib3000mc_write_word(state, 175, 0x0002); + dib3000mc_write_word(state, 1032, 0x0000); + } else { + dib3000mc_write_word(state, 139, 0x0001); + dib3000mc_write_word(state, 141, 0x0000); + dib3000mc_write_word(state, 175, 0x0000); + dib3000mc_write_word(state, 1032, 0x012C); + } + dib3000mc_write_word(state, 1033, 0x0000); + + // P_clk_cfg + dib3000mc_write_word(state, 1037, 0x3130); + + // other configurations + + // P_ctrl_sfreq + dib3000mc_write_word(state, 33, (5 << 0)); + dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0)); + + // Phase noise control + // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange + dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0)); + + if (state->cfg->phase_noise_mode == 0) + dib3000mc_write_word(state, 111, 0x00); + else + dib3000mc_write_word(state, 111, 0x02); + + // P_agc_global + dib3000mc_write_word(state, 50, 0x8000); + + // agc setup misc + dib3000mc_setup_pwm_state(state); + + // P_agc_counter_lock + dib3000mc_write_word(state, 53, 0x87); + // P_agc_counter_unlock + dib3000mc_write_word(state, 54, 0x87); + + /* agc */ + dib3000mc_write_word(state, 36, state->cfg->max_time); + dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0)); + dib3000mc_write_word(state, 38, state->cfg->pwm3_value); + dib3000mc_write_word(state, 39, state->cfg->ln_adc_level); + + // set_agc_loop_Bw + dib3000mc_write_word(state, 40, 0x0179); + dib3000mc_write_word(state, 41, 0x03f0); + + dib3000mc_write_word(state, 42, agc->agc1_max); + dib3000mc_write_word(state, 43, agc->agc1_min); + dib3000mc_write_word(state, 44, agc->agc2_max); + dib3000mc_write_word(state, 45, agc->agc2_min); + dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + +// Begin: TimeOut registers + // P_pha3_thres + dib3000mc_write_word(state, 110, 3277); + // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80 + dib3000mc_write_word(state, 26, 0x6680); + // lock_mask0 + dib3000mc_write_word(state, 1, 4); + // lock_mask1 + dib3000mc_write_word(state, 2, 4); + // lock_mask2 + dib3000mc_write_word(state, 3, 0x1000); + // P_search_maxtrial=1 + dib3000mc_write_word(state, 5, 1); + + dib3000mc_set_bandwidth(state, 8000); + + // div_lock_mask + dib3000mc_write_word(state, 4, 0x814); + + dib3000mc_write_word(state, 21, (1 << 9) | 0x164); + dib3000mc_write_word(state, 22, 0x463d); + + // Spurious rm cfg + // P_cspu_regul, P_cspu_win_cut + dib3000mc_write_word(state, 120, 0x200f); + // P_adp_selec_monit + dib3000mc_write_word(state, 134, 0); + + // Fec cfg + dib3000mc_write_word(state, 195, 0x10); + + // diversity register: P_dvsy_sync_wait.. + dib3000mc_write_word(state, 180, 0x2FF0); + + // Impulse noise configuration + dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K); + + // output mode set-up + dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); + + /* close the i2c-gate */ + dib3000mc_write_word(state, 769, (1 << 7) ); + + return 0; +} + +static int dib3000mc_sleep(struct dvb_frontend *demod) +{ + struct dib3000mc_state *state = demod->demodulator_priv; + + dib3000mc_write_word(state, 1031, 0xFFFF); + dib3000mc_write_word(state, 1032, 0xFFFF); + dib3000mc_write_word(state, 1033, 0xFFF0); + + return 0; +} + +static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam) +{ + u16 cfg[4] = { 0 },reg; + switch (qam) { + case QPSK: + cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0; + break; + case QAM_16: + cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0; + break; + case QAM_64: + cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8; + break; + } + for (reg = 129; reg < 133; reg++) + dib3000mc_write_word(state, reg, cfg[reg - 129]); +} + +static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, + struct dtv_frontend_properties *ch, u16 seq) +{ + u16 value; + u32 bw = BANDWIDTH_TO_KHZ(ch->bandwidth_hz); + + dib3000mc_set_bandwidth(state, bw); + dib3000mc_set_timing(state, ch->transmission_mode, bw, 0); + +#if 1 + dib3000mc_write_word(state, 100, (16 << 6) + 9); +#else + if (boost) + dib3000mc_write_word(state, 100, (11 << 6) + 6); + else + dib3000mc_write_word(state, 100, (16 << 6) + 9); +#endif + + dib3000mc_write_word(state, 1027, 0x0800); + dib3000mc_write_word(state, 1027, 0x0000); + + //Default cfg isi offset adp + dib3000mc_write_word(state, 26, 0x6680); + dib3000mc_write_word(state, 29, 0x1273); + dib3000mc_write_word(state, 33, 5); + dib3000mc_set_adp_cfg(state, QAM_16); + dib3000mc_write_word(state, 133, 15564); + + dib3000mc_write_word(state, 12 , 0x0); + dib3000mc_write_word(state, 13 , 0x3e8); + dib3000mc_write_word(state, 14 , 0x0); + dib3000mc_write_word(state, 15 , 0x3f2); + + dib3000mc_write_word(state, 93,0); + dib3000mc_write_word(state, 94,0); + dib3000mc_write_word(state, 95,0); + dib3000mc_write_word(state, 96,0); + dib3000mc_write_word(state, 97,0); + dib3000mc_write_word(state, 98,0); + + dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode); + + value = 0; + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: value |= (0 << 7); break; + default: + case TRANSMISSION_MODE_8K: value |= (1 << 7); break; + } + switch (ch->guard_interval) { + case GUARD_INTERVAL_1_32: value |= (0 << 5); break; + case GUARD_INTERVAL_1_16: value |= (1 << 5); break; + case GUARD_INTERVAL_1_4: value |= (3 << 5); break; + default: + case GUARD_INTERVAL_1_8: value |= (2 << 5); break; + } + switch (ch->modulation) { + case QPSK: value |= (0 << 3); break; + case QAM_16: value |= (1 << 3); break; + default: + case QAM_64: value |= (2 << 3); break; + } + switch (HIERARCHY_1) { + case HIERARCHY_2: value |= 2; break; + case HIERARCHY_4: value |= 4; break; + default: + case HIERARCHY_1: value |= 1; break; + } + dib3000mc_write_word(state, 0, value); + dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4)); + + value = 0; + if (ch->hierarchy == 1) + value |= (1 << 4); + if (1 == 1) + value |= 1; + switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { + case FEC_2_3: value |= (2 << 1); break; + case FEC_3_4: value |= (3 << 1); break; + case FEC_5_6: value |= (5 << 1); break; + case FEC_7_8: value |= (7 << 1); break; + default: + case FEC_1_2: value |= (1 << 1); break; + } + dib3000mc_write_word(state, 181, value); + + // diversity synchro delay add 50% SFN margin + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_8K: value = 256; break; + case TRANSMISSION_MODE_2K: + default: value = 64; break; + } + switch (ch->guard_interval) { + case GUARD_INTERVAL_1_16: value *= 2; break; + case GUARD_INTERVAL_1_8: value *= 4; break; + case GUARD_INTERVAL_1_4: value *= 8; break; + default: + case GUARD_INTERVAL_1_32: value *= 1; break; + } + value <<= 4; + value |= dib3000mc_read_word(state, 180) & 0x000f; + dib3000mc_write_word(state, 180, value); + + // restart demod + value = dib3000mc_read_word(state, 0); + dib3000mc_write_word(state, 0, value | (1 << 9)); + dib3000mc_write_word(state, 0, value); + + msleep(30); + + dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode); +} + +static int dib3000mc_autosearch_start(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *chan = &demod->dtv_property_cache; + struct dib3000mc_state *state = demod->demodulator_priv; + u16 reg; +// u32 val; + struct dtv_frontend_properties schan; + + schan = *chan; + + /* TODO what is that ? */ + + /* a channel for autosearch */ + schan.transmission_mode = TRANSMISSION_MODE_8K; + schan.guard_interval = GUARD_INTERVAL_1_32; + schan.modulation = QAM_64; + schan.code_rate_HP = FEC_2_3; + schan.code_rate_LP = FEC_2_3; + schan.hierarchy = 0; + + dib3000mc_set_channel_cfg(state, &schan, 11); + + reg = dib3000mc_read_word(state, 0); + dib3000mc_write_word(state, 0, reg | (1 << 8)); + dib3000mc_read_word(state, 511); + dib3000mc_write_word(state, 0, reg); + + return 0; +} + +static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib3000mc_state *state = demod->demodulator_priv; + u16 irq_pending = dib3000mc_read_word(state, 511); + + if (irq_pending & 0x1) // failed + return 1; + + if (irq_pending & 0x2) // succeeded + return 2; + + return 0; // still pending +} + +static int dib3000mc_tune(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib3000mc_state *state = demod->demodulator_priv; + + // ** configure demod ** + dib3000mc_set_channel_cfg(state, ch, 0); + + // activates isi + if (state->sfn_workaround_active) { + dprintk("SFN workaround is active\n"); + dib3000mc_write_word(state, 29, 0x1273); + dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift + } else { + dib3000mc_write_word(state, 29, 0x1073); + dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift + } + + dib3000mc_set_adp_cfg(state, (u8)ch->modulation); + if (ch->transmission_mode == TRANSMISSION_MODE_8K) { + dib3000mc_write_word(state, 26, 38528); + dib3000mc_write_word(state, 33, 8); + } else { + dib3000mc_write_word(state, 26, 30336); + dib3000mc_write_word(state, 33, 6); + } + + if (dib3000mc_read_word(state, 509) & 0x80) + dib3000mc_set_timing(state, ch->transmission_mode, + BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1); + + return 0; +} + +struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating) +{ + struct dib3000mc_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating); +} + +EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master); + +static int dib3000mc_get_frontend(struct dvb_frontend* fe, + struct dtv_frontend_properties *fep) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + u16 tps = dib3000mc_read_word(state,458); + + fep->inversion = INVERSION_AUTO; + + fep->bandwidth_hz = state->current_bandwidth; + + switch ((tps >> 8) & 0x1) { + case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break; + } + + switch (tps & 0x3) { + case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 13) & 0x3) { + case 0: fep->modulation = QPSK; break; + case 1: fep->modulation = QAM_16; break; + case 2: + default: fep->modulation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */ + + fep->hierarchy = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->code_rate_HP = FEC_1_2; break; + case 2: fep->code_rate_HP = FEC_2_3; break; + case 3: fep->code_rate_HP = FEC_3_4; break; + case 5: fep->code_rate_HP = FEC_5_6; break; + case 7: + default: fep->code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->code_rate_LP = FEC_1_2; break; + case 2: fep->code_rate_LP = FEC_2_3; break; + case 3: fep->code_rate_LP = FEC_3_4; break; + case 5: fep->code_rate_LP = FEC_5_6; break; + case 7: + default: fep->code_rate_LP = FEC_7_8; break; + } + + return 0; +} + +static int dib3000mc_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct dib3000mc_state *state = fe->demodulator_priv; + int ret; + + dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); + + state->current_bandwidth = fep->bandwidth_hz; + dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz)); + + /* maybe the parameter has been changed */ + state->sfn_workaround_active = buggy_sfn_workaround; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + msleep(100); + } + + if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || + fep->guard_interval == GUARD_INTERVAL_AUTO || + fep->modulation == QAM_AUTO || + fep->code_rate_HP == FEC_AUTO) { + int i = 1000, found; + + dib3000mc_autosearch_start(fe); + do { + msleep(1); + found = dib3000mc_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n",found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib3000mc_get_frontend(fe, fep); + } + + ret = dib3000mc_tune(fe); + + /* make this a config parameter */ + dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO); + return ret; +} + +static int dib3000mc_read_status(struct dvb_frontend *fe, enum fe_status *stat) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + u16 lock = dib3000mc_read_word(state, 509); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501); + return 0; +} + +static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + *unc = dib3000mc_read_word(state, 508); + return 0; +} + +static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + u16 val = dib3000mc_read_word(state, 392); + *strength = 65535 - val; + return 0; +} + +static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + *snr = 0x0000; + return 0; +} + +static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void dib3000mc_release(struct dvb_frontend *fe) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + dibx000_exit_i2c_master(&state->i2c_master); + kfree(state); +} + +int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0); + return 0; +} +EXPORT_SYMBOL(dib3000mc_pid_control); + +int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4); + tmp |= (onoff << 4); + return dib3000mc_write_word(state, 206, tmp); +} +EXPORT_SYMBOL(dib3000mc_pid_parse); + +void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg) +{ + struct dib3000mc_state *state = fe->demodulator_priv; + state->cfg = cfg; +} +EXPORT_SYMBOL(dib3000mc_set_config); + +int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[]) +{ + struct dib3000mc_state *dmcst; + int k; + u8 new_addr; + + static const u8 DIB3000MC_I2C_ADDRESS[] = { 20, 22, 24, 26 }; + + dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); + if (dmcst == NULL) + return -ENOMEM; + + dmcst->i2c_adap = i2c; + + for (k = no_of_demods-1; k >= 0; k--) { + dmcst->cfg = &cfg[k]; + + /* designated i2c address */ + new_addr = DIB3000MC_I2C_ADDRESS[k]; + dmcst->i2c_addr = new_addr; + if (dib3000mc_identify(dmcst) != 0) { + dmcst->i2c_addr = default_addr; + if (dib3000mc_identify(dmcst) != 0) { + dprintk("-E- DiB3000P/MC #%d: not identified\n", k); + kfree(dmcst); + return -ENODEV; + } + } + + dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK); + + // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0) + dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1); + dmcst->i2c_addr = new_addr; + } + + for (k = 0; k < no_of_demods; k++) { + dmcst->cfg = &cfg[k]; + dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k]; + + dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3); + + /* turn off data output */ + dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z); + } + + kfree(dmcst); + return 0; +} +EXPORT_SYMBOL(dib3000mc_i2c_enumeration); + +static const struct dvb_frontend_ops dib3000mc_ops; + +struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg) +{ + struct dvb_frontend *demod; + struct dib3000mc_state *st; + st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + st->cfg = cfg; + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops)); + + if (dib3000mc_identify(st) != 0) + goto error; + + dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr); + + dib3000mc_write_word(st, 1037, 0x3130); + + return demod; + +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL_GPL(dib3000mc_attach); + +static const struct dvb_frontend_ops dib3000mc_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "DiBcom 3000MC/P", + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib3000mc_release, + + .init = dib3000mc_init, + .sleep = dib3000mc_sleep, + + .set_frontend = dib3000mc_set_frontend, + .get_tune_settings = dib3000mc_fe_get_tune_settings, + .get_frontend = dib3000mc_get_frontend, + + .read_status = dib3000mc_read_status, + .read_ber = dib3000mc_read_ber, + .read_signal_strength = dib3000mc_read_signal_strength, + .read_snr = dib3000mc_read_snr, + .read_ucblocks = dib3000mc_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib3000mc.h b/drivers/media/dvb-frontends/dib3000mc.h new file mode 100644 index 0000000000..5ed3d9147f --- /dev/null +++ b/drivers/media/dvb-frontends/dib3000mc.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Driver for DiBcom DiB3000MC/P-demodulator. + * + * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/) + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) + * + * This code is partially based on the previous dib3000mc.c . + */ +#ifndef DIB3000MC_H +#define DIB3000MC_H + +#include "dibx000_common.h" + +struct dib3000mc_config { + struct dibx000_agc_config *agc; + + u8 phase_noise_mode; + u8 impulse_noise_mode; + + u8 pwm3_inversion; + u8 use_pwm3; + u16 pwm3_value; + + u16 max_time; + u16 ln_adc_level; + + u8 agc_command1 :1; + u8 agc_command2 :1; + + u8 mobile_mode; + + u8 output_mpeg2_in_188_bytes; +}; + +#define DEFAULT_DIB3000MC_I2C_ADDRESS 16 +#define DEFAULT_DIB3000P_I2C_ADDRESS 24 + +#if IS_REACHABLE(CONFIG_DVB_DIB3000MC) +extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, + struct dib3000mc_config *cfg); +extern int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, + int no_of_demods, u8 default_addr, + struct dib3000mc_config cfg[]); +extern +struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, + int gating); +#else +static inline +struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, + struct dib3000mc_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline +int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, + int no_of_demods, u8 default_addr, + struct dib3000mc_config cfg[]) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline +struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, + int gating) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_DIB3000MC + +extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff); +extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff); + +extern void dib3000mc_set_config(struct dvb_frontend *, struct dib3000mc_config *); + +#endif diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c new file mode 100644 index 0000000000..fdb22f32e3 --- /dev/null +++ b/drivers/media/dvb-frontends/dib7000m.c @@ -0,0 +1,1474 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Linux-DVB Driver for DiBcom's DiB7000M and + * first generation DiB7000P-demodulator-family. + * + * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +#include <media/dvb_frontend.h> + +#include "dib7000m.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +struct dib7000m_state { + struct dvb_frontend demod; + struct dib7000m_config cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + +/* offset is 1 in case of the 7000MC */ + u8 reg_offs; + + u16 wbd_ref; + + u8 current_band; + u32 current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + u32 timf_default; + u32 internal_clk; + + u8 div_force_off : 1; + u8 div_state : 1; + u16 div_sync_wait; + + u16 revision; + + u8 agc_state; + + /* for the I2C transfer */ + struct i2c_msg msg[2]; + u8 i2c_write_buffer[4]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; +}; + +enum dib7000m_power_mode { + DIB7000M_POWER_ALL = 0, + + DIB7000M_POWER_NO, + DIB7000M_POWER_INTERF_ANALOG_AGC, + DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, + DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD, + DIB7000M_POWER_INTERFACE_ONLY, +}; + +static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + state->i2c_write_buffer[0] = (reg >> 8) | 0x80; + state->i2c_write_buffer[1] = reg & 0xff; + + memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c_addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 2; + state->msg[1].addr = state->i2c_addr >> 1; + state->msg[1].flags = I2C_M_RD; + state->msg[1].buf = state->i2c_read_buffer; + state->msg[1].len = 2; + + if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) + dprintk("i2c read error on %d\n", reg); + + ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + mutex_unlock(&state->i2c_buffer_lock); + + return ret; +} + +static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; + state->i2c_write_buffer[1] = reg & 0xff; + state->i2c_write_buffer[2] = (val >> 8) & 0xff; + state->i2c_write_buffer[3] = val & 0xff; + + memset(&state->msg[0], 0, sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c_addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 4; + + ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? + -EREMOTEIO : 0); + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} +static void dib7000m_write_tab(struct dib7000m_state *state, u16 *buf) +{ + u16 l = 0, r, *n; + n = buf; + l = *n++; + while (l) { + r = *n++; + + if (state->reg_offs && (r >= 112 && r <= 331)) // compensate for 7000MC + r++; + + do { + dib7000m_write_word(state, r, *n++); + r++; + } while (--l); + l = *n++; + } +} + +static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode) +{ + int ret = 0; + u16 outreg, fifo_threshold, smo_mode, + sram = 0x0005; /* by default SRAM output is disabled */ + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1); + + dprintk("setting output mode for demod %p to %d\n", &state->demod, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + sram |= 0x0c00; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->demod); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5) ; + + ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode); + ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */ + ret |= dib7000m_write_word(state, 1795, outreg); + ret |= dib7000m_write_word(state, 1805, sram); + + if (state->revision == 0x4003) { + u16 clk_cfg1 = dib7000m_read_word(state, 909) & 0xfffd; + if (mode == OUTMODE_DIVERSITY) + clk_cfg1 |= (1 << 1); // P_O_CLK_en + dib7000m_write_word(state, 909, clk_cfg1); + } + return ret; +} + +static void dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff; + u8 offset = 0; + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB7000M_POWER_ALL: + reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000; + break; + + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ + case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); + break; + + case DIB7000M_POWER_INTERF_ANALOG_AGC: + reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); + reg_906 &= ~((1 << 0)); + break; + + case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: + reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000; + break; + + case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD: + reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000; + break; + case DIB7000M_POWER_NO: + break; + } + + /* always power down unused parts */ + if (!state->cfg.mobile_mode) + reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); + + /* P_sdio_select_clk = 0 on MC and after*/ + if (state->revision != 0x4000) + reg_906 <<= 1; + + if (state->revision == 0x4003) + offset = 1; + + dib7000m_write_word(state, 903 + offset, reg_903); + dib7000m_write_word(state, 904 + offset, reg_904); + dib7000m_write_word(state, 905 + offset, reg_905); + dib7000m_write_word(state, 906 + offset, reg_906); +} + +static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no) +{ + int ret = 0; + u16 reg_913 = dib7000m_read_word(state, 913), + reg_914 = dib7000m_read_word(state, 914); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_914 |= (1 << 1) | (1 << 0); + ret |= dib7000m_write_word(state, 914, reg_914); + reg_914 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_914 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + if (state->revision == 0x4000) { // workaround for PA/MA + // power-up ADC + dib7000m_write_word(state, 913, 0); + dib7000m_write_word(state, 914, reg_914 & 0x3); + // power-down bandgag + dib7000m_write_word(state, 913, (1 << 15)); + dib7000m_write_word(state, 914, reg_914 & 0x3); + } + + reg_913 &= 0x0fff; + reg_914 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_913 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_913 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_913 |= (1 << 15); + break; + + default: + break; + } + +// dprintk("913: %x, 914: %x\n", reg_913, reg_914); + ret |= dib7000m_write_word(state, 913, reg_913); + ret |= dib7000m_write_word(state, 914, reg_914); + + return ret; +} + +static int dib7000m_set_bandwidth(struct dib7000m_state *state, u32 bw) +{ + u32 timf; + + if (!bw) + bw = 8000; + + // store the current bandwidth for later use + state->current_bandwidth = bw; + + if (state->timf == 0) { + dprintk("using default timf\n"); + timf = state->timf_default; + } else { + dprintk("using updated timf\n"); + timf = state->timf; + } + + timf = timf * (bw / 50) / 160; + + dib7000m_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); + dib7000m_write_word(state, 24, (u16) ((timf ) & 0xffff)); + + return 0; +} + +static int dib7000m_set_diversity_in(struct dvb_frontend *demod, int onoff) +{ + struct dib7000m_state *state = demod->demodulator_priv; + + if (state->div_force_off) { + dprintk("diversity combination deactivated - forced by COFDM parameters\n"); + onoff = 0; + } + state->div_state = (u8)onoff; + + if (onoff) { + dib7000m_write_word(state, 263 + state->reg_offs, 6); + dib7000m_write_word(state, 264 + state->reg_offs, 6); + dib7000m_write_word(state, 266 + state->reg_offs, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); + } else { + dib7000m_write_word(state, 263 + state->reg_offs, 1); + dib7000m_write_word(state, 264 + state->reg_offs, 0); + dib7000m_write_word(state, 266 + state->reg_offs, 0); + } + + return 0; +} + +static int dib7000m_sad_calib(struct dib7000m_state *state) +{ + +/* internal */ +// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writing in set_bandwidth + dib7000m_write_word(state, 929, (0 << 1) | (0 << 0)); + dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib7000m_write_word(state, 929, (1 << 0)); + dib7000m_write_word(state, 929, (0 << 0)); + + msleep(1); + + return 0; +} + +static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw) +{ + dib7000m_write_word(state, 18, (u16) (((bw->internal*1000) >> 16) & 0xffff)); + dib7000m_write_word(state, 19, (u16) ( (bw->internal*1000) & 0xffff)); + dib7000m_write_word(state, 21, (u16) ( (bw->ifreq >> 16) & 0xffff)); + dib7000m_write_word(state, 22, (u16) ( bw->ifreq & 0xffff)); + + dib7000m_write_word(state, 928, bw->sad_cfg); +} + +static void dib7000m_reset_pll(struct dib7000m_state *state) +{ + const struct dibx000_bandwidth_config *bw = state->cfg.bw; + u16 reg_907,reg_910; + + /* default */ + reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) | + (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | + (bw->enable_refdiv << 1) | (0 << 0); + reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset; + + // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value) + // this is only working only for 30 MHz crystals + if (!state->cfg.quartz_direct) { + reg_910 |= (1 << 5); // forcing the predivider to 1 + + // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2) + if(state->cfg.input_clk_is_div_2) + reg_907 |= (16 << 9); + else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary + reg_907 |= (8 << 9); + } else { + reg_907 |= (bw->pll_ratio & 0x3f) << 9; + reg_910 |= (bw->pll_prediv << 5); + } + + dib7000m_write_word(state, 910, reg_910); // pll cfg + dib7000m_write_word(state, 907, reg_907); // clk cfg0 + dib7000m_write_word(state, 908, 0x0006); // clk_cfg1 + + dib7000m_reset_pll_common(state, bw); +} + +static void dib7000mc_reset_pll(struct dib7000m_state *state) +{ + const struct dibx000_bandwidth_config *bw = state->cfg.bw; + u16 clk_cfg1; + + // clk_cfg0 + dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0)); + + // clk_cfg1 + //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) | + clk_cfg1 = (0 << 14) | (3 << 12) |(0 << 11) | + (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) | + (1 << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0); + dib7000m_write_word(state, 908, clk_cfg1); + clk_cfg1 = (clk_cfg1 & 0xfff7) | (bw->pll_bypass << 3); + dib7000m_write_word(state, 908, clk_cfg1); + + // smpl_cfg + dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7)); + + dib7000m_reset_pll_common(state, bw); +} + +static int dib7000m_reset_gpio(struct dib7000m_state *st) +{ + /* reset the GPIOs */ + dib7000m_write_word(st, 773, st->cfg.gpio_dir); + dib7000m_write_word(st, 774, st->cfg.gpio_val); + + /* TODO 782 is P_gpio_od */ + + dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos); + + dib7000m_write_word(st, 780, st->cfg.pwm_freq_div); + return 0; +} + +static u16 dib7000m_defaults_common[] = + +{ + // auto search configuration + 3, 2, + 0x0004, + 0x1000, + 0x0814, + + 12, 6, + 0x001b, + 0x7740, + 0x005b, + 0x8d80, + 0x01c9, + 0xc380, + 0x0000, + 0x0080, + 0x0000, + 0x0090, + 0x0001, + 0xd4c0, + + 1, 26, + 0x6680, // P_corm_thres Lock algorithms configuration + + 1, 170, + 0x0410, // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on + + 8, 173, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + 1, 182, + 8192, // P_fft_nb_to_cut + + 2, 195, + 0x0ccd, // P_pha3_thres + 0, // P_cti_use_cpe, P_cti_use_prog + + 1, 205, + 0x200f, // P_cspu_regul, P_cspu_win_cut + + 5, 214, + 0x023d, // P_adp_regul_cnt + 0x00a4, // P_adp_noise_cnt + 0x00a4, // P_adp_regul_ext + 0x7ff0, // P_adp_noise_ext + 0x3ccc, // P_adp_fil + + 1, 226, + 0, // P_2d_byp_ti_num + + 1, 255, + 0x800, // P_equal_thres_wgn + + 1, 263, + 0x0001, + + 1, 281, + 0x0010, // P_fec_* + + 1, 294, + 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + + 0 +}; + +static u16 dib7000m_defaults[] = + +{ + /* set ADC level to -16 */ + 11, 76, + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117, + + // Tuner IO bank: max drive (14mA) + 1, 912, + 0x2c8a, + + 1, 1817, + 1, + + 0, +}; + +static int dib7000m_demod_reset(struct dib7000m_state *state) +{ + dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); + + /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ + dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib7000m_write_word(state, 898, 0xffff); + dib7000m_write_word(state, 899, 0xffff); + dib7000m_write_word(state, 900, 0xff0f); + dib7000m_write_word(state, 901, 0xfffc); + + dib7000m_write_word(state, 898, 0); + dib7000m_write_word(state, 899, 0); + dib7000m_write_word(state, 900, 0); + dib7000m_write_word(state, 901, 0); + + if (state->revision == 0x4000) + dib7000m_reset_pll(state); + else + dib7000mc_reset_pll(state); + + if (dib7000m_reset_gpio(state) != 0) + dprintk("GPIO reset was not successful.\n"); + + if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("OUTPUT_MODE could not be reset.\n"); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) ); + + dib7000m_set_bandwidth(state, 8000); + + dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib7000m_sad_calib(state); + dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + if (state->cfg.dvbt_mode) + dib7000m_write_word(state, 1796, 0x0); // select DVB-T output + + if (state->cfg.mobile_mode) + dib7000m_write_word(state, 261 + state->reg_offs, 2); + else + dib7000m_write_word(state, 224 + state->reg_offs, 1); + + // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... + if(state->cfg.tuner_is_baseband) + dib7000m_write_word(state, 36, 0x0755); + else + dib7000m_write_word(state, 36, 0x1f55); + + // P_divclksel=3 P_divbitsel=1 + if (state->revision == 0x4000) + dib7000m_write_word(state, 909, (3 << 10) | (1 << 6)); + else + dib7000m_write_word(state, 909, (3 << 4) | 1); + + dib7000m_write_tab(state, dib7000m_defaults_common); + dib7000m_write_tab(state, dib7000m_defaults); + + dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY); + + state->internal_clk = state->cfg.bw->internal; + + return 0; +} + +static void dib7000m_restart_agc(struct dib7000m_state *state) +{ + // P_restart_iqc & P_restart_agc + dib7000m_write_word(state, 898, 0x0c00); + dib7000m_write_word(state, 898, 0x0000); +} + +static int dib7000m_agc_soft_split(struct dib7000m_state *state) +{ + u16 agc,split_offset; + + if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) + return 0; + + // n_agc_global + agc = dib7000m_read_word(state, 390); + + if (agc > state->current_agc->split.min_thres) + split_offset = state->current_agc->split.min; + else if (agc < state->current_agc->split.max_thres) + split_offset = state->current_agc->split.max; + else + split_offset = state->current_agc->split.max * + (agc - state->current_agc->split.min_thres) / + (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + + dprintk("AGC split_offset: %d\n", split_offset); + + // P_agc_force_split and P_agc_split_offset + return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset); +} + +static int dib7000m_update_lna(struct dib7000m_state *state) +{ + u16 dyn_gain; + + if (state->cfg.update_lna) { + // read dyn_gain here (because it is demod-dependent and not fe) + dyn_gain = dib7000m_read_word(state, 390); + + if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed + dib7000m_restart_agc(state); + return 1; + } + } + return 0; +} + +static int dib7000m_set_agc_config(struct dib7000m_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + if (state->current_band == band && state->current_agc != NULL) + return 0; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("no valid AGC configuration found for band 0x%02x\n", band); + return -EINVAL; + } + + state->current_agc = agc; + + /* AGC */ + dib7000m_write_word(state, 72 , agc->setup); + dib7000m_write_word(state, 73 , agc->inv_gain); + dib7000m_write_word(state, 74 , agc->time_stabiliz); + dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp); + dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp); + + dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + /* AGC continued */ + if (state->wbd_ref != 0) + dib7000m_write_word(state, 102, state->wbd_ref); + else // use default + dib7000m_write_word(state, 102, agc->wbd_ref); + + dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); + dib7000m_write_word(state, 104, agc->agc1_max); + dib7000m_write_word(state, 105, agc->agc1_min); + dib7000m_write_word(state, 106, agc->agc2_max); + dib7000m_write_word(state, 107, agc->agc2_min); + dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); + dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + if (state->revision > 0x4000) { // settings for the MC + dib7000m_write_word(state, 71, agc->agc1_pt3); +// dprintk("929: %x %d %d\n", +// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel); + dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); + } else { + // wrong default values + u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 }; + for (i = 0; i < 9; i++) + dib7000m_write_word(state, 88 + i, b[i]); + } + return 0; +} + +static void dib7000m_update_timf(struct dib7000m_state *state) +{ + u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437); + state->timf = timf * 160 / (state->current_bandwidth / 50); + dib7000m_write_word(state, 23, (u16) (timf >> 16)); + dib7000m_write_word(state, 24, (u16) (timf & 0xffff)); + dprintk("updated timf_frequency: %d (default: %d)\n", state->timf, state->timf_default); +} + +static int dib7000m_agc_startup(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib7000m_state *state = demod->demodulator_priv; + u16 cfg_72 = dib7000m_read_word(state, 72); + int ret = -1; + u8 *agc_state = &state->agc_state; + u8 agc_split; + + switch (state->agc_state) { + case 0: + // set power-up level: interf+analog+AGC + dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC); + dib7000m_set_adc_state(state, DIBX000_ADC_ON); + + if (dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency/1000)) != 0) + return -1; + + ret = 7; /* ADC power up */ + (*agc_state)++; + break; + + case 1: + /* AGC initialization */ + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); + + dib7000m_write_word(state, 75, 32768); + if (!state->current_agc->perform_agc_softsplit) { + /* we are using the wbd - so slow AGC startup */ + dib7000m_write_word(state, 103, 1 << 8); /* force 0 split on WBD and restart AGC */ + (*agc_state)++; + ret = 5; + } else { + /* default AGC startup */ + (*agc_state) = 4; + /* wait AGC rough lock time */ + ret = 7; + } + + dib7000m_restart_agc(state); + break; + + case 2: /* fast split search path after 5sec */ + dib7000m_write_word(state, 72, cfg_72 | (1 << 4)); /* freeze AGC loop */ + dib7000m_write_word(state, 103, 2 << 9); /* fast split search 0.25kHz */ + (*agc_state)++; + ret = 14; + break; + + case 3: /* split search ended */ + agc_split = (u8)dib7000m_read_word(state, 392); /* store the split value for the next time */ + dib7000m_write_word(state, 75, dib7000m_read_word(state, 390)); /* set AGC gain start value */ + + dib7000m_write_word(state, 72, cfg_72 & ~(1 << 4)); /* std AGC loop */ + dib7000m_write_word(state, 103, (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ + + dib7000m_restart_agc(state); + + dprintk("SPLIT %p: %u\n", demod, agc_split); + + (*agc_state)++; + ret = 5; + break; + + case 4: /* LNA startup */ + /* wait AGC accurate lock time */ + ret = 7; + + if (dib7000m_update_lna(state)) + // wait only AGC rough lock time + ret = 5; + else + (*agc_state)++; + break; + + case 5: + dib7000m_agc_soft_split(state); + + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); + + (*agc_state)++; + break; + + default: + break; + } + return ret; +} + +static void dib7000m_set_channel(struct dib7000m_state *state, struct dtv_frontend_properties *ch, + u8 seq) +{ + u16 value, est[4]; + + dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); + + /* nfft, guard, qam, alpha */ + value = 0; + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: value |= (0 << 7); break; + case TRANSMISSION_MODE_4K: value |= (2 << 7); break; + default: + case TRANSMISSION_MODE_8K: value |= (1 << 7); break; + } + switch (ch->guard_interval) { + case GUARD_INTERVAL_1_32: value |= (0 << 5); break; + case GUARD_INTERVAL_1_16: value |= (1 << 5); break; + case GUARD_INTERVAL_1_4: value |= (3 << 5); break; + default: + case GUARD_INTERVAL_1_8: value |= (2 << 5); break; + } + switch (ch->modulation) { + case QPSK: value |= (0 << 3); break; + case QAM_16: value |= (1 << 3); break; + default: + case QAM_64: value |= (2 << 3); break; + } + switch (HIERARCHY_1) { + case HIERARCHY_2: value |= 2; break; + case HIERARCHY_4: value |= 4; break; + default: + case HIERARCHY_1: value |= 1; break; + } + dib7000m_write_word(state, 0, value); + dib7000m_write_word(state, 5, (seq << 4)); + + /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ + value = 0; + if (1 != 0) + value |= (1 << 6); + if (ch->hierarchy == 1) + value |= (1 << 4); + if (1 == 1) + value |= 1; + switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { + case FEC_2_3: value |= (2 << 1); break; + case FEC_3_4: value |= (3 << 1); break; + case FEC_5_6: value |= (5 << 1); break; + case FEC_7_8: value |= (7 << 1); break; + default: + case FEC_1_2: value |= (1 << 1); break; + } + dib7000m_write_word(state, 267 + state->reg_offs, value); + + /* offset loop parameters */ + + /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */ + dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */ + dib7000m_write_word(state, 32, (0 << 4) | 0x3); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */ + dib7000m_write_word(state, 33, (0 << 4) | 0x5); + + /* P_dvsy_sync_wait */ + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_8K: value = 256; break; + case TRANSMISSION_MODE_4K: value = 128; break; + case TRANSMISSION_MODE_2K: + default: value = 64; break; + } + switch (ch->guard_interval) { + case GUARD_INTERVAL_1_16: value *= 2; break; + case GUARD_INTERVAL_1_8: value *= 4; break; + case GUARD_INTERVAL_1_4: value *= 8; break; + default: + case GUARD_INTERVAL_1_32: value *= 1; break; + } + state->div_sync_wait = (value * 3) / 2 + 32; // add 50% SFN margin + compensate for one DVSY-fifo TODO + + /* deactivate the possibility of diversity reception if extended interleave - not for 7000MC */ + /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ + if (1 == 1 || state->revision > 0x4000) + state->div_force_off = 0; + else + state->div_force_off = 1; + dib7000m_set_diversity_in(&state->demod, state->div_state); + + /* channel estimation fine configuration */ + switch (ch->modulation) { + case QAM_64: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case QAM_16: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (value = 0; value < 4; value++) + dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]); + + // set power-up level: autosearch + dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD); +} + +static int dib7000m_autosearch_start(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib7000m_state *state = demod->demodulator_priv; + struct dtv_frontend_properties schan; + int ret = 0; + u32 value, factor; + + schan = *ch; + + schan.modulation = QAM_64; + schan.guard_interval = GUARD_INTERVAL_1_32; + schan.transmission_mode = TRANSMISSION_MODE_8K; + schan.code_rate_HP = FEC_2_3; + schan.code_rate_LP = FEC_3_4; + schan.hierarchy = 0; + + dib7000m_set_channel(state, &schan, 7); + + factor = BANDWIDTH_TO_KHZ(schan.bandwidth_hz); + if (factor >= 5000) + factor = 1; + else + factor = 6; + + // always use the setting for 8MHz here lock_time for 7,6 MHz are longer + value = 30 * state->internal_clk * factor; + ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->internal_clk * factor; + ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + value = 500 * state->internal_clk * factor; + ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + + // start search + value = dib7000m_read_word(state, 0); + ret |= dib7000m_write_word(state, 0, (u16) (value | (1 << 9))); + + /* clear n_irq_pending */ + if (state->revision == 0x4000) + dib7000m_write_word(state, 1793, 0); + else + dib7000m_read_word(state, 537); + + ret |= dib7000m_write_word(state, 0, (u16) value); + + return ret; +} + +static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg) +{ + u16 irq_pending = dib7000m_read_word(state, reg); + + if (irq_pending & 0x1) { // failed + dprintk("autosearch failed\n"); + return 1; + } + + if (irq_pending & 0x2) { // succeeded + dprintk("autosearch succeeded\n"); + return 2; + } + return 0; // still pending +} + +static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib7000m_state *state = demod->demodulator_priv; + if (state->revision == 0x4000) + return dib7000m_autosearch_irq(state, 1793); + else + return dib7000m_autosearch_irq(state, 537); +} + +static int dib7000m_tune(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib7000m_state *state = demod->demodulator_priv; + int ret = 0; + u16 value; + + // we are already tuned - just resuming from suspend + dib7000m_set_channel(state, ch, 0); + + // restart demod + ret |= dib7000m_write_word(state, 898, 0x4000); + ret |= dib7000m_write_word(state, 898, 0x0000); + msleep(45); + + dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD); + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + // never achieved a lock before - wait for timfreq to update + if (state->timf == 0) + msleep(200); + + //dump_reg(state); + /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ + value = (6 << 8) | 0x80; + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: value |= (7 << 12); break; + case TRANSMISSION_MODE_4K: value |= (8 << 12); break; + default: + case TRANSMISSION_MODE_8K: value |= (9 << 12); break; + } + ret |= dib7000m_write_word(state, 26, value); + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ + value = (0 << 4); + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: value |= 0x6; break; + case TRANSMISSION_MODE_4K: value |= 0x7; break; + default: + case TRANSMISSION_MODE_8K: value |= 0x8; break; + } + ret |= dib7000m_write_word(state, 32, value); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ + value = (0 << 4); + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: value |= 0x6; break; + case TRANSMISSION_MODE_4K: value |= 0x7; break; + default: + case TRANSMISSION_MODE_8K: value |= 0x8; break; + } + ret |= dib7000m_write_word(state, 33, value); + + // we achieved a lock - it's time to update the timf freq + if ((dib7000m_read_word(state, 535) >> 6) & 0x1) + dib7000m_update_timf(state); + + dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); + return ret; +} + +static int dib7000m_wakeup(struct dvb_frontend *demod) +{ + struct dib7000m_state *state = demod->demodulator_priv; + + dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); + + if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) + dprintk("could not start Slow ADC\n"); + + return 0; +} + +static int dib7000m_sleep(struct dvb_frontend *demod) +{ + struct dib7000m_state *st = demod->demodulator_priv; + dib7000m_set_output_mode(st, OUTMODE_HIGH_Z); + dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY); + return dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | + dib7000m_set_adc_state(st, DIBX000_ADC_OFF); +} + +static int dib7000m_identify(struct dib7000m_state *state) +{ + u16 value; + + if ((value = dib7000m_read_word(state, 896)) != 0x01b3) { + dprintk("wrong Vendor ID (0x%x)\n", value); + return -EREMOTEIO; + } + + state->revision = dib7000m_read_word(state, 897); + if (state->revision != 0x4000 && + state->revision != 0x4001 && + state->revision != 0x4002 && + state->revision != 0x4003) { + dprintk("wrong Device ID (0x%x)\n", value); + return -EREMOTEIO; + } + + /* protect this driver to be used with 7000PC */ + if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) { + dprintk("this driver does not work with DiB7000PC\n"); + return -EREMOTEIO; + } + + switch (state->revision) { + case 0x4000: dprintk("found DiB7000MA/PA/MB/PB\n"); break; + case 0x4001: state->reg_offs = 1; dprintk("found DiB7000HC\n"); break; + case 0x4002: state->reg_offs = 1; dprintk("found DiB7000MC\n"); break; + case 0x4003: state->reg_offs = 1; dprintk("found DiB9000\n"); break; + } + + return 0; +} + + +static int dib7000m_get_frontend(struct dvb_frontend* fe, + struct dtv_frontend_properties *fep) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 tps = dib7000m_read_word(state,480); + + fep->inversion = INVERSION_AUTO; + + fep->bandwidth_hz = BANDWIDTH_TO_HZ(state->current_bandwidth); + + switch ((tps >> 8) & 0x3) { + case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break; + /* case 2: fep->transmission_mode = TRANSMISSION_MODE_4K; break; */ + } + + switch (tps & 0x3) { + case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 14) & 0x3) { + case 0: fep->modulation = QPSK; break; + case 1: fep->modulation = QAM_16; break; + case 2: + default: fep->modulation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ + + fep->hierarchy = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->code_rate_HP = FEC_1_2; break; + case 2: fep->code_rate_HP = FEC_2_3; break; + case 3: fep->code_rate_HP = FEC_3_4; break; + case 5: fep->code_rate_HP = FEC_5_6; break; + case 7: + default: fep->code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->code_rate_LP = FEC_1_2; break; + case 2: fep->code_rate_LP = FEC_2_3; break; + case 3: fep->code_rate_LP = FEC_3_4; break; + case 5: fep->code_rate_LP = FEC_5_6; break; + case 7: + default: fep->code_rate_LP = FEC_7_8; break; + } + + /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */ + + return 0; +} + +static int dib7000m_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct dib7000m_state *state = fe->demodulator_priv; + int time, ret; + + dib7000m_set_output_mode(state, OUTMODE_HIGH_Z); + + dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz)); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* start up the AGC */ + state->agc_state = 0; + do { + time = dib7000m_agc_startup(fe); + if (time != -1) + msleep(time); + } while (time != -1); + + if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || + fep->guard_interval == GUARD_INTERVAL_AUTO || + fep->modulation == QAM_AUTO || + fep->code_rate_HP == FEC_AUTO) { + int i = 800, found; + + dib7000m_autosearch_start(fe); + do { + msleep(1); + found = dib7000m_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n", found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib7000m_get_frontend(fe, fep); + } + + ret = dib7000m_tune(fe); + + /* make this a config parameter */ + dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO); + return ret; +} + +static int dib7000m_read_status(struct dvb_frontend *fe, enum fe_status *stat) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 lock = dib7000m_read_word(state, 535); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dib7000m_state *state = fe->demodulator_priv; + *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527); + return 0; +} + +static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct dib7000m_state *state = fe->demodulator_priv; + *unc = dib7000m_read_word(state, 534); + return 0; +} + +static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 val = dib7000m_read_word(state, 390); + *strength = 65535 - val; + return 0; +} + +static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + *snr = 0x0000; + return 0; +} + +static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void dib7000m_release(struct dvb_frontend *demod) +{ + struct dib7000m_state *st = demod->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +{ + struct dib7000m_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} +EXPORT_SYMBOL(dib7000m_get_i2c_master); + +int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 val = dib7000m_read_word(state, 294 + state->reg_offs) & 0xffef; + val |= (onoff & 0x1) << 4; + dprintk("PID filter enabled %d\n", onoff); + return dib7000m_write_word(state, 294 + state->reg_offs, val); +} +EXPORT_SYMBOL(dib7000m_pid_filter_ctrl); + +int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib7000m_state *state = fe->demodulator_priv; + dprintk("PID filter: index %x, PID %d, OnOff %d\n", id, pid, onoff); + return dib7000m_write_word(state, 300 + state->reg_offs + id, + onoff ? (1 << 13) | pid : 0); +} +EXPORT_SYMBOL(dib7000m_pid_filter); + +#if 0 +/* used with some prototype boards */ +int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, + u8 default_addr, struct dib7000m_config cfg[]) +{ + struct dib7000m_state st = { .i2c_adap = i2c }; + int k = 0; + u8 new_addr = 0; + + for (k = no_of_demods-1; k >= 0; k--) { + st.cfg = cfg[k]; + + /* designated i2c address */ + new_addr = (0x40 + k) << 1; + st.i2c_addr = new_addr; + if (dib7000m_identify(&st) != 0) { + st.i2c_addr = default_addr; + if (dib7000m_identify(&st) != 0) { + dprintk("DiB7000M #%d: not identified\n", k); + return -EIO; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY); + + dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output + + /* set new i2c address and force divstart */ + dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + st.cfg = cfg[k]; + st.i2c_addr = (0x40 + k) << 1; + + // unforce divstr + dib7000m_write_word(&st,1794, st.i2c_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z); + } + + return 0; +} +EXPORT_SYMBOL(dib7000m_i2c_enumeration); +#endif + +static const struct dvb_frontend_ops dib7000m_ops; +struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) +{ + struct dvb_frontend *demod; + struct dib7000m_state *st; + st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config)); + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops)); + mutex_init(&st->i2c_buffer_lock); + + st->timf_default = cfg->bw->timf; + + if (dib7000m_identify(st) != 0) + goto error; + + if (st->revision == 0x4000) + dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr); + else + dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr); + + dib7000m_demod_reset(st); + + return demod; + +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL_GPL(dib7000m_attach); + +static const struct dvb_frontend_ops dib7000m_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "DiBcom 7000MA/MB/PA/PB/MC", + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000m_release, + + .init = dib7000m_wakeup, + .sleep = dib7000m_sleep, + + .set_frontend = dib7000m_set_frontend, + .get_tune_settings = dib7000m_fe_get_tune_settings, + .get_frontend = dib7000m_get_frontend, + + .read_status = dib7000m_read_status, + .read_ber = dib7000m_read_ber, + .read_signal_strength = dib7000m_read_signal_strength, + .read_snr = dib7000m_read_snr, + .read_ucblocks = dib7000m_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib7000m.h b/drivers/media/dvb-frontends/dib7000m.h new file mode 100644 index 0000000000..df7ecb4314 --- /dev/null +++ b/drivers/media/dvb-frontends/dib7000m.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DIB7000M_H +#define DIB7000M_H + +#include "dibx000_common.h" + +struct dib7000m_config { + u8 dvbt_mode; + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + u8 mobile_mode; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + + struct dibx000_bandwidth_config *bw; + +#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + u16 pwm_freq_div; + + u8 quartz_direct; + + u8 input_clk_is_div_2; + + int (*agc_control) (struct dvb_frontend *, u8 before); +}; + +#define DEFAULT_DIB7000M_I2C_ADDRESS 18 + +#if IS_REACHABLE(CONFIG_DVB_DIB7000M) +extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, + struct dib7000m_config *cfg); +extern struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *, + enum dibx000_i2c_interface, + int); +extern int dib7000m_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); +extern int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +#else +static inline +struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, struct dib7000m_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline +struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *demod, + enum dibx000_i2c_interface intf, + int gating) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, + u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, + uint8_t onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif + +/* TODO +extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); +extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod); +extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); +extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod); +*/ + +#endif diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c new file mode 100644 index 0000000000..444fe1c4bf --- /dev/null +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -0,0 +1,2860 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC). + * + * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <asm/div64.h> + +#include <linux/int_log.h> +#include <media/dvb_frontend.h> + +#include "dib7000p.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +static int buggy_sfn_workaround; +module_param(buggy_sfn_workaround, int, 0644); +MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +struct i2c_device { + struct i2c_adapter *i2c_adap; + u8 i2c_addr; +}; + +struct dib7000p_state { + struct dvb_frontend demod; + struct dib7000p_config cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + + u16 wbd_ref; + + u8 current_band; + u32 current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + + u8 div_force_off:1; + u8 div_state:1; + u16 div_sync_wait; + + u8 agc_state; + + u16 gpio_dir; + u16 gpio_val; + + u8 sfn_workaround_active:1; + +#define SOC7090 0x7090 + u16 version; + + u16 tuner_enable; + struct i2c_adapter dib7090_tuner_adap; + + /* for the I2C transfer */ + struct i2c_msg msg[2]; + u8 i2c_write_buffer[4]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; + + u8 input_mode_mpeg; + + /* for DVBv5 stats */ + s64 old_ucb; + unsigned long per_jiffies_stats; + unsigned long ber_jiffies_stats; + unsigned long get_stats_time; +}; + +enum dib7000p_power_mode { + DIB7000P_POWER_ALL = 0, + DIB7000P_POWER_ANALOG_ADC, + DIB7000P_POWER_INTERFACE_ONLY, +}; + +/* dib7090 specific functions */ +static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode); +static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff); +static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode); +static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode); + +static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + state->i2c_write_buffer[0] = reg >> 8; + state->i2c_write_buffer[1] = reg & 0xff; + + memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c_addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 2; + state->msg[1].addr = state->i2c_addr >> 1; + state->msg[1].flags = I2C_M_RD; + state->msg[1].buf = state->i2c_read_buffer; + state->msg[1].len = 2; + + if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) + dprintk("i2c read error on %d\n", reg); + + ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; + state->i2c_write_buffer[1] = reg & 0xff; + state->i2c_write_buffer[2] = (val >> 8) & 0xff; + state->i2c_write_buffer[3] = val & 0xff; + + memset(&state->msg[0], 0, sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c_addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 4; + + ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? + -EREMOTEIO : 0); + mutex_unlock(&state->i2c_buffer_lock); + return ret; +} + +static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf) +{ + u16 l = 0, r, *n; + n = buf; + l = *n++; + while (l) { + r = *n++; + + do { + dib7000p_write_word(state, r, *n++); + r++; + } while (--l); + l = *n++; + } +} + +static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) +{ + int ret = 0; + u16 outreg, fifo_threshold, smo_mode; + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); + + dprintk("setting output mode for demod %p to %d\n", &state->demod, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + outreg = (1 << 11); + break; + case OUTMODE_MPEG2_FIFO: + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_ANALOG_ADC: + outreg = (1 << 10) | (3 << 6); + break; + case OUTMODE_HIGH_Z: + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->demod); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + ret |= dib7000p_write_word(state, 235, smo_mode); + ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ + if (state->version != SOC7090) + ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ + + return ret; +} + +static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) +{ + struct dib7000p_state *state = demod->demodulator_priv; + + if (state->div_force_off) { + dprintk("diversity combination deactivated - forced by COFDM parameters\n"); + onoff = 0; + dib7000p_write_word(state, 207, 0); + } else + dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); + + state->div_state = (u8) onoff; + + if (onoff) { + dib7000p_write_word(state, 204, 6); + dib7000p_write_word(state, 205, 16); + /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ + } else { + dib7000p_write_word(state, 204, 1); + dib7000p_write_word(state, 205, 0); + } + + return 0; +} + +static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode) +{ + /* by default everything is powered off */ + u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB7000P_POWER_ALL: + reg_774 = 0x0000; + reg_775 = 0x0000; + reg_776 = 0x0; + reg_899 = 0x0; + if (state->version == SOC7090) + reg_1280 &= 0x001f; + else + reg_1280 &= 0x01ff; + break; + + case DIB7000P_POWER_ANALOG_ADC: + /* dem, cfg, iqc, sad, agc */ + reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9)); + /* nud */ + reg_776 &= ~((1 << 0)); + /* Dout */ + if (state->version != SOC7090) + reg_1280 &= ~((1 << 11)); + reg_1280 &= ~(1 << 6); + fallthrough; + case DIB7000P_POWER_INTERFACE_ONLY: + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ + /* TODO power up either SDIO or I2C */ + if (state->version == SOC7090) + reg_1280 &= ~((1 << 7) | (1 << 5)); + else + reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)); + break; + +/* TODO following stuff is just converted from the dib7000-driver - check when is used what */ + } + + dib7000p_write_word(state, 774, reg_774); + dib7000p_write_word(state, 775, reg_775); + dib7000p_write_word(state, 776, reg_776); + dib7000p_write_word(state, 1280, reg_1280); + if (state->version != SOC7090) + dib7000p_write_word(state, 899, reg_899); + + return 0; +} + +static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no) +{ + u16 reg_908 = 0, reg_909 = 0; + u16 reg; + + if (state->version != SOC7090) { + reg_908 = dib7000p_read_word(state, 908); + reg_909 = dib7000p_read_word(state, 909); + } + + switch (no) { + case DIBX000_SLOW_ADC_ON: + if (state->version == SOC7090) { + reg = dib7000p_read_word(state, 1925); + + dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */ + + reg = dib7000p_read_word(state, 1925); /* read access to make it works... strange ... */ + msleep(200); + dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */ + + reg = dib7000p_read_word(state, 72) & ~((0x3 << 14) | (0x3 << 12)); + dib7000p_write_word(state, 72, reg | (1 << 14) | (3 << 12) | 524); /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; (Vin2 = Vcm) */ + } else { + reg_909 |= (1 << 1) | (1 << 0); + dib7000p_write_word(state, 909, reg_909); + reg_909 &= ~(1 << 1); + } + break; + + case DIBX000_SLOW_ADC_OFF: + if (state->version == SOC7090) { + reg = dib7000p_read_word(state, 1925); + dib7000p_write_word(state, 1925, (reg & ~(1 << 2)) | (1 << 4)); /* reset_sladc = 1 en_slowAdc = 0 */ + } else + reg_909 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + reg_908 &= 0x0fff; + reg_909 &= 0x0003; + break; + + case DIBX000_ADC_OFF: + reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_908 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_908 |= (1 << 15); + break; + + default: + break; + } + +// dprintk( "908: %x, 909: %x\n", reg_908, reg_909); + + reg_909 |= (state->cfg.disable_sample_and_hold & 1) << 4; + reg_908 |= (state->cfg.enable_current_mirror & 1) << 7; + + if (state->version != SOC7090) { + dib7000p_write_word(state, 908, reg_908); + dib7000p_write_word(state, 909, reg_909); + } +} + +static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw) +{ + u32 timf; + + // store the current bandwidth for later use + state->current_bandwidth = bw; + + if (state->timf == 0) { + dprintk("using default timf\n"); + timf = state->cfg.bw->timf; + } else { + dprintk("using updated timf\n"); + timf = state->timf; + } + + timf = timf * (bw / 50) / 160; + + dib7000p_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); + dib7000p_write_word(state, 24, (u16) ((timf) & 0xffff)); + + return 0; +} + +static int dib7000p_sad_calib(struct dib7000p_state *state) +{ +/* internal */ + dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); + + if (state->version == SOC7090) + dib7000p_write_word(state, 74, 2048); + else + dib7000p_write_word(state, 74, 776); + + /* do the calibration */ + dib7000p_write_word(state, 73, (1 << 0)); + dib7000p_write_word(state, 73, (0 << 0)); + + msleep(1); + + return 0; +} + +static int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) +{ + struct dib7000p_state *state = demod->demodulator_priv; + if (value > 4095) + value = 4095; + state->wbd_ref = value; + return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value); +} + +static int dib7000p_get_agc_values(struct dvb_frontend *fe, + u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) +{ + struct dib7000p_state *state = fe->demodulator_priv; + + if (agc_global != NULL) + *agc_global = dib7000p_read_word(state, 394); + if (agc1 != NULL) + *agc1 = dib7000p_read_word(state, 392); + if (agc2 != NULL) + *agc2 = dib7000p_read_word(state, 393); + if (wbd != NULL) + *wbd = dib7000p_read_word(state, 397); + + return 0; +} + +static int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) +{ + struct dib7000p_state *state = fe->demodulator_priv; + return dib7000p_write_word(state, 108, v); +} + +static void dib7000p_reset_pll(struct dib7000p_state *state) +{ + struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; + u16 clk_cfg0; + + if (state->version == SOC7090) { + dib7000p_write_word(state, 1856, (!bw->pll_reset << 13) | (bw->pll_range << 12) | (bw->pll_ratio << 6) | (bw->pll_prediv)); + + while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) + ; + + dib7000p_write_word(state, 1857, dib7000p_read_word(state, 1857) | (!bw->pll_bypass << 15)); + } else { + /* force PLL bypass */ + clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) | + (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0); + + dib7000p_write_word(state, 900, clk_cfg0); + + /* P_pll_cfg */ + dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); + clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff); + dib7000p_write_word(state, 900, clk_cfg0); + } + + dib7000p_write_word(state, 18, (u16) (((bw->internal * 1000) >> 16) & 0xffff)); + dib7000p_write_word(state, 19, (u16) ((bw->internal * 1000) & 0xffff)); + dib7000p_write_word(state, 21, (u16) ((bw->ifreq >> 16) & 0xffff)); + dib7000p_write_word(state, 22, (u16) ((bw->ifreq) & 0xffff)); + + dib7000p_write_word(state, 72, bw->sad_cfg); +} + +static u32 dib7000p_get_internal_freq(struct dib7000p_state *state) +{ + u32 internal = (u32) dib7000p_read_word(state, 18) << 16; + internal |= (u32) dib7000p_read_word(state, 19); + internal /= 1000; + + return internal; +} + +static int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856); + u8 loopdiv, prediv; + u32 internal, xtal; + + /* get back old values */ + prediv = reg_1856 & 0x3f; + loopdiv = (reg_1856 >> 6) & 0x3f; + + if (loopdiv && bw && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) { + dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)\n", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio); + reg_1856 &= 0xf000; + reg_1857 = dib7000p_read_word(state, 1857); + dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15)); + + dib7000p_write_word(state, 1856, reg_1856 | ((bw->pll_ratio & 0x3f) << 6) | (bw->pll_prediv & 0x3f)); + + /* write new system clk into P_sec_len */ + internal = dib7000p_get_internal_freq(state); + xtal = (internal / loopdiv) * prediv; + internal = 1000 * (xtal / bw->pll_prediv) * bw->pll_ratio; /* new internal */ + dib7000p_write_word(state, 18, (u16) ((internal >> 16) & 0xffff)); + dib7000p_write_word(state, 19, (u16) (internal & 0xffff)); + + dib7000p_write_word(state, 1857, reg_1857 | (1 << 15)); + + while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) + dprintk("Waiting for PLL to lock\n"); + + return 0; + } + return -EIO; +} + +static int dib7000p_reset_gpio(struct dib7000p_state *st) +{ + /* reset the GPIOs */ + dprintk("gpio dir: %x: val: %x, pwm_pos: %x\n", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos); + + dib7000p_write_word(st, 1029, st->gpio_dir); + dib7000p_write_word(st, 1030, st->gpio_val); + + /* TODO 1031 is P_gpio_od */ + + dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos); + + dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div); + return 0; +} + +static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val) +{ + st->gpio_dir = dib7000p_read_word(st, 1029); + st->gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + dib7000p_write_word(st, 1029, st->gpio_dir); + + st->gpio_val = dib7000p_read_word(st, 1030); + st->gpio_val &= ~(1 << num); /* reset the direction bit */ + st->gpio_val |= (val & 0x01) << num; /* set the new value */ + dib7000p_write_word(st, 1030, st->gpio_val); + + return 0; +} + +static int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val) +{ + struct dib7000p_state *state = demod->demodulator_priv; + return dib7000p_cfg_gpio(state, num, dir, val); +} + +static u16 dib7000p_defaults[] = { + // auto search configuration + 3, 2, + 0x0004, + (1<<3)|(1<<11)|(1<<12)|(1<<13), + 0x0814, /* Equal Lock */ + + 12, 6, + 0x001b, + 0x7740, + 0x005b, + 0x8d80, + 0x01c9, + 0xc380, + 0x0000, + 0x0080, + 0x0000, + 0x0090, + 0x0001, + 0xd4c0, + + 1, 26, + 0x6680, + + /* set ADC level to -16 */ + 11, 79, + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117, + + 1, 142, + 0x0410, + + /* disable power smoothing */ + 8, 145, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + 1, 154, + 1 << 13, + + 1, 168, + 0x0ccd, + + 1, 183, + 0x200f, + + 1, 212, + 0x169, + + 5, 187, + 0x023d, + 0x00a4, + 0x00a4, + 0x7ff0, + 0x3ccc, + + 1, 198, + 0x800, + + 1, 222, + 0x0010, + + 1, 235, + 0x0062, + + 0, +}; + +static void dib7000p_reset_stats(struct dvb_frontend *fe); + +static int dib7000p_demod_reset(struct dib7000p_state *state) +{ + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + + if (state->version == SOC7090) + dibx000_reset_i2c_master(&state->i2c_master); + + dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib7000p_write_word(state, 770, 0xffff); + dib7000p_write_word(state, 771, 0xffff); + dib7000p_write_word(state, 772, 0x001f); + dib7000p_write_word(state, 1280, 0x001f - ((1 << 4) | (1 << 3))); + + dib7000p_write_word(state, 770, 0); + dib7000p_write_word(state, 771, 0); + dib7000p_write_word(state, 772, 0); + dib7000p_write_word(state, 1280, 0); + + if (state->version != SOC7090) { + dib7000p_write_word(state, 898, 0x0003); + dib7000p_write_word(state, 898, 0); + } + + /* default */ + dib7000p_reset_pll(state); + + if (dib7000p_reset_gpio(state) != 0) + dprintk("GPIO reset was not successful.\n"); + + if (state->version == SOC7090) { + dib7000p_write_word(state, 899, 0); + + /* impulse noise */ + dib7000p_write_word(state, 42, (1<<5) | 3); /* P_iqc_thsat_ipc = 1 ; P_iqc_win2 = 3 */ + dib7000p_write_word(state, 43, 0x2d4); /*-300 fag P_iqc_dect_min = -280 */ + dib7000p_write_word(state, 44, 300); /* 300 fag P_iqc_dect_min = +280 */ + dib7000p_write_word(state, 273, (0<<6) | 30); + } + if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("OUTPUT_MODE could not be reset.\n"); + + dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib7000p_sad_calib(state); + dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1)); + + dib7000p_set_bandwidth(state, 8000); + + if (state->version == SOC7090) { + dib7000p_write_word(state, 36, 0x0755);/* P_iqc_impnc_on =1 & P_iqc_corr_inh = 1 for impulsive noise */ + } else { + if (state->cfg.tuner_is_baseband) + dib7000p_write_word(state, 36, 0x0755); + else + dib7000p_write_word(state, 36, 0x1f55); + } + + dib7000p_write_tab(state, dib7000p_defaults); + if (state->version != SOC7090) { + dib7000p_write_word(state, 901, 0x0006); + dib7000p_write_word(state, 902, (3 << 10) | (1 << 6)); + dib7000p_write_word(state, 905, 0x2c8e); + } + + dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) +{ + u16 tmp = 0; + tmp = dib7000p_read_word(state, 903); + dib7000p_write_word(state, 903, (tmp | 0x1)); + tmp = dib7000p_read_word(state, 900); + dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); +} + +static void dib7000p_restart_agc(struct dib7000p_state *state) +{ + // P_restart_iqc & P_restart_agc + dib7000p_write_word(state, 770, (1 << 11) | (1 << 9)); + dib7000p_write_word(state, 770, 0x0000); +} + +static int dib7000p_update_lna(struct dib7000p_state *state) +{ + u16 dyn_gain; + + if (state->cfg.update_lna) { + dyn_gain = dib7000p_read_word(state, 394); + if (state->cfg.update_lna(&state->demod, dyn_gain)) { + dib7000p_restart_agc(state); + return 1; + } + } + + return 0; +} + +static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + if (state->current_band == band && state->current_agc != NULL) + return 0; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("no valid AGC configuration found for band 0x%02x\n", band); + return -EINVAL; + } + + state->current_agc = agc; + + /* AGC */ + dib7000p_write_word(state, 75, agc->setup); + dib7000p_write_word(state, 76, agc->inv_gain); + dib7000p_write_word(state, 77, agc->time_stabiliz); + dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp); + dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); + + /* AGC continued */ + dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + if (state->wbd_ref != 0) + dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref); + else + dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref); + + dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); + + dib7000p_write_word(state, 107, agc->agc1_max); + dib7000p_write_word(state, 108, agc->agc1_min); + dib7000p_write_word(state, 109, agc->agc2_max); + dib7000p_write_word(state, 110, agc->agc2_min); + dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib7000p_write_word(state, 112, agc->agc1_pt3); + dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + return 0; +} + +static int dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) +{ + u32 internal = dib7000p_get_internal_freq(state); + s32 unit_khz_dds_val; + u32 abs_offset_khz = abs(offset_khz); + u32 dds = state->cfg.bw->ifreq & 0x1ffffff; + u8 invert = !!(state->cfg.bw->ifreq & (1 << 25)); + if (internal == 0) { + pr_warn("DIB7000P: dib7000p_get_internal_freq returned 0\n"); + return -1; + } + /* 2**26 / Fsampling is the unit 1KHz offset */ + unit_khz_dds_val = 67108864 / (internal); + + dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d\n", offset_khz, internal, invert); + + if (offset_khz < 0) + unit_khz_dds_val *= -1; + + /* IF tuner */ + if (invert) + dds -= (abs_offset_khz * unit_khz_dds_val); /* /100 because of /100 on the unit_khz_dds_val line calc for better accuracy */ + else + dds += (abs_offset_khz * unit_khz_dds_val); + + if (abs_offset_khz <= (internal / 2)) { /* Max dds offset is the half of the demod freq */ + dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9))); + dib7000p_write_word(state, 22, (u16) (dds & 0xffff)); + } + return 0; +} + +static int dib7000p_agc_startup(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib7000p_state *state = demod->demodulator_priv; + int ret = -1; + u8 *agc_state = &state->agc_state; + u8 agc_split; + u16 reg; + u32 upd_demod_gain_period = 0x1000; + s32 frequency_offset = 0; + + switch (state->agc_state) { + case 0: + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + if (state->version == SOC7090) { + reg = dib7000p_read_word(state, 0x79b) & 0xff00; + dib7000p_write_word(state, 0x79a, upd_demod_gain_period & 0xFFFF); /* lsb */ + dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF)); + + /* enable adc i & q */ + reg = dib7000p_read_word(state, 0x780); + dib7000p_write_word(state, 0x780, (reg | (0x3)) & (~(1 << 7))); + } else { + dib7000p_set_adc_state(state, DIBX000_ADC_ON); + dib7000p_pll_clk_cfg(state); + } + + if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0) + return -1; + + if (demod->ops.tuner_ops.get_frequency) { + u32 frequency_tuner; + + demod->ops.tuner_ops.get_frequency(demod, &frequency_tuner); + frequency_offset = (s32)frequency_tuner / 1000 - ch->frequency / 1000; + } + + if (dib7000p_set_dds(state, frequency_offset) < 0) + return -1; + + ret = 7; + (*agc_state)++; + break; + + case 1: + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); + + dib7000p_write_word(state, 78, 32768); + if (!state->current_agc->perform_agc_softsplit) { + /* we are using the wbd - so slow AGC startup */ + /* force 0 split on WBD and restart AGC */ + dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8)); + (*agc_state)++; + ret = 5; + } else { + /* default AGC startup */ + (*agc_state) = 4; + /* wait AGC rough lock time */ + ret = 7; + } + + dib7000p_restart_agc(state); + break; + + case 2: /* fast split search path after 5sec */ + dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */ + dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */ + (*agc_state)++; + ret = 14; + break; + + case 3: /* split search ended */ + agc_split = (u8) dib7000p_read_word(state, 396); /* store the split value for the next time */ + dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */ + + dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */ + dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ + + dib7000p_restart_agc(state); + + dprintk("SPLIT %p: %u\n", demod, agc_split); + + (*agc_state)++; + ret = 5; + break; + + case 4: /* LNA startup */ + ret = 7; + + if (dib7000p_update_lna(state)) + ret = 5; + else + (*agc_state)++; + break; + + case 5: + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); + (*agc_state)++; + break; + default: + break; + } + return ret; +} + +static void dib7000p_update_timf(struct dib7000p_state *state) +{ + u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428); + state->timf = timf * 160 / (state->current_bandwidth / 50); + dib7000p_write_word(state, 23, (u16) (timf >> 16)); + dib7000p_write_word(state, 24, (u16) (timf & 0xffff)); + dprintk("updated timf_frequency: %d (default: %d)\n", state->timf, state->cfg.bw->timf); + +} + +static u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) +{ + struct dib7000p_state *state = fe->demodulator_priv; + switch (op) { + case DEMOD_TIMF_SET: + state->timf = timf; + break; + case DEMOD_TIMF_UPDATE: + dib7000p_update_timf(state); + break; + case DEMOD_TIMF_GET: + break; + } + dib7000p_set_bandwidth(state, state->current_bandwidth); + return state->timf; +} + +static void dib7000p_set_channel(struct dib7000p_state *state, + struct dtv_frontend_properties *ch, u8 seq) +{ + u16 value, est[4]; + + dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); + + /* nfft, guard, qam, alpha */ + value = 0; + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: + value |= (0 << 7); + break; + case TRANSMISSION_MODE_4K: + value |= (2 << 7); + break; + default: + case TRANSMISSION_MODE_8K: + value |= (1 << 7); + break; + } + switch (ch->guard_interval) { + case GUARD_INTERVAL_1_32: + value |= (0 << 5); + break; + case GUARD_INTERVAL_1_16: + value |= (1 << 5); + break; + case GUARD_INTERVAL_1_4: + value |= (3 << 5); + break; + default: + case GUARD_INTERVAL_1_8: + value |= (2 << 5); + break; + } + switch (ch->modulation) { + case QPSK: + value |= (0 << 3); + break; + case QAM_16: + value |= (1 << 3); + break; + default: + case QAM_64: + value |= (2 << 3); + break; + } + switch (HIERARCHY_1) { + case HIERARCHY_2: + value |= 2; + break; + case HIERARCHY_4: + value |= 4; + break; + default: + case HIERARCHY_1: + value |= 1; + break; + } + dib7000p_write_word(state, 0, value); + dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ + + /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ + value = 0; + if (1 != 0) + value |= (1 << 6); + if (ch->hierarchy == 1) + value |= (1 << 4); + if (1 == 1) + value |= 1; + switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { + case FEC_2_3: + value |= (2 << 1); + break; + case FEC_3_4: + value |= (3 << 1); + break; + case FEC_5_6: + value |= (5 << 1); + break; + case FEC_7_8: + value |= (7 << 1); + break; + default: + case FEC_1_2: + value |= (1 << 1); + break; + } + dib7000p_write_word(state, 208, value); + + /* offset loop parameters */ + dib7000p_write_word(state, 26, 0x6680); + dib7000p_write_word(state, 32, 0x0003); + dib7000p_write_word(state, 29, 0x1273); + dib7000p_write_word(state, 33, 0x0005); + + /* P_dvsy_sync_wait */ + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_8K: + value = 256; + break; + case TRANSMISSION_MODE_4K: + value = 128; + break; + case TRANSMISSION_MODE_2K: + default: + value = 64; + break; + } + switch (ch->guard_interval) { + case GUARD_INTERVAL_1_16: + value *= 2; + break; + case GUARD_INTERVAL_1_8: + value *= 4; + break; + case GUARD_INTERVAL_1_4: + value *= 8; + break; + default: + case GUARD_INTERVAL_1_32: + value *= 1; + break; + } + if (state->cfg.diversity_delay == 0) + state->div_sync_wait = (value * 3) / 2 + 48; + else + state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; + + /* deactivate the possibility of diversity reception if extended interleaver */ + state->div_force_off = !1 && ch->transmission_mode != TRANSMISSION_MODE_8K; + dib7000p_set_diversity_in(&state->demod, state->div_state); + + /* channel estimation fine configuration */ + switch (ch->modulation) { + case QAM_64: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case QAM_16: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (value = 0; value < 4; value++) + dib7000p_write_word(state, 187 + value, est[value]); +} + +static int dib7000p_autosearch_start(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib7000p_state *state = demod->demodulator_priv; + struct dtv_frontend_properties schan; + u32 value, factor; + u32 internal = dib7000p_get_internal_freq(state); + + schan = *ch; + schan.modulation = QAM_64; + schan.guard_interval = GUARD_INTERVAL_1_32; + schan.transmission_mode = TRANSMISSION_MODE_8K; + schan.code_rate_HP = FEC_2_3; + schan.code_rate_LP = FEC_3_4; + schan.hierarchy = 0; + + dib7000p_set_channel(state, &schan, 7); + + factor = BANDWIDTH_TO_KHZ(ch->bandwidth_hz); + if (factor >= 5000) { + if (state->version == SOC7090) + factor = 2; + else + factor = 1; + } else + factor = 6; + + value = 30 * internal * factor; + dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); + dib7000p_write_word(state, 7, (u16) (value & 0xffff)); + value = 100 * internal * factor; + dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); + dib7000p_write_word(state, 9, (u16) (value & 0xffff)); + value = 500 * internal * factor; + dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); + dib7000p_write_word(state, 11, (u16) (value & 0xffff)); + + value = dib7000p_read_word(state, 0); + dib7000p_write_word(state, 0, (u16) ((1 << 9) | value)); + dib7000p_read_word(state, 1284); + dib7000p_write_word(state, 0, (u16) value); + + return 0; +} + +static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u16 irq_pending = dib7000p_read_word(state, 1284); + + if (irq_pending & 0x1) + return 1; + + if (irq_pending & 0x2) + return 2; + + return 0; +} + +static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw) +{ + static const s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 }; + static const u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, + 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, + 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, + 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105, + 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126, + 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146, + 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165, + 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, + 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235, + 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, + 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249, + 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, + 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 + }; + + u32 xtal = state->cfg.bw->xtal_hz / 1000; + int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz; + int k; + int coef_re[8], coef_im[8]; + int bw_khz = bw; + u32 pha; + + dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)\n", f_rel, rf_khz, xtal); + + if (f_rel < -bw_khz / 2 || f_rel > bw_khz / 2) + return; + + bw_khz /= 100; + + dib7000p_write_word(state, 142, 0x0610); + + for (k = 0; k < 8; k++) { + pha = ((f_rel * (k + 1) * 112 * 80 / bw_khz) / 1000) & 0x3ff; + + if (pha == 0) { + coef_re[k] = 256; + coef_im[k] = 0; + } else if (pha < 256) { + coef_re[k] = sine[256 - (pha & 0xff)]; + coef_im[k] = sine[pha & 0xff]; + } else if (pha == 256) { + coef_re[k] = 0; + coef_im[k] = 256; + } else if (pha < 512) { + coef_re[k] = -sine[pha & 0xff]; + coef_im[k] = sine[256 - (pha & 0xff)]; + } else if (pha == 512) { + coef_re[k] = -256; + coef_im[k] = 0; + } else if (pha < 768) { + coef_re[k] = -sine[256 - (pha & 0xff)]; + coef_im[k] = -sine[pha & 0xff]; + } else if (pha == 768) { + coef_re[k] = 0; + coef_im[k] = -256; + } else { + coef_re[k] = sine[pha & 0xff]; + coef_im[k] = -sine[256 - (pha & 0xff)]; + } + + coef_re[k] *= notch[k]; + coef_re[k] += (1 << 14); + if (coef_re[k] >= (1 << 24)) + coef_re[k] = (1 << 24) - 1; + coef_re[k] /= (1 << 15); + + coef_im[k] *= notch[k]; + coef_im[k] += (1 << 14); + if (coef_im[k] >= (1 << 24)) + coef_im[k] = (1 << 24) - 1; + coef_im[k] /= (1 << 15); + + dprintk("PALF COEF: %d re: %d im: %d\n", k, coef_re[k], coef_im[k]); + + dib7000p_write_word(state, 143, (0 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); + dib7000p_write_word(state, 144, coef_im[k] & 0x3ff); + dib7000p_write_word(state, 143, (1 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); + } + dib7000p_write_word(state, 143, 0); +} + +static int dib7000p_tune(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *ch = &demod->dtv_property_cache; + struct dib7000p_state *state = demod->demodulator_priv; + u16 tmp = 0; + + if (ch != NULL) + dib7000p_set_channel(state, ch, 0); + else + return -EINVAL; + + // restart demod + dib7000p_write_word(state, 770, 0x4000); + dib7000p_write_word(state, 770, 0x0000); + msleep(45); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3); + if (state->sfn_workaround_active) { + dprintk("SFN workaround is active\n"); + tmp |= (1 << 9); + dib7000p_write_word(state, 166, 0x4000); + } else { + dib7000p_write_word(state, 166, 0x0000); + } + dib7000p_write_word(state, 29, tmp); + + // never achieved a lock with that bandwidth so far - wait for osc-freq to update + if (state->timf == 0) + msleep(200); + + /* offset loop parameters */ + + /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ + tmp = (6 << 8) | 0x80; + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: + tmp |= (2 << 12); + break; + case TRANSMISSION_MODE_4K: + tmp |= (3 << 12); + break; + default: + case TRANSMISSION_MODE_8K: + tmp |= (4 << 12); + break; + } + dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ + tmp = (0 << 4); + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: + tmp |= 0x6; + break; + case TRANSMISSION_MODE_4K: + tmp |= 0x7; + break; + default: + case TRANSMISSION_MODE_8K: + tmp |= 0x8; + break; + } + dib7000p_write_word(state, 32, tmp); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ + tmp = (0 << 4); + switch (ch->transmission_mode) { + case TRANSMISSION_MODE_2K: + tmp |= 0x6; + break; + case TRANSMISSION_MODE_4K: + tmp |= 0x7; + break; + default: + case TRANSMISSION_MODE_8K: + tmp |= 0x8; + break; + } + dib7000p_write_word(state, 33, tmp); + + tmp = dib7000p_read_word(state, 509); + if (!((tmp >> 6) & 0x1)) { + /* restart the fec */ + tmp = dib7000p_read_word(state, 771); + dib7000p_write_word(state, 771, tmp | (1 << 1)); + dib7000p_write_word(state, 771, tmp); + msleep(40); + tmp = dib7000p_read_word(state, 509); + } + // we achieved a lock - it's time to update the osc freq + if ((tmp >> 6) & 0x1) { + dib7000p_update_timf(state); + /* P_timf_alpha += 2 */ + tmp = dib7000p_read_word(state, 26); + dib7000p_write_word(state, 26, (tmp & ~(0xf << 12)) | ((((tmp >> 12) & 0xf) + 5) << 12)); + } + + if (state->cfg.spur_protect) + dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); + + dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); + + dib7000p_reset_stats(demod); + + return 0; +} + +static int dib7000p_wakeup(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); + if (state->version == SOC7090) + dib7000p_sad_calib(state); + return 0; +} + +static int dib7000p_sleep(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + if (state->version == SOC7090) + return dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); + return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); +} + +static int dib7000p_identify(struct dib7000p_state *st) +{ + u16 value; + dprintk("checking demod on I2C address: %d (%x)\n", st->i2c_addr, st->i2c_addr); + + if ((value = dib7000p_read_word(st, 768)) != 0x01b3) { + dprintk("wrong Vendor ID (read=0x%x)\n", value); + return -EREMOTEIO; + } + + if ((value = dib7000p_read_word(st, 769)) != 0x4000) { + dprintk("wrong Device ID (%x)\n", value); + return -EREMOTEIO; + } + + return 0; +} + +static int dib7000p_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *fep) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 tps = dib7000p_read_word(state, 463); + + fep->inversion = INVERSION_AUTO; + + fep->bandwidth_hz = BANDWIDTH_TO_HZ(state->current_bandwidth); + + switch ((tps >> 8) & 0x3) { + case 0: + fep->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + fep->transmission_mode = TRANSMISSION_MODE_8K; + break; + /* case 2: fep->transmission_mode = TRANSMISSION_MODE_4K; break; */ + } + + switch (tps & 0x3) { + case 0: + fep->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + fep->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + fep->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + fep->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((tps >> 14) & 0x3) { + case 0: + fep->modulation = QPSK; + break; + case 1: + fep->modulation = QAM_16; + break; + case 2: + default: + fep->modulation = QAM_64; + break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ + + fep->hierarchy = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: + fep->code_rate_HP = FEC_1_2; + break; + case 2: + fep->code_rate_HP = FEC_2_3; + break; + case 3: + fep->code_rate_HP = FEC_3_4; + break; + case 5: + fep->code_rate_HP = FEC_5_6; + break; + case 7: + default: + fep->code_rate_HP = FEC_7_8; + break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: + fep->code_rate_LP = FEC_1_2; + break; + case 2: + fep->code_rate_LP = FEC_2_3; + break; + case 3: + fep->code_rate_LP = FEC_3_4; + break; + case 5: + fep->code_rate_LP = FEC_5_6; + break; + case 7: + default: + fep->code_rate_LP = FEC_7_8; + break; + } + + /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */ + + return 0; +} + +static int dib7000p_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct dib7000p_state *state = fe->demodulator_priv; + int time, ret; + + if (state->version == SOC7090) + dib7090_set_diversity_in(fe, 0); + else + dib7000p_set_output_mode(state, OUTMODE_HIGH_Z); + + /* maybe the parameter has been changed */ + state->sfn_workaround_active = buggy_sfn_workaround; + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* start up the AGC */ + state->agc_state = 0; + do { + time = dib7000p_agc_startup(fe); + if (time != -1) + msleep(time); + } while (time != -1); + + if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || + fep->guard_interval == GUARD_INTERVAL_AUTO || fep->modulation == QAM_AUTO || fep->code_rate_HP == FEC_AUTO) { + int i = 800, found; + + dib7000p_autosearch_start(fe); + do { + msleep(1); + found = dib7000p_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n", found); + if (found == 0 || found == 1) + return 0; + + dib7000p_get_frontend(fe, fep); + } + + ret = dib7000p_tune(fe); + + /* make this a config parameter */ + if (state->version == SOC7090) { + dib7090_set_output_mode(fe, state->cfg.output_mode); + if (state->cfg.enMpegOutput == 0) { + dib7090_setDibTxMux(state, MPEG_ON_DIBTX); + dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); + } + } else + dib7000p_set_output_mode(state, state->cfg.output_mode); + + return ret; +} + +static int dib7000p_get_stats(struct dvb_frontend *fe, enum fe_status stat); + +static int dib7000p_read_status(struct dvb_frontend *fe, enum fe_status *stat) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 lock = dib7000p_read_word(state, 509); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if ((lock & 0x0038) == 0x38) + *stat |= FE_HAS_LOCK; + + dib7000p_get_stats(fe, *stat); + + return 0; +} + +static int dib7000p_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct dib7000p_state *state = fe->demodulator_priv; + *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501); + return 0; +} + +static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct dib7000p_state *state = fe->demodulator_priv; + *unc = dib7000p_read_word(state, 506); + return 0; +} + +static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 394); + *strength = 65535 - val; + return 0; +} + +static u32 dib7000p_get_snr(struct dvb_frontend *fe) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val; + s32 signal_mant, signal_exp, noise_mant, noise_exp; + u32 result = 0; + + val = dib7000p_read_word(state, 479); + noise_mant = (val >> 4) & 0xff; + noise_exp = ((val & 0xf) << 2); + val = dib7000p_read_word(state, 480); + noise_exp += ((val >> 14) & 0x3); + if ((noise_exp & 0x20) != 0) + noise_exp -= 0x40; + + signal_mant = (val >> 6) & 0xFF; + signal_exp = (val & 0x3F); + if ((signal_exp & 0x20) != 0) + signal_exp -= 0x40; + + if (signal_mant != 0) + result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); + else + result = intlog10(2) * 10 * signal_exp - 100; + + if (noise_mant != 0) + result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); + else + result -= intlog10(2) * 10 * noise_exp - 100; + + return result; +} + +static int dib7000p_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + u32 result; + + result = dib7000p_get_snr(fe); + + *snr = result / ((1 << 24) / 10); + return 0; +} + +static void dib7000p_reset_stats(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + struct dtv_frontend_properties *c = &demod->dtv_property_cache; + u32 ucb; + + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->post_bit_error, 0, sizeof(c->post_bit_error)); + memset(&c->post_bit_count, 0, sizeof(c->post_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + + c->strength.len = 1; + c->cnr.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].uvalue = 0; + + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dib7000p_read_unc_blocks(demod, &ucb); + + state->old_ucb = ucb; + state->ber_jiffies_stats = 0; + state->per_jiffies_stats = 0; +} + +struct linear_segments { + unsigned x; + signed y; +}; + +/* + * Table to estimate signal strength in dBm. + * This table should be empirically determinated by measuring the signal + * strength generated by a RF generator directly connected into + * a device. + * This table was determinated by measuring the signal strength generated + * by a DTA-2111 RF generator directly connected into a dib7000p device + * (a Hauppauge Nova-TD stick), using a good quality 3 meters length + * RC6 cable and good RC6 connectors, connected directly to antenna 1. + * As the minimum output power of DTA-2111 is -31dBm, a 16 dBm attenuator + * were used, for the lower power values. + * The real value can actually be on other devices, or even at the + * second antena input, depending on several factors, like if LNA + * is enabled or not, if diversity is enabled, type of connectors, etc. + * Yet, it is better to use this measure in dB than a random non-linear + * percentage value, especially for antenna adjustments. + * On my tests, the precision of the measure using this table is about + * 0.5 dB, with sounds reasonable enough to adjust antennas. + */ +#define DB_OFFSET 131000 + +static struct linear_segments strength_to_db_table[] = { + { 63630, DB_OFFSET - 20500}, + { 62273, DB_OFFSET - 21000}, + { 60162, DB_OFFSET - 22000}, + { 58730, DB_OFFSET - 23000}, + { 58294, DB_OFFSET - 24000}, + { 57778, DB_OFFSET - 25000}, + { 57320, DB_OFFSET - 26000}, + { 56779, DB_OFFSET - 27000}, + { 56293, DB_OFFSET - 28000}, + { 55724, DB_OFFSET - 29000}, + { 55145, DB_OFFSET - 30000}, + { 54680, DB_OFFSET - 31000}, + { 54293, DB_OFFSET - 32000}, + { 53813, DB_OFFSET - 33000}, + { 53427, DB_OFFSET - 34000}, + { 52981, DB_OFFSET - 35000}, + + { 52636, DB_OFFSET - 36000}, + { 52014, DB_OFFSET - 37000}, + { 51674, DB_OFFSET - 38000}, + { 50692, DB_OFFSET - 39000}, + { 49824, DB_OFFSET - 40000}, + { 49052, DB_OFFSET - 41000}, + { 48436, DB_OFFSET - 42000}, + { 47836, DB_OFFSET - 43000}, + { 47368, DB_OFFSET - 44000}, + { 46468, DB_OFFSET - 45000}, + { 45597, DB_OFFSET - 46000}, + { 44586, DB_OFFSET - 47000}, + { 43667, DB_OFFSET - 48000}, + { 42673, DB_OFFSET - 49000}, + { 41816, DB_OFFSET - 50000}, + { 40876, DB_OFFSET - 51000}, + { 0, 0}, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx; + s32 dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i - 1].y - segments[i].y; + dx = segments[i - 1].x - segments[i].x; + + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y + tmp64; + + return ret; +} + +/* FIXME: may require changes - this one was borrowed from dib8000 */ +static u32 dib7000p_get_time_us(struct dvb_frontend *demod) +{ + struct dtv_frontend_properties *c = &demod->dtv_property_cache; + u64 time_us, tmp64; + u32 tmp, denom; + int guard, rate_num, rate_denum = 1, bits_per_symbol; + int interleaving = 0, fft_div; + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_4: + guard = 4; + break; + case GUARD_INTERVAL_1_8: + guard = 8; + break; + case GUARD_INTERVAL_1_16: + guard = 16; + break; + default: + case GUARD_INTERVAL_1_32: + guard = 32; + break; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + fft_div = 4; + break; + case TRANSMISSION_MODE_4K: + fft_div = 2; + break; + default: + case TRANSMISSION_MODE_8K: + fft_div = 1; + break; + } + + switch (c->modulation) { + case DQPSK: + case QPSK: + bits_per_symbol = 2; + break; + case QAM_16: + bits_per_symbol = 4; + break; + default: + case QAM_64: + bits_per_symbol = 6; + break; + } + + switch ((c->hierarchy == 0 || 1 == 1) ? c->code_rate_HP : c->code_rate_LP) { + case FEC_1_2: + rate_num = 1; + rate_denum = 2; + break; + case FEC_2_3: + rate_num = 2; + rate_denum = 3; + break; + case FEC_3_4: + rate_num = 3; + rate_denum = 4; + break; + case FEC_5_6: + rate_num = 5; + rate_denum = 6; + break; + default: + case FEC_7_8: + rate_num = 7; + rate_denum = 8; + break; + } + + denom = bits_per_symbol * rate_num * fft_div * 384; + + /* + * FIXME: check if the math makes sense. If so, fill the + * interleaving var. + */ + + /* If calculus gets wrong, wait for 1s for the next stats */ + if (!denom) + return 0; + + /* Estimate the period for the total bit rate */ + time_us = rate_denum * (1008 * 1562500L); + tmp64 = time_us; + do_div(tmp64, guard); + time_us = time_us + tmp64; + time_us += denom / 2; + do_div(time_us, denom); + + tmp = 1008 * 96 * interleaving; + time_us += tmp + tmp / guard; + + return time_us; +} + +static int dib7000p_get_stats(struct dvb_frontend *demod, enum fe_status stat) +{ + struct dib7000p_state *state = demod->demodulator_priv; + struct dtv_frontend_properties *c = &demod->dtv_property_cache; + int show_per_stats = 0; + u32 time_us = 0, val, snr; + u64 blocks, ucb; + s32 db; + u16 strength; + + /* Get Signal strength */ + dib7000p_read_signal_strength(demod, &strength); + val = strength; + db = interpolate_value(val, + strength_to_db_table, + ARRAY_SIZE(strength_to_db_table)) - DB_OFFSET; + c->strength.stat[0].svalue = db; + + /* UCB/BER/CNR measures require lock */ + if (!(stat & FE_HAS_LOCK)) { + c->cnr.len = 1; + c->block_count.len = 1; + c->block_error.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + /* Check if time for stats was elapsed */ + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); + + /* Get SNR */ + snr = dib7000p_get_snr(demod); + if (snr) + snr = (1000L * snr) >> 24; + else + snr = 0; + c->cnr.stat[0].svalue = snr; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + + /* Get UCB measures */ + dib7000p_read_unc_blocks(demod, &val); + ucb = val - state->old_ucb; + if (val < state->old_ucb) + ucb += 0x100000000LL; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = ucb; + + /* Estimate the number of packets based on bitrate */ + if (!time_us) + time_us = dib7000p_get_time_us(demod); + + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + + show_per_stats = 1; + } + + /* Get post-BER measures */ + if (time_after(jiffies, state->ber_jiffies_stats)) { + time_us = dib7000p_get_time_us(demod); + state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + + dprintk("Next all layers stats available in %u us.\n", time_us); + + dib7000p_read_ber(demod, &val); + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += val; + + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += 100000000; + } + + /* Get PER measures */ + if (show_per_stats) { + dib7000p_read_unc_blocks(demod, &val); + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += val; + + time_us = dib7000p_get_time_us(demod); + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + } + return 0; +} + +static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void dib7000p_release(struct dvb_frontend *demod) +{ + struct dib7000p_state *st = demod->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + i2c_del_adapter(&st->dib7090_tuner_adap); + kfree(st); +} + +static int dib7000pc_detection(struct i2c_adapter *i2c_adap) +{ + u8 *tx, *rx; + struct i2c_msg msg[2] = { + {.addr = 18 >> 1, .flags = 0, .len = 2}, + {.addr = 18 >> 1, .flags = I2C_M_RD, .len = 2}, + }; + int ret = 0; + + tx = kzalloc(2, GFP_KERNEL); + if (!tx) + return -ENOMEM; + rx = kzalloc(2, GFP_KERNEL); + if (!rx) { + ret = -ENOMEM; + goto rx_memory_error; + } + + msg[0].buf = tx; + msg[1].buf = rx; + + tx[0] = 0x03; + tx[1] = 0x00; + + if (i2c_transfer(i2c_adap, msg, 2) == 2) + if (rx[0] == 0x01 && rx[1] == 0xb3) { + dprintk("-D- DiB7000PC detected\n"); + ret = 1; + goto out; + } + + msg[0].addr = msg[1].addr = 0x40; + + if (i2c_transfer(i2c_adap, msg, 2) == 2) + if (rx[0] == 0x01 && rx[1] == 0xb3) { + dprintk("-D- DiB7000PC detected\n"); + ret = 1; + goto out; + } + + dprintk("-D- DiB7000PC not detected\n"); + +out: + kfree(rx); +rx_memory_error: + kfree(tx); + return ret; +} + +static struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +{ + struct dib7000p_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} + +static int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 235) & 0xffef; + val |= (onoff & 0x1) << 4; + dprintk("PID filter enabled %d\n", onoff); + return dib7000p_write_word(state, 235, val); +} + +static int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + dprintk("PID filter: index %x, PID %d, OnOff %d\n", id, pid, onoff); + return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); +} + +static int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) +{ + struct dib7000p_state *dpst; + int k = 0; + u8 new_addr = 0; + + dpst = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); + if (!dpst) + return -ENOMEM; + + dpst->i2c_adap = i2c; + mutex_init(&dpst->i2c_buffer_lock); + + for (k = no_of_demods - 1; k >= 0; k--) { + dpst->cfg = cfg[k]; + + /* designated i2c address */ + if (cfg[k].default_i2c_addr != 0) + new_addr = cfg[k].default_i2c_addr + (k << 1); + else + new_addr = (0x40 + k) << 1; + dpst->i2c_addr = new_addr; + dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ + if (dib7000p_identify(dpst) != 0) { + dpst->i2c_addr = default_addr; + dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ + if (dib7000p_identify(dpst) != 0) { + dprintk("DiB7000P #%d: not identified\n", k); + kfree(dpst); + return -EIO; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib7000p_set_output_mode(dpst, OUTMODE_DIVERSITY); + + /* set new i2c address and force divstart */ + dib7000p_write_word(dpst, 1285, (new_addr << 2) | 0x2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + dpst->cfg = cfg[k]; + if (cfg[k].default_i2c_addr != 0) + dpst->i2c_addr = (cfg[k].default_i2c_addr + k) << 1; + else + dpst->i2c_addr = (0x40 + k) << 1; + + // unforce divstr + dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib7000p_set_output_mode(dpst, OUTMODE_HIGH_Z); + } + + kfree(dpst); + return 0; +} + +static const s32 lut_1000ln_mant[] = { + 6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600 +}; + +static s32 dib7000p_get_adc_power(struct dvb_frontend *fe) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u32 tmp_val = 0, exp = 0, mant = 0; + s32 pow_i; + u16 buf[2]; + u8 ix = 0; + + buf[0] = dib7000p_read_word(state, 0x184); + buf[1] = dib7000p_read_word(state, 0x185); + pow_i = (buf[0] << 16) | buf[1]; + dprintk("raw pow_i = %d\n", pow_i); + + tmp_val = pow_i; + while (tmp_val >>= 1) + exp++; + + mant = (pow_i * 1000 / (1 << exp)); + dprintk(" mant = %d exp = %d\n", mant / 1000, exp); + + ix = (u8) ((mant - 1000) / 100); /* index of the LUT */ + dprintk(" ix = %d\n", ix); + + pow_i = (lut_1000ln_mant[ix] + 693 * (exp - 20) - 6908); + pow_i = (pow_i << 8) / 1000; + dprintk(" pow_i = %d\n", pow_i); + + return pow_i; +} + +static int map_addr_to_serpar_number(struct i2c_msg *msg) +{ + if ((msg->buf[0] <= 15)) + msg->buf[0] -= 1; + else if (msg->buf[0] == 17) + msg->buf[0] = 15; + else if (msg->buf[0] == 16) + msg->buf[0] = 17; + else if (msg->buf[0] == 19) + msg->buf[0] = 16; + else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) + msg->buf[0] -= 3; + else if (msg->buf[0] == 28) + msg->buf[0] = 23; + else + return -EINVAL; + return 0; +} + +static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + u8 n_overflow = 1; + u16 i = 1000; + u16 serpar_num = msg[0].buf[0]; + + while (n_overflow == 1 && i) { + n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; + i--; + if (i == 0) + dprintk("Tuner ITF: write busy (overflow)\n"); + } + dib7000p_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); + dib7000p_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); + + return num; +} + +static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + u8 n_overflow = 1, n_empty = 1; + u16 i = 1000; + u16 serpar_num = msg[0].buf[0]; + u16 read_word; + + while (n_overflow == 1 && i) { + n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; + i--; + if (i == 0) + dprintk("TunerITF: read busy (overflow)\n"); + } + dib7000p_write_word(state, 1985, (0 << 6) | (serpar_num & 0x3f)); + + i = 1000; + while (n_empty == 1 && i) { + n_empty = dib7000p_read_word(state, 1984) & 0x1; + i--; + if (i == 0) + dprintk("TunerITF: read busy (empty)\n"); + } + read_word = dib7000p_read_word(state, 1987); + msg[1].buf[0] = (read_word >> 8) & 0xff; + msg[1].buf[1] = (read_word) & 0xff; + + return num; +} + +static int w7090p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + if (map_addr_to_serpar_number(&msg[0]) == 0) { /* else = Tuner regs to ignore : DIG_CFG, CTRL_RF_LT, PLL_CFG, PWM1_REG, ADCCLK, DIG_CFG_3; SLEEP_EN... */ + if (num == 1) { /* write */ + return w7090p_tuner_write_serpar(i2c_adap, msg, 1); + } else { /* read */ + return w7090p_tuner_read_serpar(i2c_adap, msg, 2); + } + } + return num; +} + +static int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num, u16 apb_address) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + u16 word; + + if (num == 1) { /* write */ + dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); + } else { + word = dib7000p_read_word(state, apb_address); + msg[1].buf[0] = (word >> 8) & 0xff; + msg[1].buf[1] = (word) & 0xff; + } + + return num; +} + +static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); + + u16 apb_address = 0, word; + int i = 0; + switch (msg[0].buf[0]) { + case 0x12: + apb_address = 1920; + break; + case 0x14: + apb_address = 1921; + break; + case 0x24: + apb_address = 1922; + break; + case 0x1a: + apb_address = 1923; + break; + case 0x22: + apb_address = 1924; + break; + case 0x33: + apb_address = 1926; + break; + case 0x34: + apb_address = 1927; + break; + case 0x35: + apb_address = 1928; + break; + case 0x36: + apb_address = 1929; + break; + case 0x37: + apb_address = 1930; + break; + case 0x38: + apb_address = 1931; + break; + case 0x39: + apb_address = 1932; + break; + case 0x2a: + apb_address = 1935; + break; + case 0x2b: + apb_address = 1936; + break; + case 0x2c: + apb_address = 1937; + break; + case 0x2d: + apb_address = 1938; + break; + case 0x2e: + apb_address = 1939; + break; + case 0x2f: + apb_address = 1940; + break; + case 0x30: + apb_address = 1941; + break; + case 0x31: + apb_address = 1942; + break; + case 0x32: + apb_address = 1943; + break; + case 0x3e: + apb_address = 1944; + break; + case 0x3f: + apb_address = 1945; + break; + case 0x40: + apb_address = 1948; + break; + case 0x25: + apb_address = 914; + break; + case 0x26: + apb_address = 915; + break; + case 0x27: + apb_address = 917; + break; + case 0x28: + apb_address = 916; + break; + case 0x1d: + i = ((dib7000p_read_word(state, 72) >> 12) & 0x3); + word = dib7000p_read_word(state, 384 + i); + msg[1].buf[0] = (word >> 8) & 0xff; + msg[1].buf[1] = (word) & 0xff; + return num; + case 0x1f: + if (num == 1) { /* write */ + word = (u16) ((msg[0].buf[1] << 8) | msg[0].buf[2]); + word &= 0x3; + word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12); + dib7000p_write_word(state, 72, word); /* Set the proper input */ + return num; + } + } + + if (apb_address != 0) /* R/W access via APB */ + return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address); + else /* R/W access via SERPAR */ + return w7090p_tuner_rw_serpar(i2c_adap, msg, num); + + return 0; +} + +static u32 dib7000p_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm dib7090_tuner_xfer_algo = { + .master_xfer = dib7090_tuner_xfer, + .functionality = dib7000p_i2c_func, +}; + +static struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +{ + struct dib7000p_state *st = fe->demodulator_priv; + return &st->dib7090_tuner_adap; +} + +static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive) +{ + u16 reg; + + /* drive host bus 2, 3, 4 */ + reg = dib7000p_read_word(state, 1798) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); + reg |= (drive << 12) | (drive << 6) | drive; + dib7000p_write_word(state, 1798, reg); + + /* drive host bus 5,6 */ + reg = dib7000p_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); + reg |= (drive << 8) | (drive << 2); + dib7000p_write_word(state, 1799, reg); + + /* drive host bus 7, 8, 9 */ + reg = dib7000p_read_word(state, 1800) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); + reg |= (drive << 12) | (drive << 6) | drive; + dib7000p_write_word(state, 1800, reg); + + /* drive host bus 10, 11 */ + reg = dib7000p_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); + reg |= (drive << 8) | (drive << 2); + dib7000p_write_word(state, 1801, reg); + + /* drive host bus 12, 13, 14 */ + reg = dib7000p_read_word(state, 1802) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); + reg |= (drive << 12) | (drive << 6) | drive; + dib7000p_write_word(state, 1802, reg); + + return 0; +} + +static u32 dib7090_calcSyncFreq(u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 syncSize) +{ + u32 quantif = 3; + u32 nom = (insertExtSynchro * P_Kin + syncSize); + u32 denom = P_Kout; + u32 syncFreq = ((nom << quantif) / denom); + + if ((syncFreq & ((1 << quantif) - 1)) != 0) + syncFreq = (syncFreq >> quantif) + 1; + else + syncFreq = (syncFreq >> quantif); + + if (syncFreq != 0) + syncFreq = syncFreq - 1; + + return syncFreq; +} + +static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, u32 syncWord, u32 syncSize) +{ + dprintk("Configure DibStream Tx\n"); + + dib7000p_write_word(state, 1615, 1); + dib7000p_write_word(state, 1603, P_Kin); + dib7000p_write_word(state, 1605, P_Kout); + dib7000p_write_word(state, 1606, insertExtSynchro); + dib7000p_write_word(state, 1608, synchroMode); + dib7000p_write_word(state, 1609, (syncWord >> 16) & 0xffff); + dib7000p_write_word(state, 1610, syncWord & 0xffff); + dib7000p_write_word(state, 1612, syncSize); + dib7000p_write_word(state, 1615, 0); + + return 0; +} + +static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, u32 syncWord, u32 syncSize, + u32 dataOutRate) +{ + u32 syncFreq; + + dprintk("Configure DibStream Rx\n"); + if ((P_Kin != 0) && (P_Kout != 0)) { + syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize); + dib7000p_write_word(state, 1542, syncFreq); + } + dib7000p_write_word(state, 1554, 1); + dib7000p_write_word(state, 1536, P_Kin); + dib7000p_write_word(state, 1537, P_Kout); + dib7000p_write_word(state, 1539, synchroMode); + dib7000p_write_word(state, 1540, (syncWord >> 16) & 0xffff); + dib7000p_write_word(state, 1541, syncWord & 0xffff); + dib7000p_write_word(state, 1543, syncSize); + dib7000p_write_word(state, 1544, dataOutRate); + dib7000p_write_word(state, 1554, 0); + + return 0; +} + +static void dib7090_enMpegMux(struct dib7000p_state *state, int onoff) +{ + u16 reg_1287 = dib7000p_read_word(state, 1287); + + switch (onoff) { + case 1: + reg_1287 &= ~(1<<7); + break; + case 0: + reg_1287 |= (1<<7); + break; + } + + dib7000p_write_word(state, 1287, reg_1287); +} + +static void dib7090_configMpegMux(struct dib7000p_state *state, + u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) +{ + dprintk("Enable Mpeg mux\n"); + + dib7090_enMpegMux(state, 0); + + /* If the input mode is MPEG do not divide the serial clock */ + if ((enSerialMode == 1) && (state->input_mode_mpeg == 1)) + enSerialClkDiv2 = 0; + + dib7000p_write_word(state, 1287, ((pulseWidth & 0x1f) << 2) + | ((enSerialMode & 0x1) << 1) + | (enSerialClkDiv2 & 0x1)); + + dib7090_enMpegMux(state, 1); +} + +static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode) +{ + u16 reg_1288 = dib7000p_read_word(state, 1288) & ~(0x7 << 7); + + switch (mode) { + case MPEG_ON_DIBTX: + dprintk("SET MPEG ON DIBSTREAM TX\n"); + dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); + reg_1288 |= (1<<9); + break; + case DIV_ON_DIBTX: + dprintk("SET DIV_OUT ON DIBSTREAM TX\n"); + dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); + reg_1288 |= (1<<8); + break; + case ADC_ON_DIBTX: + dprintk("SET ADC_OUT ON DIBSTREAM TX\n"); + dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); + reg_1288 |= (1<<7); + break; + default: + break; + } + dib7000p_write_word(state, 1288, reg_1288); +} + +static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode) +{ + u16 reg_1288 = dib7000p_read_word(state, 1288) & ~(0x7 << 4); + + switch (mode) { + case DEMOUT_ON_HOSTBUS: + dprintk("SET DEM OUT OLD INTERF ON HOST BUS\n"); + dib7090_enMpegMux(state, 0); + reg_1288 |= (1<<6); + break; + case DIBTX_ON_HOSTBUS: + dprintk("SET DIBSTREAM TX ON HOST BUS\n"); + dib7090_enMpegMux(state, 0); + reg_1288 |= (1<<5); + break; + case MPEG_ON_HOSTBUS: + dprintk("SET MPEG MUX ON HOST BUS\n"); + reg_1288 |= (1<<4); + break; + default: + break; + } + dib7000p_write_word(state, 1288, reg_1288); +} + +static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 reg_1287; + + switch (onoff) { + case 0: /* only use the internal way - not the diversity input */ + dprintk("%s mode OFF : by default Enable Mpeg INPUT\n", __func__); + dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); + + /* Do not divide the serial clock of MPEG MUX */ + /* in SERIAL MODE in case input mode MPEG is used */ + reg_1287 = dib7000p_read_word(state, 1287); + /* enSerialClkDiv2 == 1 ? */ + if ((reg_1287 & 0x1) == 1) { + /* force enSerialClkDiv2 = 0 */ + reg_1287 &= ~0x1; + dib7000p_write_word(state, 1287, reg_1287); + } + state->input_mode_mpeg = 1; + break; + case 1: /* both ways */ + case 2: /* only the diversity input */ + dprintk("%s ON : Enable diversity INPUT\n", __func__); + dib7090_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); + state->input_mode_mpeg = 0; + break; + } + + dib7000p_set_diversity_in(&state->demod, onoff); + return 0; +} + +static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) +{ + struct dib7000p_state *state = fe->demodulator_priv; + + u16 outreg, smo_mode, fifo_threshold; + u8 prefer_mpeg_mux_use = 1; + int ret = 0; + + dib7090_host_bus_drive(state, 1); + + fifo_threshold = 1792; + smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); + outreg = dib7000p_read_word(state, 1286) & ~((1 << 10) | (0x7 << 6) | (1 << 1)); + + switch (mode) { + case OUTMODE_HIGH_Z: + outreg = 0; + break; + + case OUTMODE_MPEG2_SERIAL: + if (prefer_mpeg_mux_use) { + dprintk("setting output mode TS_SERIAL using Mpeg Mux\n"); + dib7090_configMpegMux(state, 3, 1, 1); + dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); + } else {/* Use Smooth block */ + dprintk("setting output mode TS_SERIAL using Smooth bloc\n"); + dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); + outreg |= (2<<6) | (0 << 1); + } + break; + + case OUTMODE_MPEG2_PAR_GATED_CLK: + if (prefer_mpeg_mux_use) { + dprintk("setting output mode TS_PARALLEL_GATED using Mpeg Mux\n"); + dib7090_configMpegMux(state, 2, 0, 0); + dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); + } else { /* Use Smooth block */ + dprintk("setting output mode TS_PARALLEL_GATED using Smooth block\n"); + dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); + outreg |= (0<<6); + } + break; + + case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ + dprintk("setting output mode TS_PARALLEL_CONT using Smooth block\n"); + dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); + outreg |= (1<<6); + break; + + case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */ + dprintk("setting output mode TS_FIFO using Smooth block\n"); + dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); + outreg |= (5<<6); + smo_mode |= (3 << 1); + fifo_threshold = 512; + break; + + case OUTMODE_DIVERSITY: + dprintk("setting output mode MODE_DIVERSITY\n"); + dib7090_setDibTxMux(state, DIV_ON_DIBTX); + dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); + break; + + case OUTMODE_ANALOG_ADC: + dprintk("setting output mode MODE_ANALOG_ADC\n"); + dib7090_setDibTxMux(state, ADC_ON_DIBTX); + dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); + break; + } + if (mode != OUTMODE_HIGH_Z) + outreg |= (1 << 10); + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + ret |= dib7000p_write_word(state, 235, smo_mode); + ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ + ret |= dib7000p_write_word(state, 1286, outreg); + + return ret; +} + +static int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 en_cur_state; + + dprintk("sleep dib7090: %d\n", onoff); + + en_cur_state = dib7000p_read_word(state, 1922); + + if (en_cur_state > 0xff) + state->tuner_enable = en_cur_state; + + if (onoff) + en_cur_state &= 0x00ff; + else { + if (state->tuner_enable != 0) + en_cur_state = state->tuner_enable; + } + + dib7000p_write_word(state, 1922, en_cur_state); + + return 0; +} + +static int dib7090_get_adc_power(struct dvb_frontend *fe) +{ + return dib7000p_get_adc_power(fe); +} + +static int dib7090_slave_reset(struct dvb_frontend *fe) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 reg; + + reg = dib7000p_read_word(state, 1794); + dib7000p_write_word(state, 1794, reg | (4 << 12)); + + dib7000p_write_word(state, 1032, 0xffff); + return 0; +} + +static const struct dvb_frontend_ops dib7000p_ops; +static struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +{ + struct dvb_frontend *demod; + struct dib7000p_state *st; + st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config)); + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + st->gpio_val = cfg->gpio_val; + st->gpio_dir = cfg->gpio_dir; + + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + st->cfg.output_mode = OUTMODE_MPEG2_FIFO; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); + mutex_init(&st->i2c_buffer_lock); + + dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ + + if (dib7000p_identify(st) != 0) + goto error; + + st->version = dib7000p_read_word(st, 897); + + /* FIXME: make sure the dev.parent field is initialized, or else + request_firmware() will hit an OOPS (this should be moved somewhere + more common) */ + st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; + + dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); + + /* init 7090 tuner adapter */ + strscpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface", + sizeof(st->dib7090_tuner_adap.name)); + st->dib7090_tuner_adap.algo = &dib7090_tuner_xfer_algo; + st->dib7090_tuner_adap.algo_data = NULL; + st->dib7090_tuner_adap.dev.parent = st->i2c_adap->dev.parent; + i2c_set_adapdata(&st->dib7090_tuner_adap, st); + i2c_add_adapter(&st->dib7090_tuner_adap); + + dib7000p_demod_reset(st); + + dib7000p_reset_stats(demod); + + if (st->version == SOC7090) { + dib7090_set_output_mode(demod, st->cfg.output_mode); + dib7090_set_diversity_in(demod, 0); + } + + return demod; + +error: + kfree(st); + return NULL; +} + +void *dib7000p_attach(struct dib7000p_ops *ops) +{ + if (!ops) + return NULL; + + ops->slave_reset = dib7090_slave_reset; + ops->get_adc_power = dib7090_get_adc_power; + ops->dib7000pc_detection = dib7000pc_detection; + ops->get_i2c_tuner = dib7090_get_i2c_tuner; + ops->tuner_sleep = dib7090_tuner_sleep; + ops->init = dib7000p_init; + ops->set_agc1_min = dib7000p_set_agc1_min; + ops->set_gpio = dib7000p_set_gpio; + ops->i2c_enumeration = dib7000p_i2c_enumeration; + ops->pid_filter = dib7000p_pid_filter; + ops->pid_filter_ctrl = dib7000p_pid_filter_ctrl; + ops->get_i2c_master = dib7000p_get_i2c_master; + ops->update_pll = dib7000p_update_pll; + ops->ctrl_timf = dib7000p_ctrl_timf; + ops->get_agc_values = dib7000p_get_agc_values; + ops->set_wbd_ref = dib7000p_set_wbd_ref; + + return ops; +} +EXPORT_SYMBOL_GPL(dib7000p_attach); + +static const struct dvb_frontend_ops dib7000p_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "DiBcom 7000PC", + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000p_release, + + .init = dib7000p_wakeup, + .sleep = dib7000p_sleep, + + .set_frontend = dib7000p_set_frontend, + .get_tune_settings = dib7000p_fe_get_tune_settings, + .get_frontend = dib7000p_get_frontend, + + .read_status = dib7000p_read_status, + .read_ber = dib7000p_read_ber, + .read_signal_strength = dib7000p_read_signal_strength, + .read_snr = dib7000p_read_snr, + .read_ucblocks = dib7000p_read_unc_blocks, +}; + +MODULE_AUTHOR("Olivier Grenie <olivie.grenie@parrot.com>"); +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h new file mode 100644 index 0000000000..2e10b5ccce --- /dev/null +++ b/drivers/media/dvb-frontends/dib7000p.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DIB7000P_H +#define DIB7000P_H + +#include "dibx000_common.h" + +struct dib7000p_config { + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + struct dibx000_bandwidth_config *bw; + +#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + u16 pwm_freq_div; + + u8 quartz_direct; + + u8 spur_protect; + + int (*agc_control) (struct dvb_frontend *, u8 before); + + u8 output_mode; + u8 disable_sample_and_hold:1; + + u8 enable_current_mirror:1; + u16 diversity_delay; + + u8 default_i2c_addr; + u8 enMpegOutput:1; +}; + +#define DEFAULT_DIB7000P_I2C_ADDRESS 18 + +struct dib7000p_ops { + int (*set_wbd_ref)(struct dvb_frontend *demod, u16 value); + int (*get_agc_values)(struct dvb_frontend *fe, + u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); + int (*set_agc1_min)(struct dvb_frontend *fe, u16 v); + int (*update_pll)(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw); + int (*set_gpio)(struct dvb_frontend *demod, u8 num, u8 dir, u8 val); + u32 (*ctrl_timf)(struct dvb_frontend *fe, u8 op, u32 timf); + int (*dib7000pc_detection)(struct i2c_adapter *i2c_adap); + struct i2c_adapter *(*get_i2c_master)(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating); + int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff); + int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); + int (*i2c_enumeration)(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); + struct i2c_adapter *(*get_i2c_tuner)(struct dvb_frontend *fe); + int (*tuner_sleep)(struct dvb_frontend *fe, int onoff); + int (*get_adc_power)(struct dvb_frontend *fe); + int (*slave_reset)(struct dvb_frontend *fe); + struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +}; + +#if IS_REACHABLE(CONFIG_DVB_DIB7000P) +void *dib7000p_attach(struct dib7000p_ops *ops); +#else +static inline void *dib7000p_attach(struct dib7000p_ops *ops) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c new file mode 100644 index 0000000000..2f51659181 --- /dev/null +++ b/drivers/media/dvb-frontends/dib8000.c @@ -0,0 +1,4534 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Linux-DVB Driver for DiBcom's DiB8000 chip (ISDB-T). + * + * Copyright (C) 2009 DiBcom (http://www.dibcom.fr/) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <asm/div64.h> + +#include <linux/int_log.h> + +#include <media/dvb_frontend.h> + +#include "dib8000.h" + +#define LAYER_ALL -1 +#define LAYER_A 1 +#define LAYER_B 2 +#define LAYER_C 3 + +#define MAX_NUMBER_OF_FRONTENDS 6 +/* #define DIB8000_AGC_FREEZE */ + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +struct i2c_device { + struct i2c_adapter *adap; + u8 addr; + u8 *i2c_write_buffer; + u8 *i2c_read_buffer; + struct mutex *i2c_buffer_lock; +}; + +enum param_loop_step { + LOOP_TUNE_1, + LOOP_TUNE_2 +}; + +enum dib8000_autosearch_step { + AS_START = 0, + AS_SEARCHING_FFT, + AS_SEARCHING_GUARD, + AS_DONE = 100, +}; + +enum timeout_mode { + SYMBOL_DEPENDENT_OFF = 0, + SYMBOL_DEPENDENT_ON, +}; + +struct dib8000_state { + struct dib8000_config cfg; + + struct i2c_device i2c; + + struct dibx000_i2c_master i2c_master; + + u16 wbd_ref; + + u8 current_band; + u32 current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + u32 timf_default; + + u8 div_force_off:1; + u8 div_state:1; + u16 div_sync_wait; + + u8 agc_state; + u8 differential_constellation; + u8 diversity_onoff; + + s16 ber_monitored_layer; + u16 gpio_dir; + u16 gpio_val; + + u16 revision; + u8 isdbt_cfg_loaded; + enum frontend_tune_state tune_state; + s32 status; + + struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; + + /* for the I2C transfer */ + struct i2c_msg msg[2]; + u8 i2c_write_buffer[4]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; + u8 input_mode_mpeg; + + u16 tuner_enable; + struct i2c_adapter dib8096p_tuner_adap; + u16 current_demod_bw; + + u16 seg_mask; + u16 seg_diff_mask; + u16 mode; + u8 layer_b_nb_seg; + u8 layer_c_nb_seg; + + u8 channel_parameters_set; + u16 autosearch_state; + u16 found_nfft; + u16 found_guard; + u8 subchannel; + u8 symbol_duration; + unsigned long timeout; + u8 longest_intlv_layer; + u16 output_mode; + + /* for DVBv5 stats */ + s64 init_ucb; + unsigned long per_jiffies_stats; + unsigned long ber_jiffies_stats; + unsigned long ber_jiffies_stats_layer[3]; + +#ifdef DIB8000_AGC_FREEZE + u16 agc1_max; + u16 agc1_min; + u16 agc2_max; + u16 agc2_min; +#endif +}; + +enum dib8000_power_mode { + DIB8000_POWER_ALL = 0, + DIB8000_POWER_INTERFACE_ONLY, +}; + +static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) +{ + u16 ret; + struct i2c_msg msg[2] = { + {.addr = i2c->addr >> 1, .flags = 0, .len = 2}, + {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2}, + }; + + if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + msg[0].buf = i2c->i2c_write_buffer; + msg[0].buf[0] = reg >> 8; + msg[0].buf[1] = reg & 0xff; + msg[1].buf = i2c->i2c_read_buffer; + + if (i2c_transfer(i2c->adap, msg, 2) != 2) + dprintk("i2c read error on %d\n", reg); + + ret = (msg[1].buf[0] << 8) | msg[1].buf[1]; + mutex_unlock(i2c->i2c_buffer_lock); + return ret; +} + +static u16 __dib8000_read_word(struct dib8000_state *state, u16 reg) +{ + u16 ret; + + state->i2c_write_buffer[0] = reg >> 8; + state->i2c_write_buffer[1] = reg & 0xff; + + memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c.addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 2; + state->msg[1].addr = state->i2c.addr >> 1; + state->msg[1].flags = I2C_M_RD; + state->msg[1].buf = state->i2c_read_buffer; + state->msg[1].len = 2; + + if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2) + dprintk("i2c read error on %d\n", reg); + + ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + + return ret; +} + +static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + ret = __dib8000_read_word(state, reg); + + mutex_unlock(&state->i2c_buffer_lock); + + return ret; +} + +static u32 dib8000_read32(struct dib8000_state *state, u16 reg) +{ + u16 rw[2]; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + rw[0] = __dib8000_read_word(state, reg + 0); + rw[1] = __dib8000_read_word(state, reg + 1); + + mutex_unlock(&state->i2c_buffer_lock); + + return ((rw[0] << 16) | (rw[1])); +} + +static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +{ + struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4}; + int ret = 0; + + if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + msg.buf = i2c->i2c_write_buffer; + msg.buf[0] = (reg >> 8) & 0xff; + msg.buf[1] = reg & 0xff; + msg.buf[2] = (val >> 8) & 0xff; + msg.buf[3] = val & 0xff; + + ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0; + mutex_unlock(i2c->i2c_buffer_lock); + + return ret; +} + +static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; + state->i2c_write_buffer[1] = reg & 0xff; + state->i2c_write_buffer[2] = (val >> 8) & 0xff; + state->i2c_write_buffer[3] = val & 0xff; + + memset(&state->msg[0], 0, sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c.addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 4; + + ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? + -EREMOTEIO : 0); + mutex_unlock(&state->i2c_buffer_lock); + + return ret; +} + +static const s16 coeff_2k_sb_1seg_dqpsk[8] = { + (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c, + (920 << 5) | 0x09 +}; + +static const s16 coeff_2k_sb_1seg[8] = { + (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f +}; + +static const s16 coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { + (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11, + (-931 << 5) | 0x0f +}; + +static const s16 coeff_2k_sb_3seg_0dqpsk[8] = { + (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e, + (982 << 5) | 0x0c +}; + +static const s16 coeff_2k_sb_3seg_1dqpsk[8] = { + (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12, + (-720 << 5) | 0x0d +}; + +static const s16 coeff_2k_sb_3seg[8] = { + (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e, + (-610 << 5) | 0x0a +}; + +static const s16 coeff_4k_sb_1seg_dqpsk[8] = { + (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f, + (-922 << 5) | 0x0d +}; + +static const s16 coeff_4k_sb_1seg[8] = { + (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d, + (-655 << 5) | 0x0a +}; + +static const s16 coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { + (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14, + (-958 << 5) | 0x13 +}; + +static const s16 coeff_4k_sb_3seg_0dqpsk[8] = { + (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12, + (-568 << 5) | 0x0f +}; + +static const s16 coeff_4k_sb_3seg_1dqpsk[8] = { + (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14, + (-848 << 5) | 0x13 +}; + +static const s16 coeff_4k_sb_3seg[8] = { + (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12, + (-869 << 5) | 0x13 +}; + +static const s16 coeff_8k_sb_1seg_dqpsk[8] = { + (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13, + (-598 << 5) | 0x10 +}; + +static const s16 coeff_8k_sb_1seg[8] = { + (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f, + (585 << 5) | 0x0f +}; + +static const s16 coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { + (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18, + (0 << 5) | 0x14 +}; + +static const s16 coeff_8k_sb_3seg_0dqpsk[8] = { + (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15, + (-877 << 5) | 0x15 +}; + +static const s16 coeff_8k_sb_3seg_1dqpsk[8] = { + (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18, + (-921 << 5) | 0x14 +}; + +static const s16 coeff_8k_sb_3seg[8] = { + (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15, + (690 << 5) | 0x14 +}; + +static const s16 ana_fe_coeff_3seg[24] = { + 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017 +}; + +static const s16 ana_fe_coeff_1seg[24] = { + 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003 +}; + +static const s16 ana_fe_coeff_13seg[24] = { + 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1 +}; + +static u16 fft_to_mode(struct dib8000_state *state) +{ + u16 mode; + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + mode = 1; + break; + case TRANSMISSION_MODE_4K: + mode = 2; + break; + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + mode = 3; + break; + } + return mode; +} + +static void dib8000_set_acquisition_mode(struct dib8000_state *state) +{ + u16 nud = dib8000_read_word(state, 298); + nud |= (1 << 3) | (1 << 0); + dprintk("acquisition mode activated\n"); + dib8000_write_word(state, 298, nud); +} +static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ + + state->output_mode = mode; + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->fe[0], mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) { + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + sram &= 0xfdff; + } else + sram |= 0x0c00; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + + case OUTMODE_ANALOG_ADC: + outreg = (1 << 10) | (3 << 6); + dib8000_set_acquisition_mode(state); + break; + + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n", + &state->fe[0]); + return -EINVAL; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + dib8000_write_word(state, 299, smo_mode); + dib8000_write_word(state, 300, fifo_threshold); /* synchronous fread */ + dib8000_write_word(state, 1286, outreg); + dib8000_write_word(state, 1291, sram); + + return 0; +} + +static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 tmp, sync_wait = dib8000_read_word(state, 273) & 0xfff0; + + dprintk("set diversity input to %i\n", onoff); + if (!state->differential_constellation) { + dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 + dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 + } else { + dib8000_write_word(state, 272, 0); //dvsy_off_lmod4 = 0 + dib8000_write_word(state, 273, sync_wait); // sync_enable = 0; comb_mode = 0 + } + state->diversity_onoff = onoff; + + switch (onoff) { + case 0: /* only use the internal way - not the diversity input */ + dib8000_write_word(state, 270, 1); + dib8000_write_word(state, 271, 0); + break; + case 1: /* both ways */ + dib8000_write_word(state, 270, 6); + dib8000_write_word(state, 271, 6); + break; + case 2: /* only the diversity input */ + dib8000_write_word(state, 270, 0); + dib8000_write_word(state, 271, 1); + break; + } + + if (state->revision == 0x8002) { + tmp = dib8000_read_word(state, 903); + dib8000_write_word(state, 903, tmp & ~(1 << 3)); + msleep(30); + dib8000_write_word(state, 903, tmp | (1 << 3)); + } + return 0; +} + +static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, + reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, + reg_1280; + + if (state->revision != 0x8090) + reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; + else + reg_1280 = (dib8000_read_word(state, 1280) & 0x707f) | 0x8f80; + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB8000_POWER_ALL: + reg_774 = 0x0000; + reg_775 = 0x0000; + reg_776 = 0x0000; + reg_900 &= 0xfffc; + if (state->revision != 0x8090) + reg_1280 &= 0x00ff; + else + reg_1280 &= 0x707f; + break; + case DIB8000_POWER_INTERFACE_ONLY: + if (state->revision != 0x8090) + reg_1280 &= 0x00ff; + else + reg_1280 &= 0xfa7b; + break; + } + + dprintk("powermode : 774 : %x ; 775 : %x; 776 : %x ; 900 : %x; 1280 : %x\n", reg_774, reg_775, reg_776, reg_900, reg_1280); + dib8000_write_word(state, 774, reg_774); + dib8000_write_word(state, 775, reg_775); + dib8000_write_word(state, 776, reg_776); + dib8000_write_word(state, 900, reg_900); + dib8000_write_word(state, 1280, reg_1280); +} + +static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) +{ + int ret = 0; + u16 reg, reg_907 = dib8000_read_word(state, 907); + u16 reg_908 = dib8000_read_word(state, 908); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + if (state->revision != 0x8090) { + reg_908 |= (1 << 1) | (1 << 0); + ret |= dib8000_write_word(state, 908, reg_908); + reg_908 &= ~(1 << 1); + } else { + reg = dib8000_read_word(state, 1925); + /* en_slowAdc = 1 & reset_sladc = 1 */ + dib8000_write_word(state, 1925, reg | + (1<<4) | (1<<2)); + + /* read access to make it works... strange ... */ + reg = dib8000_read_word(state, 1925); + msleep(20); + /* en_slowAdc = 1 & reset_sladc = 0 */ + dib8000_write_word(state, 1925, reg & ~(1<<4)); + + reg = dib8000_read_word(state, 921) & ~((0x3 << 14) + | (0x3 << 12)); + /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; + (Vin2 = Vcm) */ + dib8000_write_word(state, 921, reg | (1 << 14) + | (3 << 12)); + } + break; + + case DIBX000_SLOW_ADC_OFF: + if (state->revision == 0x8090) { + reg = dib8000_read_word(state, 1925); + /* reset_sladc = 1 en_slowAdc = 0 */ + dib8000_write_word(state, 1925, + (reg & ~(1<<2)) | (1<<4)); + } + reg_908 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + reg_907 &= 0x0fff; + reg_908 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_907 = (1 << 13) | (1 << 12); + reg_908 = (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1); + break; + + case DIBX000_VBG_ENABLE: + reg_907 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_907 |= (1 << 15); + break; + + default: + break; + } + + ret |= dib8000_write_word(state, 907, reg_907); + ret |= dib8000_write_word(state, 908, reg_908); + + return ret; +} + +static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw) +{ + struct dib8000_state *state = fe->demodulator_priv; + u32 timf; + + if (bw == 0) + bw = 6000; + + if (state->timf == 0) { + dprintk("using default timf\n"); + timf = state->timf_default; + } else { + dprintk("using updated timf\n"); + timf = state->timf; + } + + dib8000_write_word(state, 29, (u16) ((timf >> 16) & 0xffff)); + dib8000_write_word(state, 30, (u16) ((timf) & 0xffff)); + + return 0; +} + +static int dib8000_sad_calib(struct dib8000_state *state) +{ + u8 sad_sel = 3; + + if (state->revision == 0x8090) { + dib8000_write_word(state, 922, (sad_sel << 2)); + dib8000_write_word(state, 923, 2048); + + dib8000_write_word(state, 922, (sad_sel << 2) | 0x1); + dib8000_write_word(state, 922, (sad_sel << 2)); + } else { + /* internal */ + dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); + dib8000_write_word(state, 924, 776); + + /* do the calibration */ + dib8000_write_word(state, 923, (1 << 0)); + dib8000_write_word(state, 923, (0 << 0)); + } + + msleep(1); + return 0; +} + +static int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +{ + struct dib8000_state *state = fe->demodulator_priv; + if (value > 4095) + value = 4095; + state->wbd_ref = value; + return dib8000_write_word(state, 106, value); +} + +static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) +{ + dprintk("ifreq: %d %x, inversion: %d\n", bw->ifreq, bw->ifreq, bw->ifreq >> 25); + if (state->revision != 0x8090) { + dib8000_write_word(state, 23, + (u16) (((bw->internal * 1000) >> 16) & 0xffff)); + dib8000_write_word(state, 24, + (u16) ((bw->internal * 1000) & 0xffff)); + } else { + dib8000_write_word(state, 23, (u16) (((bw->internal / 2 * 1000) >> 16) & 0xffff)); + dib8000_write_word(state, 24, + (u16) ((bw->internal / 2 * 1000) & 0xffff)); + } + dib8000_write_word(state, 27, (u16) ((bw->ifreq >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (bw->ifreq & 0xffff)); + dib8000_write_word(state, 26, (u16) ((bw->ifreq >> 25) & 0x0003)); + + if (state->revision != 0x8090) + dib8000_write_word(state, 922, bw->sad_cfg); +} + +static void dib8000_reset_pll(struct dib8000_state *state) +{ + const struct dibx000_bandwidth_config *pll = state->cfg.pll; + u16 clk_cfg1, reg; + + if (state->revision != 0x8090) { + dib8000_write_word(state, 901, + (pll->pll_prediv << 8) | (pll->pll_ratio << 0)); + + clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | + (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | + (1 << 3) | (pll->pll_range << 1) | + (pll->pll_reset << 0); + + dib8000_write_word(state, 902, clk_cfg1); + clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); + dib8000_write_word(state, 902, clk_cfg1); + + dprintk("clk_cfg1: 0x%04x\n", clk_cfg1); + + /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */ + if (state->cfg.pll->ADClkSrc == 0) + dib8000_write_word(state, 904, + (0 << 15) | (0 << 12) | (0 << 10) | + (pll->modulo << 8) | + (pll->ADClkSrc << 7) | (0 << 1)); + else if (state->cfg.refclksel != 0) + dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | + ((state->cfg.refclksel & 0x3) << 10) | + (pll->modulo << 8) | + (pll->ADClkSrc << 7) | (0 << 1)); + else + dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | + (3 << 10) | (pll->modulo << 8) | + (pll->ADClkSrc << 7) | (0 << 1)); + } else { + dib8000_write_word(state, 1856, (!pll->pll_reset<<13) | + (pll->pll_range<<12) | (pll->pll_ratio<<6) | + (pll->pll_prediv)); + + reg = dib8000_read_word(state, 1857); + dib8000_write_word(state, 1857, reg|(!pll->pll_bypass<<15)); + + reg = dib8000_read_word(state, 1858); /* Force clk out pll /2 */ + dib8000_write_word(state, 1858, reg | 1); + + dib8000_write_word(state, 904, (pll->modulo << 8)); + } + + dib8000_reset_pll_common(state, pll); +} + +static int dib8000_update_pll(struct dvb_frontend *fe, + struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 reg_1857, reg_1856 = dib8000_read_word(state, 1856); + u8 loopdiv, prediv, oldprediv = state->cfg.pll->pll_prediv ; + u32 internal, xtal; + + /* get back old values */ + prediv = reg_1856 & 0x3f; + loopdiv = (reg_1856 >> 6) & 0x3f; + + if ((pll == NULL) || (pll->pll_prediv == prediv && + pll->pll_ratio == loopdiv)) + return -EINVAL; + + dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)\n", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); + if (state->revision == 0x8090) { + reg_1856 &= 0xf000; + reg_1857 = dib8000_read_word(state, 1857); + /* disable PLL */ + dib8000_write_word(state, 1857, reg_1857 & ~(1 << 15)); + + dib8000_write_word(state, 1856, reg_1856 | + ((pll->pll_ratio & 0x3f) << 6) | + (pll->pll_prediv & 0x3f)); + + /* write new system clk into P_sec_len */ + internal = dib8000_read32(state, 23) / 1000; + dprintk("Old Internal = %d\n", internal); + xtal = 2 * (internal / loopdiv) * prediv; + internal = 1000 * (xtal/pll->pll_prediv) * pll->pll_ratio; + dprintk("Xtal = %d , New Fmem = %d New Fdemod = %d, New Fsampling = %d\n", xtal, internal/1000, internal/2000, internal/8000); + dprintk("New Internal = %d\n", internal); + + dib8000_write_word(state, 23, + (u16) (((internal / 2) >> 16) & 0xffff)); + dib8000_write_word(state, 24, (u16) ((internal / 2) & 0xffff)); + /* enable PLL */ + dib8000_write_word(state, 1857, reg_1857 | (1 << 15)); + + while (((dib8000_read_word(state, 1856)>>15)&0x1) != 1) + dprintk("Waiting for PLL to lock\n"); + + /* verify */ + reg_1856 = dib8000_read_word(state, 1856); + dprintk("PLL Updated with prediv = %d and loopdiv = %d\n", + reg_1856&0x3f, (reg_1856>>6)&0x3f); + } else { + if (bw != state->current_demod_bw) { + /** Bandwidth change => force PLL update **/ + dprintk("PLL: Bandwidth Change %d MHz -> %d MHz (prediv: %d->%d)\n", state->current_demod_bw / 1000, bw / 1000, oldprediv, state->cfg.pll->pll_prediv); + + if (state->cfg.pll->pll_prediv != oldprediv) { + /** Full PLL change only if prediv is changed **/ + + /** full update => bypass and reconfigure **/ + dprintk("PLL: New Setting for %d MHz Bandwidth (prediv: %d, ratio: %d)\n", bw/1000, state->cfg.pll->pll_prediv, state->cfg.pll->pll_ratio); + dib8000_write_word(state, 902, dib8000_read_word(state, 902) | (1<<3)); /* bypass PLL */ + dib8000_reset_pll(state); + dib8000_write_word(state, 898, 0x0004); /* sad */ + } else + ratio = state->cfg.pll->pll_ratio; + + state->current_demod_bw = bw; + } + + if (ratio != 0) { + /** ratio update => only change ratio **/ + dprintk("PLL: Update ratio (prediv: %d, ratio: %d)\n", state->cfg.pll->pll_prediv, ratio); + dib8000_write_word(state, 901, (state->cfg.pll->pll_prediv << 8) | (ratio << 0)); /* only the PLL ratio is updated. */ + } + } + + return 0; +} + +static int dib8000_reset_gpio(struct dib8000_state *st) +{ + /* reset the GPIOs */ + dib8000_write_word(st, 1029, st->cfg.gpio_dir); + dib8000_write_word(st, 1030, st->cfg.gpio_val); + + /* TODO 782 is P_gpio_od */ + + dib8000_write_word(st, 1032, st->cfg.gpio_pwm_pos); + + dib8000_write_word(st, 1037, st->cfg.pwm_freq_div); + return 0; +} + +static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val) +{ + st->cfg.gpio_dir = dib8000_read_word(st, 1029); + st->cfg.gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->cfg.gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + dib8000_write_word(st, 1029, st->cfg.gpio_dir); + + st->cfg.gpio_val = dib8000_read_word(st, 1030); + st->cfg.gpio_val &= ~(1 << num); /* reset the direction bit */ + st->cfg.gpio_val |= (val & 0x01) << num; /* set the new value */ + dib8000_write_word(st, 1030, st->cfg.gpio_val); + + dprintk("gpio dir: %x: gpio val: %x\n", st->cfg.gpio_dir, st->cfg.gpio_val); + + return 0; +} + +static int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + struct dib8000_state *state = fe->demodulator_priv; + return dib8000_cfg_gpio(state, num, dir, val); +} + +static const u16 dib8000_defaults[] = { + /* auto search configuration - lock0 by default waiting + * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */ + 3, 7, + 0x0004, + 0x0400, + 0x0814, + + 12, 11, + 0x001b, + 0x7740, + 0x005b, + 0x8d80, + 0x01c9, + 0xc380, + 0x0000, + 0x0080, + 0x0000, + 0x0090, + 0x0001, + 0xd4c0, + + /*1, 32, + 0x6680 // P_corm_thres Lock algorithms configuration */ + + 11, 80, /* set ADC level to -16 */ + (1 << 13) - 825 - 117, + (1 << 13) - 837 - 117, + (1 << 13) - 811 - 117, + (1 << 13) - 766 - 117, + (1 << 13) - 737 - 117, + (1 << 13) - 693 - 117, + (1 << 13) - 648 - 117, + (1 << 13) - 619 - 117, + (1 << 13) - 575 - 117, + (1 << 13) - 531 - 117, + (1 << 13) - 501 - 117, + + 4, 108, + 0, + 0, + 0, + 0, + + 1, 175, + 0x0410, + 1, 179, + 8192, // P_fft_nb_to_cut + + 6, 181, + 0x2800, // P_coff_corthres_ ( 2k 4k 8k ) 0x2800 + 0x2800, + 0x2800, + 0x2800, // P_coff_cpilthres_ ( 2k 4k 8k ) 0x2800 + 0x2800, + 0x2800, + + 2, 193, + 0x0666, // P_pha3_thres + 0x0000, // P_cti_use_cpe, P_cti_use_prog + + 2, 205, + 0x200f, // P_cspu_regul, P_cspu_win_cut + 0x000f, // P_des_shift_work + + 5, 215, + 0x023d, // P_adp_regul_cnt + 0x00a4, // P_adp_noise_cnt + 0x00a4, // P_adp_regul_ext + 0x7ff0, // P_adp_noise_ext + 0x3ccc, // P_adp_fil + + 1, 230, + 0x0000, // P_2d_byp_ti_num + + 1, 263, + 0x800, //P_equal_thres_wgn + + 1, 268, + (2 << 9) | 39, // P_equal_ctrl_synchro, P_equal_speedmode + + 1, 270, + 0x0001, // P_div_lock0_wait + 1, 285, + 0x0020, //p_fec_ + 1, 299, + 0x0062, /* P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard */ + + 1, 338, + (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 + (1 << 10) | + (0 << 9) | /* P_ctrl_pre_freq_inh=0 */ + (3 << 5) | /* P_ctrl_pre_freq_step=3 */ + (1 << 0), /* P_pre_freq_win_len=1 */ + + 0, +}; + +static u16 dib8000_identify(struct i2c_device *client) +{ + u16 value; + + //because of glitches sometimes + value = dib8000_i2c_read16(client, 896); + + if ((value = dib8000_i2c_read16(client, 896)) != 0x01b3) { + dprintk("wrong Vendor ID (read=0x%x)\n", value); + return 0; + } + + value = dib8000_i2c_read16(client, 897); + if (value != 0x8000 && value != 0x8001 && + value != 0x8002 && value != 0x8090) { + dprintk("wrong Device ID (%x)\n", value); + return 0; + } + + switch (value) { + case 0x8000: + dprintk("found DiB8000A\n"); + break; + case 0x8001: + dprintk("found DiB8000B\n"); + break; + case 0x8002: + dprintk("found DiB8000C\n"); + break; + case 0x8090: + dprintk("found DiB8096P\n"); + break; + } + return value; +} + +static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 *unc); + +static void dib8000_reset_stats(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u32 ucb; + + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->post_bit_error, 0, sizeof(c->post_bit_error)); + memset(&c->post_bit_count, 0, sizeof(c->post_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + + c->strength.len = 1; + c->cnr.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].uvalue = 0; + + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dib8000_read_unc_blocks(fe, &ucb); + + state->init_ucb = -ucb; + state->ber_jiffies_stats = 0; + state->per_jiffies_stats = 0; + memset(&state->ber_jiffies_stats_layer, 0, + sizeof(state->ber_jiffies_stats_layer)); +} + +static int dib8000_reset(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + if ((state->revision = dib8000_identify(&state->i2c)) == 0) + return -EINVAL; + + /* sram lead in, rdy */ + if (state->revision != 0x8090) + dib8000_write_word(state, 1287, 0x0003); + + if (state->revision == 0x8000) + dprintk("error : dib8000 MA not supported\n"); + + dibx000_reset_i2c_master(&state->i2c_master); + + dib8000_set_power_mode(state, DIB8000_POWER_ALL); + + /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ + dib8000_set_adc_state(state, DIBX000_ADC_OFF); + + /* restart all parts */ + dib8000_write_word(state, 770, 0xffff); + dib8000_write_word(state, 771, 0xffff); + dib8000_write_word(state, 772, 0xfffc); + dib8000_write_word(state, 898, 0x000c); /* restart sad */ + if (state->revision == 0x8090) + dib8000_write_word(state, 1280, 0x0045); + else + dib8000_write_word(state, 1280, 0x004d); + dib8000_write_word(state, 1281, 0x000c); + + dib8000_write_word(state, 770, 0x0000); + dib8000_write_word(state, 771, 0x0000); + dib8000_write_word(state, 772, 0x0000); + dib8000_write_word(state, 898, 0x0004); // sad + dib8000_write_word(state, 1280, 0x0000); + dib8000_write_word(state, 1281, 0x0000); + + /* drives */ + if (state->revision != 0x8090) { + if (state->cfg.drives) + dib8000_write_word(state, 906, state->cfg.drives); + else { + dprintk("using standard PAD-drive-settings, please adjust settings in config-struct to be optimal.\n"); + /* min drive SDRAM - not optimal - adjust */ + dib8000_write_word(state, 906, 0x2d98); + } + } + + dib8000_reset_pll(state); + if (state->revision != 0x8090) + dib8000_write_word(state, 898, 0x0004); + + if (dib8000_reset_gpio(state) != 0) + dprintk("GPIO reset was not successful.\n"); + + if ((state->revision != 0x8090) && + (dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0)) + dprintk("OUTPUT_MODE could not be reset.\n"); + + state->current_agc = NULL; + + // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... + /* P_iqc_ca2 = 0; P_iqc_impnc_on = 0; P_iqc_mode = 0; */ + if (state->cfg.pll->ifreq == 0) + dib8000_write_word(state, 40, 0x0755); /* P_iqc_corr_inh = 0 enable IQcorr block */ + else + dib8000_write_word(state, 40, 0x1f55); /* P_iqc_corr_inh = 1 disable IQcorr block */ + + { + u16 l = 0, r; + const u16 *n; + n = dib8000_defaults; + l = *n++; + while (l) { + r = *n++; + do { + dib8000_write_word(state, r, *n++); + r++; + } while (--l); + l = *n++; + } + } + + state->isdbt_cfg_loaded = 0; + + //div_cfg override for special configs + if ((state->revision != 8090) && (state->cfg.div_cfg != 0)) + dib8000_write_word(state, 903, state->cfg.div_cfg); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1)); + + dib8000_set_bandwidth(fe, 6000); + + dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib8000_sad_calib(state); + if (state->revision != 0x8090) + dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + /* ber_rs_len = 3 */ + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); + + dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); + + dib8000_reset_stats(fe); + + return 0; +} + +static void dib8000_restart_agc(struct dib8000_state *state) +{ + // P_restart_iqc & P_restart_agc + dib8000_write_word(state, 770, 0x0a00); + dib8000_write_word(state, 770, 0x0000); +} + +static int dib8000_update_lna(struct dib8000_state *state) +{ + u16 dyn_gain; + + if (state->cfg.update_lna) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib8000_read_word(state, 390); + + if (state->cfg.update_lna(state->fe[0], dyn_gain)) { + dib8000_restart_agc(state); + return 1; + } + } + return 0; +} + +static int dib8000_set_agc_config(struct dib8000_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + u16 reg; + + if (state->current_band == band && state->current_agc != NULL) + return 0; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("no valid AGC configuration found for band 0x%02x\n", band); + return -EINVAL; + } + + state->current_agc = agc; + + /* AGC */ + dib8000_write_word(state, 76, agc->setup); + dib8000_write_word(state, 77, agc->inv_gain); + dib8000_write_word(state, 78, agc->time_stabiliz); + dib8000_write_word(state, 101, (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib8000_write_word(state, 102, (agc->alpha_mant << 5) | agc->alpha_exp); + dib8000_write_word(state, 103, (agc->beta_mant << 6) | agc->beta_exp); + + dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + /* AGC continued */ + if (state->wbd_ref != 0) + dib8000_write_word(state, 106, state->wbd_ref); + else // use default + dib8000_write_word(state, 106, agc->wbd_ref); + + if (state->revision == 0x8090) { + reg = dib8000_read_word(state, 922) & (0x3 << 2); + dib8000_write_word(state, 922, reg | (agc->wbd_sel << 2)); + } + + dib8000_write_word(state, 107, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); + dib8000_write_word(state, 108, agc->agc1_max); + dib8000_write_word(state, 109, agc->agc1_min); + dib8000_write_word(state, 110, agc->agc2_max); + dib8000_write_word(state, 111, agc->agc2_min); + dib8000_write_word(state, 112, (agc->agc1_pt1 << 8) | agc->agc1_pt2); + dib8000_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib8000_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib8000_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + dib8000_write_word(state, 75, agc->agc1_pt3); + if (state->revision != 0x8090) + dib8000_write_word(state, 923, + (dib8000_read_word(state, 923) & 0xffe3) | + (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); + + return 0; +} + +static void dib8000_pwm_agc_reset(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + dib8000_set_adc_state(state, DIBX000_ADC_ON); + dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))); +} + +static int dib8000_agc_soft_split(struct dib8000_state *state) +{ + u16 agc, split_offset; + + if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) + return 0; + + // n_agc_global + agc = dib8000_read_word(state, 390); + + if (agc > state->current_agc->split.min_thres) + split_offset = state->current_agc->split.min; + else if (agc < state->current_agc->split.max_thres) + split_offset = state->current_agc->split.max; + else + split_offset = state->current_agc->split.max * + (agc - state->current_agc->split.min_thres) / + (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + + dprintk("AGC split_offset: %d\n", split_offset); + + // P_agc_force_split and P_agc_split_offset + dib8000_write_word(state, 107, (dib8000_read_word(state, 107) & 0xff00) | split_offset); + return 5000; +} + +static int dib8000_agc_startup(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + enum frontend_tune_state *tune_state = &state->tune_state; + int ret = 0; + u16 reg; + u32 upd_demod_gain_period = 0x8000; + + switch (*tune_state) { + case CT_AGC_START: + // set power-up level: interf+analog+AGC + + if (state->revision != 0x8090) + dib8000_set_adc_state(state, DIBX000_ADC_ON); + else { + dib8000_set_power_mode(state, DIB8000_POWER_ALL); + + reg = dib8000_read_word(state, 1947)&0xff00; + dib8000_write_word(state, 1946, + upd_demod_gain_period & 0xFFFF); + /* bit 14 = enDemodGain */ + dib8000_write_word(state, 1947, reg | (1<<14) | + ((upd_demod_gain_period >> 16) & 0xFF)); + + /* enable adc i & q */ + reg = dib8000_read_word(state, 1920); + dib8000_write_word(state, 1920, (reg | 0x3) & + (~(1 << 7))); + } + + if (dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))) != 0) { + *tune_state = CT_AGC_STOP; + state->status = FE_STATUS_TUNE_FAILED; + break; + } + + ret = 70; + *tune_state = CT_AGC_STEP_0; + break; + + case CT_AGC_STEP_0: + //AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(fe, 1); + + dib8000_restart_agc(state); + + // wait AGC rough lock time + ret = 50; + *tune_state = CT_AGC_STEP_1; + break; + + case CT_AGC_STEP_1: + // wait AGC accurate lock time + ret = 70; + + if (dib8000_update_lna(state)) + // wait only AGC rough lock time + ret = 50; + else + *tune_state = CT_AGC_STEP_2; + break; + + case CT_AGC_STEP_2: + dib8000_agc_soft_split(state); + + if (state->cfg.agc_control) + state->cfg.agc_control(fe, 0); + + *tune_state = CT_AGC_STOP; + break; + default: + ret = dib8000_agc_soft_split(state); + break; + } + return ret; + +} + +static void dib8096p_host_bus_drive(struct dib8000_state *state, u8 drive) +{ + u16 reg; + + drive &= 0x7; + + /* drive host bus 2, 3, 4 */ + reg = dib8000_read_word(state, 1798) & + ~(0x7 | (0x7 << 6) | (0x7 << 12)); + reg |= (drive<<12) | (drive<<6) | drive; + dib8000_write_word(state, 1798, reg); + + /* drive host bus 5,6 */ + reg = dib8000_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); + reg |= (drive<<8) | (drive<<2); + dib8000_write_word(state, 1799, reg); + + /* drive host bus 7, 8, 9 */ + reg = dib8000_read_word(state, 1800) & + ~(0x7 | (0x7 << 6) | (0x7 << 12)); + reg |= (drive<<12) | (drive<<6) | drive; + dib8000_write_word(state, 1800, reg); + + /* drive host bus 10, 11 */ + reg = dib8000_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); + reg |= (drive<<8) | (drive<<2); + dib8000_write_word(state, 1801, reg); + + /* drive host bus 12, 13, 14 */ + reg = dib8000_read_word(state, 1802) & + ~(0x7 | (0x7 << 6) | (0x7 << 12)); + reg |= (drive<<12) | (drive<<6) | drive; + dib8000_write_word(state, 1802, reg); +} + +static u32 dib8096p_calcSyncFreq(u32 P_Kin, u32 P_Kout, + u32 insertExtSynchro, u32 syncSize) +{ + u32 quantif = 3; + u32 nom = (insertExtSynchro * P_Kin+syncSize); + u32 denom = P_Kout; + u32 syncFreq = ((nom << quantif) / denom); + + if ((syncFreq & ((1 << quantif) - 1)) != 0) + syncFreq = (syncFreq >> quantif) + 1; + else + syncFreq = (syncFreq >> quantif); + + if (syncFreq != 0) + syncFreq = syncFreq - 1; + + return syncFreq; +} + +static void dib8096p_cfg_DibTx(struct dib8000_state *state, u32 P_Kin, + u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, + u32 syncWord, u32 syncSize) +{ + dprintk("Configure DibStream Tx\n"); + + dib8000_write_word(state, 1615, 1); + dib8000_write_word(state, 1603, P_Kin); + dib8000_write_word(state, 1605, P_Kout); + dib8000_write_word(state, 1606, insertExtSynchro); + dib8000_write_word(state, 1608, synchroMode); + dib8000_write_word(state, 1609, (syncWord >> 16) & 0xffff); + dib8000_write_word(state, 1610, syncWord & 0xffff); + dib8000_write_word(state, 1612, syncSize); + dib8000_write_word(state, 1615, 0); +} + +static void dib8096p_cfg_DibRx(struct dib8000_state *state, u32 P_Kin, + u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, + u32 syncWord, u32 syncSize, u32 dataOutRate) +{ + u32 syncFreq; + + dprintk("Configure DibStream Rx synchroMode = %d\n", synchroMode); + + if ((P_Kin != 0) && (P_Kout != 0)) { + syncFreq = dib8096p_calcSyncFreq(P_Kin, P_Kout, + insertExtSynchro, syncSize); + dib8000_write_word(state, 1542, syncFreq); + } + + dib8000_write_word(state, 1554, 1); + dib8000_write_word(state, 1536, P_Kin); + dib8000_write_word(state, 1537, P_Kout); + dib8000_write_word(state, 1539, synchroMode); + dib8000_write_word(state, 1540, (syncWord >> 16) & 0xffff); + dib8000_write_word(state, 1541, syncWord & 0xffff); + dib8000_write_word(state, 1543, syncSize); + dib8000_write_word(state, 1544, dataOutRate); + dib8000_write_word(state, 1554, 0); +} + +static void dib8096p_enMpegMux(struct dib8000_state *state, int onoff) +{ + u16 reg_1287; + + reg_1287 = dib8000_read_word(state, 1287); + + switch (onoff) { + case 1: + reg_1287 &= ~(1 << 8); + break; + case 0: + reg_1287 |= (1 << 8); + break; + } + + dib8000_write_word(state, 1287, reg_1287); +} + +static void dib8096p_configMpegMux(struct dib8000_state *state, + u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) +{ + u16 reg_1287; + + dprintk("Enable Mpeg mux\n"); + + dib8096p_enMpegMux(state, 0); + + /* If the input mode is MPEG do not divide the serial clock */ + if ((enSerialMode == 1) && (state->input_mode_mpeg == 1)) + enSerialClkDiv2 = 0; + + reg_1287 = ((pulseWidth & 0x1f) << 3) | + ((enSerialMode & 0x1) << 2) | (enSerialClkDiv2 & 0x1); + dib8000_write_word(state, 1287, reg_1287); + + dib8096p_enMpegMux(state, 1); +} + +static void dib8096p_setDibTxMux(struct dib8000_state *state, int mode) +{ + u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 7); + + switch (mode) { + case MPEG_ON_DIBTX: + dprintk("SET MPEG ON DIBSTREAM TX\n"); + dib8096p_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); + reg_1288 |= (1 << 9); break; + case DIV_ON_DIBTX: + dprintk("SET DIV_OUT ON DIBSTREAM TX\n"); + dib8096p_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); + reg_1288 |= (1 << 8); break; + case ADC_ON_DIBTX: + dprintk("SET ADC_OUT ON DIBSTREAM TX\n"); + dib8096p_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); + reg_1288 |= (1 << 7); break; + default: + break; + } + dib8000_write_word(state, 1288, reg_1288); +} + +static void dib8096p_setHostBusMux(struct dib8000_state *state, int mode) +{ + u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 4); + + switch (mode) { + case DEMOUT_ON_HOSTBUS: + dprintk("SET DEM OUT OLD INTERF ON HOST BUS\n"); + dib8096p_enMpegMux(state, 0); + reg_1288 |= (1 << 6); + break; + case DIBTX_ON_HOSTBUS: + dprintk("SET DIBSTREAM TX ON HOST BUS\n"); + dib8096p_enMpegMux(state, 0); + reg_1288 |= (1 << 5); + break; + case MPEG_ON_HOSTBUS: + dprintk("SET MPEG MUX ON HOST BUS\n"); + reg_1288 |= (1 << 4); + break; + default: + break; + } + dib8000_write_word(state, 1288, reg_1288); +} + +static int dib8096p_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 reg_1287; + + switch (onoff) { + case 0: /* only use the internal way - not the diversity input */ + dprintk("%s mode OFF : by default Enable Mpeg INPUT\n", + __func__); + /* outputRate = 8 */ + dib8096p_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); + + /* Do not divide the serial clock of MPEG MUX in + SERIAL MODE in case input mode MPEG is used */ + reg_1287 = dib8000_read_word(state, 1287); + /* enSerialClkDiv2 == 1 ? */ + if ((reg_1287 & 0x1) == 1) { + /* force enSerialClkDiv2 = 0 */ + reg_1287 &= ~0x1; + dib8000_write_word(state, 1287, reg_1287); + } + state->input_mode_mpeg = 1; + break; + case 1: /* both ways */ + case 2: /* only the diversity input */ + dprintk("%s ON : Enable diversity INPUT\n", __func__); + dib8096p_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); + state->input_mode_mpeg = 0; + break; + } + + dib8000_set_diversity_in(state->fe[0], onoff); + return 0; +} + +static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 outreg, smo_mode, fifo_threshold; + u8 prefer_mpeg_mux_use = 1; + int ret = 0; + + state->output_mode = mode; + dib8096p_host_bus_drive(state, 1); + + fifo_threshold = 1792; + smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); + outreg = dib8000_read_word(state, 1286) & + ~((1 << 10) | (0x7 << 6) | (1 << 1)); + + switch (mode) { + case OUTMODE_HIGH_Z: + outreg = 0; + break; + + case OUTMODE_MPEG2_SERIAL: + if (prefer_mpeg_mux_use) { + dprintk("dib8096P setting output mode TS_SERIAL using Mpeg Mux\n"); + dib8096p_configMpegMux(state, 3, 1, 1); + dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); + } else {/* Use Smooth block */ + dprintk("dib8096P setting output mode TS_SERIAL using Smooth bloc\n"); + dib8096p_setHostBusMux(state, + DEMOUT_ON_HOSTBUS); + outreg |= (2 << 6) | (0 << 1); + } + break; + + case OUTMODE_MPEG2_PAR_GATED_CLK: + if (prefer_mpeg_mux_use) { + dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Mpeg Mux\n"); + dib8096p_configMpegMux(state, 2, 0, 0); + dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); + } else { /* Use Smooth block */ + dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Smooth block\n"); + dib8096p_setHostBusMux(state, + DEMOUT_ON_HOSTBUS); + outreg |= (0 << 6); + } + break; + + case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ + dprintk("dib8096P setting output mode TS_PARALLEL_CONT using Smooth block\n"); + dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); + outreg |= (1 << 6); + break; + + case OUTMODE_MPEG2_FIFO: + /* Using Smooth block because not supported + by new Mpeg Mux bloc */ + dprintk("dib8096P setting output mode TS_FIFO using Smooth block\n"); + dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); + outreg |= (5 << 6); + smo_mode |= (3 << 1); + fifo_threshold = 512; + break; + + case OUTMODE_DIVERSITY: + dprintk("dib8096P setting output mode MODE_DIVERSITY\n"); + dib8096p_setDibTxMux(state, DIV_ON_DIBTX); + dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); + break; + + case OUTMODE_ANALOG_ADC: + dprintk("dib8096P setting output mode MODE_ANALOG_ADC\n"); + dib8096p_setDibTxMux(state, ADC_ON_DIBTX); + dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); + break; + } + + if (mode != OUTMODE_HIGH_Z) + outreg |= (1<<10); + + dprintk("output_mpeg2_in_188_bytes = %d\n", + state->cfg.output_mpeg2_in_188_bytes); + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + + ret |= dib8000_write_word(state, 299, smo_mode); + /* synchronous fread */ + ret |= dib8000_write_word(state, 299 + 1, fifo_threshold); + ret |= dib8000_write_word(state, 1286, outreg); + + return ret; +} + +static int map_addr_to_serpar_number(struct i2c_msg *msg) +{ + if (msg->buf[0] <= 15) + msg->buf[0] -= 1; + else if (msg->buf[0] == 17) + msg->buf[0] = 15; + else if (msg->buf[0] == 16) + msg->buf[0] = 17; + else if (msg->buf[0] == 19) + msg->buf[0] = 16; + else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) + msg->buf[0] -= 3; + else if (msg->buf[0] == 28) + msg->buf[0] = 23; + else if (msg->buf[0] == 99) + msg->buf[0] = 99; + else + return -EINVAL; + return 0; +} + +static int dib8096p_tuner_write_serpar(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct dib8000_state *state = i2c_get_adapdata(i2c_adap); + u8 n_overflow = 1; + u16 i = 1000; + u16 serpar_num = msg[0].buf[0]; + + while (n_overflow == 1 && i) { + n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1; + i--; + if (i == 0) + dprintk("Tuner ITF: write busy (overflow)\n"); + } + dib8000_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); + dib8000_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); + + return num; +} + +static int dib8096p_tuner_read_serpar(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct dib8000_state *state = i2c_get_adapdata(i2c_adap); + u8 n_overflow = 1, n_empty = 1; + u16 i = 1000; + u16 serpar_num = msg[0].buf[0]; + u16 read_word; + + while (n_overflow == 1 && i) { + n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1; + i--; + if (i == 0) + dprintk("TunerITF: read busy (overflow)\n"); + } + dib8000_write_word(state, 1985, (0<<6) | (serpar_num&0x3f)); + + i = 1000; + while (n_empty == 1 && i) { + n_empty = dib8000_read_word(state, 1984)&0x1; + i--; + if (i == 0) + dprintk("TunerITF: read busy (empty)\n"); + } + + read_word = dib8000_read_word(state, 1987); + msg[1].buf[0] = (read_word >> 8) & 0xff; + msg[1].buf[1] = (read_word) & 0xff; + + return num; +} + +static int dib8096p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + if (map_addr_to_serpar_number(&msg[0]) == 0) { + if (num == 1) /* write */ + return dib8096p_tuner_write_serpar(i2c_adap, msg, 1); + else /* read */ + return dib8096p_tuner_read_serpar(i2c_adap, msg, 2); + } + return num; +} + +static int dib8096p_rw_on_apb(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num, u16 apb_address) +{ + struct dib8000_state *state = i2c_get_adapdata(i2c_adap); + u16 word; + + if (num == 1) { /* write */ + dib8000_write_word(state, apb_address, + ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); + } else { + word = dib8000_read_word(state, apb_address); + msg[1].buf[0] = (word >> 8) & 0xff; + msg[1].buf[1] = (word) & 0xff; + } + return num; +} + +static int dib8096p_tuner_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct dib8000_state *state = i2c_get_adapdata(i2c_adap); + u16 apb_address = 0, word; + int i = 0; + + switch (msg[0].buf[0]) { + case 0x12: + apb_address = 1920; + break; + case 0x14: + apb_address = 1921; + break; + case 0x24: + apb_address = 1922; + break; + case 0x1a: + apb_address = 1923; + break; + case 0x22: + apb_address = 1924; + break; + case 0x33: + apb_address = 1926; + break; + case 0x34: + apb_address = 1927; + break; + case 0x35: + apb_address = 1928; + break; + case 0x36: + apb_address = 1929; + break; + case 0x37: + apb_address = 1930; + break; + case 0x38: + apb_address = 1931; + break; + case 0x39: + apb_address = 1932; + break; + case 0x2a: + apb_address = 1935; + break; + case 0x2b: + apb_address = 1936; + break; + case 0x2c: + apb_address = 1937; + break; + case 0x2d: + apb_address = 1938; + break; + case 0x2e: + apb_address = 1939; + break; + case 0x2f: + apb_address = 1940; + break; + case 0x30: + apb_address = 1941; + break; + case 0x31: + apb_address = 1942; + break; + case 0x32: + apb_address = 1943; + break; + case 0x3e: + apb_address = 1944; + break; + case 0x3f: + apb_address = 1945; + break; + case 0x40: + apb_address = 1948; + break; + case 0x25: + apb_address = 936; + break; + case 0x26: + apb_address = 937; + break; + case 0x27: + apb_address = 938; + break; + case 0x28: + apb_address = 939; + break; + case 0x1d: + /* get sad sel request */ + i = ((dib8000_read_word(state, 921) >> 12)&0x3); + word = dib8000_read_word(state, 924+i); + msg[1].buf[0] = (word >> 8) & 0xff; + msg[1].buf[1] = (word) & 0xff; + return num; + case 0x1f: + if (num == 1) { /* write */ + word = (u16) ((msg[0].buf[1] << 8) | + msg[0].buf[2]); + /* in the VGAMODE Sel are located on bit 0/1 */ + word &= 0x3; + word = (dib8000_read_word(state, 921) & + ~(3<<12)) | (word<<12); + /* Set the proper input */ + dib8000_write_word(state, 921, word); + return num; + } + } + + if (apb_address != 0) /* R/W access via APB */ + return dib8096p_rw_on_apb(i2c_adap, msg, num, apb_address); + else /* R/W access via SERPAR */ + return dib8096p_tuner_rw_serpar(i2c_adap, msg, num); + + return 0; +} + +static u32 dib8096p_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm dib8096p_tuner_xfer_algo = { + .master_xfer = dib8096p_tuner_xfer, + .functionality = dib8096p_i2c_func, +}; + +static struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) +{ + struct dib8000_state *st = fe->demodulator_priv; + return &st->dib8096p_tuner_adap; +} + +static int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 en_cur_state; + + dprintk("sleep dib8096p: %d\n", onoff); + + en_cur_state = dib8000_read_word(state, 1922); + + /* LNAs and MIX are ON and therefore it is a valid configuration */ + if (en_cur_state > 0xff) + state->tuner_enable = en_cur_state ; + + if (onoff) + en_cur_state &= 0x00ff; + else { + if (state->tuner_enable != 0) + en_cur_state = state->tuner_enable; + } + + dib8000_write_word(state, 1922, en_cur_state); + + return 0; +} + +static const s32 lut_1000ln_mant[] = +{ + 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600 +}; + +static s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) +{ + struct dib8000_state *state = fe->demodulator_priv; + u32 ix = 0, tmp_val = 0, exp = 0, mant = 0; + s32 val; + + val = dib8000_read32(state, 384); + if (mode) { + tmp_val = val; + while (tmp_val >>= 1) + exp++; + mant = (val * 1000 / (1<<exp)); + ix = (u8)((mant-1000)/100); /* index of the LUT */ + val = (lut_1000ln_mant[ix] + 693*(exp-20) - 6908); + val = (val*256)/1000; + } + return val; +} + +static int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) +{ + struct dib8000_state *state = fe->demodulator_priv; + int val = 0; + + switch (IQ) { + case 1: + val = dib8000_read_word(state, 403); + break; + case 0: + val = dib8000_read_word(state, 404); + break; + } + if (val & 0x200) + val -= 1024; + + return val; +} + +static void dib8000_update_timf(struct dib8000_state *state) +{ + u32 timf = state->timf = dib8000_read32(state, 435); + + dib8000_write_word(state, 29, (u16) (timf >> 16)); + dib8000_write_word(state, 30, (u16) (timf & 0xffff)); + dprintk("Updated timing frequency: %d (default: %d)\n", state->timf, state->timf_default); +} + +static u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) +{ + struct dib8000_state *state = fe->demodulator_priv; + + switch (op) { + case DEMOD_TIMF_SET: + state->timf = timf; + break; + case DEMOD_TIMF_UPDATE: + dib8000_update_timf(state); + break; + case DEMOD_TIMF_GET: + break; + } + dib8000_set_bandwidth(state->fe[0], 6000); + + return state->timf; +} + +static const u16 adc_target_16dB[11] = { + 7250, 7238, 7264, 7309, 7338, 7382, 7427, 7456, 7500, 7544, 7574 +}; + +static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; + +static u16 dib8000_set_layer(struct dib8000_state *state, u8 layer_index, u16 max_constellation) +{ + u8 cr, constellation, time_intlv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + + switch (c->layer[layer_index].modulation) { + case DQPSK: + constellation = 0; + break; + case QPSK: + constellation = 1; + break; + case QAM_16: + constellation = 2; + break; + case QAM_64: + default: + constellation = 3; + break; + } + + switch (c->layer[layer_index].fec) { + case FEC_1_2: + cr = 1; + break; + case FEC_2_3: + cr = 2; + break; + case FEC_3_4: + cr = 3; + break; + case FEC_5_6: + cr = 5; + break; + case FEC_7_8: + default: + cr = 7; + break; + } + + time_intlv = fls(c->layer[layer_index].interleaving); + if (time_intlv > 3 && !(time_intlv == 4 && c->isdbt_sb_mode == 1)) + time_intlv = 0; + + dib8000_write_word(state, 2 + layer_index, (constellation << 10) | ((c->layer[layer_index].segment_count & 0xf) << 6) | (cr << 3) | time_intlv); + if (c->layer[layer_index].segment_count > 0) { + switch (max_constellation) { + case DQPSK: + case QPSK: + if (c->layer[layer_index].modulation == QAM_16 || c->layer[layer_index].modulation == QAM_64) + max_constellation = c->layer[layer_index].modulation; + break; + case QAM_16: + if (c->layer[layer_index].modulation == QAM_64) + max_constellation = c->layer[layer_index].modulation; + break; + } + } + + return max_constellation; +} + +static const u16 adp_Q64[4] = {0x0148, 0xfff0, 0x00a4, 0xfff8}; /* P_adp_regul_cnt 0.04, P_adp_noise_cnt -0.002, P_adp_regul_ext 0.02, P_adp_noise_ext -0.001 */ +static const u16 adp_Q16[4] = {0x023d, 0xffdf, 0x00a4, 0xfff0}; /* P_adp_regul_cnt 0.07, P_adp_noise_cnt -0.004, P_adp_regul_ext 0.02, P_adp_noise_ext -0.002 */ +static const u16 adp_Qdefault[4] = {0x099a, 0xffae, 0x0333, 0xfff8}; /* P_adp_regul_cnt 0.3, P_adp_noise_cnt -0.01, P_adp_regul_ext 0.1, P_adp_noise_ext -0.002 */ +static u16 dib8000_adp_fine_tune(struct dib8000_state *state, u16 max_constellation) +{ + u16 i, ana_gain = 0; + const u16 *adp; + + /* channel estimation fine configuration */ + switch (max_constellation) { + case QAM_64: + ana_gain = 0x7; + adp = &adp_Q64[0]; + break; + case QAM_16: + ana_gain = 0x7; + adp = &adp_Q16[0]; + break; + default: + ana_gain = 0; + adp = &adp_Qdefault[0]; + break; + } + + for (i = 0; i < 4; i++) + dib8000_write_word(state, 215 + i, adp[i]); + + return ana_gain; +} + +static void dib8000_update_ana_gain(struct dib8000_state *state, u16 ana_gain) +{ + u16 i; + + dib8000_write_word(state, 116, ana_gain); + + /* update ADC target depending on ana_gain */ + if (ana_gain) { /* set -16dB ADC target for ana_gain=-1 */ + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i]); + } else { /* set -22dB ADC target for ana_gain=0 */ + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); + } +} + +static void dib8000_load_ana_fe_coefs(struct dib8000_state *state, const s16 *ana_fe) +{ + u16 mode = 0; + + if (state->isdbt_cfg_loaded == 0) + for (mode = 0; mode < 24; mode++) + dib8000_write_word(state, 117 + mode, ana_fe[mode]); +} + +static const u16 lut_prbs_2k[13] = { + 0x423, 0x009, 0x5C7, + 0x7A6, 0x3D8, 0x527, + 0x7FF, 0x79B, 0x3D6, + 0x3A2, 0x53B, 0x2F4, + 0x213 +}; + +static const u16 lut_prbs_4k[13] = { + 0x208, 0x0C3, 0x7B9, + 0x423, 0x5C7, 0x3D8, + 0x7FF, 0x3D6, 0x53B, + 0x213, 0x029, 0x0D0, + 0x48E +}; + +static const u16 lut_prbs_8k[13] = { + 0x740, 0x069, 0x7DD, + 0x208, 0x7B9, 0x5C7, + 0x7FF, 0x53B, 0x029, + 0x48E, 0x4C4, 0x367, + 0x684 +}; + +static u16 dib8000_get_init_prbs(struct dib8000_state *state, u16 subchannel) +{ + int sub_channel_prbs_group = 0; + int prbs_group; + + sub_channel_prbs_group = subchannel / 3; + if (sub_channel_prbs_group >= ARRAY_SIZE(lut_prbs_2k)) + return 0; + + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + prbs_group = lut_prbs_2k[sub_channel_prbs_group]; + break; + case TRANSMISSION_MODE_4K: + prbs_group = lut_prbs_4k[sub_channel_prbs_group]; + break; + default: + case TRANSMISSION_MODE_8K: + prbs_group = lut_prbs_8k[sub_channel_prbs_group]; + } + + dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x\n", + sub_channel_prbs_group, subchannel, prbs_group); + + return prbs_group; +} + +static void dib8000_set_13seg_channel(struct dib8000_state *state) +{ + u16 i; + u16 coff_pow = 0x2800; + + state->seg_mask = 0x1fff; /* All 13 segments enabled */ + + /* ---- COFF ---- Carloff, the most robust --- */ + if (state->isdbt_cfg_loaded == 0) { /* if not Sound Broadcasting mode : put default values for 13 segments */ + dib8000_write_word(state, 180, (16 << 6) | 9); + dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); + coff_pow = 0x2800; + for (i = 0; i < 6; i++) + dib8000_write_word(state, 181+i, coff_pow); + + /* P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1 */ + /* P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 */ + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); + + /* P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 */ + dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); + /* P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 */ + dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + dib8000_write_word(state, 228, 0); /* default value */ + dib8000_write_word(state, 265, 31); /* default value */ + dib8000_write_word(state, 205, 0x200f); /* init value */ + } + + /* + * make the cpil_coff_lock more robust but slower p_coff_winlen + * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) + */ + + if (state->cfg.pll->ifreq == 0) + dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ + + dib8000_load_ana_fe_coefs(state, ana_fe_coeff_13seg); +} + +static void dib8000_set_subchannel_prbs(struct dib8000_state *state, u16 init_prbs) +{ + u16 reg_1; + + reg_1 = dib8000_read_word(state, 1); + dib8000_write_word(state, 1, (init_prbs << 2) | (reg_1 & 0x3)); /* ADDR 1 */ +} + +static void dib8000_small_fine_tune(struct dib8000_state *state) +{ + u16 i; + const s16 *ncoeff; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + + dib8000_write_word(state, 352, state->seg_diff_mask); + dib8000_write_word(state, 353, state->seg_mask); + + /* P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 */ + dib8000_write_word(state, 351, (c->isdbt_sb_mode << 9) | (c->isdbt_sb_mode << 8) | (13 << 4) | 5); + + if (c->isdbt_sb_mode) { + /* ---- SMALL ---- */ + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + if (c->isdbt_partial_reception == 0) { /* 1-seg */ + if (c->layer[0].modulation == DQPSK) /* DQPSK */ + ncoeff = coeff_2k_sb_1seg_dqpsk; + else /* QPSK or QAM */ + ncoeff = coeff_2k_sb_1seg; + } else { /* 3-segments */ + if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_2k_sb_3seg_0dqpsk; + } else { /* QPSK or QAM on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_2k_sb_3seg_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_2k_sb_3seg; + } + } + break; + case TRANSMISSION_MODE_4K: + if (c->isdbt_partial_reception == 0) { /* 1-seg */ + if (c->layer[0].modulation == DQPSK) /* DQPSK */ + ncoeff = coeff_4k_sb_1seg_dqpsk; + else /* QPSK or QAM */ + ncoeff = coeff_4k_sb_1seg; + } else { /* 3-segments */ + if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_4k_sb_3seg_0dqpsk; + } else { /* QPSK or QAM on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_4k_sb_3seg_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_4k_sb_3seg; + } + } + break; + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + default: + if (c->isdbt_partial_reception == 0) { /* 1-seg */ + if (c->layer[0].modulation == DQPSK) /* DQPSK */ + ncoeff = coeff_8k_sb_1seg_dqpsk; + else /* QPSK or QAM */ + ncoeff = coeff_8k_sb_1seg; + } else { /* 3-segments */ + if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_8k_sb_3seg_0dqpsk; + } else { /* QPSK or QAM on central segment */ + if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */ + ncoeff = coeff_8k_sb_3seg_1dqpsk; + else /* QPSK or QAM on external segments */ + ncoeff = coeff_8k_sb_3seg; + } + } + break; + } + + for (i = 0; i < 8; i++) + dib8000_write_word(state, 343 + i, ncoeff[i]); + } +} + +static const u16 coff_thres_1seg[3] = {300, 150, 80}; +static const u16 coff_thres_3seg[3] = {350, 300, 250}; +static void dib8000_set_sb_channel(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + const u16 *coff; + u16 i; + + if (c->transmission_mode == TRANSMISSION_MODE_2K || c->transmission_mode == TRANSMISSION_MODE_4K) { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); /* adp_pass =1 */ + dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); /* pha3_force_pha_shift = 1 */ + } else { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); /* adp_pass =0 */ + dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); /* pha3_force_pha_shift = 0 */ + } + + if (c->isdbt_partial_reception == 1) /* 3-segments */ + state->seg_mask = 0x00E0; + else /* 1-segment */ + state->seg_mask = 0x0040; + + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + + /* ---- COFF ---- Carloff, the most robust --- */ + /* P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64, P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 */ + dib8000_write_word(state, 187, (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~c->isdbt_partial_reception & 1) << 2) | 0x3); + + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); /* P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 */ + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0));/* P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 */ + + /* Sound Broadcasting mode 1 seg */ + if (c->isdbt_partial_reception == 0) { + /* P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width = (P_mode == 3) , P_coff_one_seg_sym = (P_mode-1) */ + if (state->mode == 3) + dib8000_write_word(state, 180, 0x1fcf | ((state->mode - 1) << 14)); + else + dib8000_write_word(state, 180, 0x0fcf | ((state->mode - 1) << 14)); + + /* P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 */ + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); + coff = &coff_thres_1seg[0]; + } else { /* Sound Broadcasting mode 3 seg */ + dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); + /* P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 */ + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); + coff = &coff_thres_3seg[0]; + } + + dib8000_write_word(state, 228, 1); /* P_2d_mode_byp=1 */ + dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); /* P_cspu_win_cut = 0 */ + + if (c->isdbt_partial_reception == 0 && c->transmission_mode == TRANSMISSION_MODE_2K) + dib8000_write_word(state, 265, 15); /* P_equal_noise_sel = 15 */ + + /* Write COFF thres */ + for (i = 0 ; i < 3; i++) { + dib8000_write_word(state, 181+i, coff[i]); + dib8000_write_word(state, 184+i, coff[i]); + } + + /* + * make the cpil_coff_lock more robust but slower p_coff_winlen + * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) + */ + + dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask); /* P_equal_noise_seg_inh */ + + if (c->isdbt_partial_reception == 0) + dib8000_write_word(state, 178, 64); /* P_fft_powrange = 64 */ + else + dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */ +} + +static void dib8000_set_isdbt_common_channel(struct dib8000_state *state, u8 seq, u8 autosearching) +{ + u16 p_cfr_left_edge = 0, p_cfr_right_edge = 0; + u16 tmcc_pow = 0, ana_gain = 0, tmp = 0, i = 0, nbseg_diff = 0 ; + u16 max_constellation = DQPSK; + int init_prbs; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + + if (autosearching) + c->isdbt_partial_reception = 1; + + /* P_mode */ + dib8000_write_word(state, 10, (seq << 4)); + + /* init mode */ + state->mode = fft_to_mode(state); + + /* set guard */ + tmp = dib8000_read_word(state, 1); + dib8000_write_word(state, 1, (tmp&0xfffc) | (c->guard_interval & 0x3)); + + dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | ((c->isdbt_partial_reception & 1) << 5) | ((c->isdbt_sb_mode & 1) << 4)); + + /* signal optimization parameter */ + if (c->isdbt_partial_reception) { + state->seg_diff_mask = (c->layer[0].modulation == DQPSK) << permu_seg[0]; + for (i = 1; i < 3; i++) + nbseg_diff += (c->layer[i].modulation == DQPSK) * c->layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + state->seg_diff_mask |= 1 << permu_seg[i+1]; + } else { + for (i = 0; i < 3; i++) + nbseg_diff += (c->layer[i].modulation == DQPSK) * c->layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + state->seg_diff_mask |= 1 << permu_seg[i]; + } + + if (state->seg_diff_mask) + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + else + dib8000_write_word(state, 268, (2 << 9) | 39); /*init value */ + + for (i = 0; i < 3; i++) + max_constellation = dib8000_set_layer(state, i, max_constellation); + if (autosearching == 0) { + state->layer_b_nb_seg = c->layer[1].segment_count; + state->layer_c_nb_seg = c->layer[2].segment_count; + } + + /* WRITE: Mode & Diff mask */ + dib8000_write_word(state, 0, (state->mode << 13) | state->seg_diff_mask); + + state->differential_constellation = (state->seg_diff_mask != 0); + + /* channel estimation fine configuration */ + ana_gain = dib8000_adp_fine_tune(state, max_constellation); + + /* update ana_gain depending on max constellation */ + dib8000_update_ana_gain(state, ana_gain); + + /* ---- ANA_FE ---- */ + if (c->isdbt_partial_reception) /* 3-segments */ + dib8000_load_ana_fe_coefs(state, ana_fe_coeff_3seg); + else + dib8000_load_ana_fe_coefs(state, ana_fe_coeff_1seg); /* 1-segment */ + + /* TSB or ISDBT ? apply it now */ + if (c->isdbt_sb_mode) { + dib8000_set_sb_channel(state); + init_prbs = dib8000_get_init_prbs(state, + c->isdbt_sb_subchannel); + } else { + dib8000_set_13seg_channel(state); + init_prbs = 0xfff; + } + + /* SMALL */ + dib8000_small_fine_tune(state); + + dib8000_set_subchannel_prbs(state, init_prbs); + + /* ---- CHAN_BLK ---- */ + for (i = 0; i < 13; i++) { + if ((((~state->seg_diff_mask) >> i) & 1) == 1) { + p_cfr_left_edge += (1 << i) * ((i == 0) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i - 1)) & 1) == 0)); + p_cfr_right_edge += (1 << i) * ((i == 12) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i + 1)) & 1) == 0)); + } + } + dib8000_write_word(state, 222, p_cfr_left_edge); /* p_cfr_left_edge */ + dib8000_write_word(state, 223, p_cfr_right_edge); /* p_cfr_right_edge */ + /* "P_cspu_left_edge" & "P_cspu_right_edge" not used => do not care */ + + dib8000_write_word(state, 189, ~state->seg_mask | state->seg_diff_mask); /* P_lmod4_seg_inh */ + dib8000_write_word(state, 192, ~state->seg_mask | state->seg_diff_mask); /* P_pha3_seg_inh */ + dib8000_write_word(state, 225, ~state->seg_mask | state->seg_diff_mask); /* P_tac_seg_inh */ + + if (!autosearching) + dib8000_write_word(state, 288, (~state->seg_mask | state->seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ + else + dib8000_write_word(state, 288, 0x1fff); /*disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. */ + + dib8000_write_word(state, 211, state->seg_mask & (~state->seg_diff_mask)); /* P_des_seg_enabled */ + dib8000_write_word(state, 287, ~state->seg_mask | 0x1000); /* P_tmcc_seg_inh */ + + dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */ + + /* ---- TMCC ---- */ + for (i = 0; i < 3; i++) + tmcc_pow += (((c->layer[i].modulation == DQPSK) * 4 + 1) * c->layer[i].segment_count) ; + + /* Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); */ + /* Threshold is set at 1/4 of max power. */ + tmcc_pow *= (1 << (9-2)); + dib8000_write_word(state, 290, tmcc_pow); /* P_tmcc_dec_thres_2k */ + dib8000_write_word(state, 291, tmcc_pow); /* P_tmcc_dec_thres_4k */ + dib8000_write_word(state, 292, tmcc_pow); /* P_tmcc_dec_thres_8k */ + /*dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); */ + + /* ---- PHA3 ---- */ + if (state->isdbt_cfg_loaded == 0) + dib8000_write_word(state, 250, 3285); /* p_2d_hspeed_thr0 */ + + state->isdbt_cfg_loaded = 0; +} + +static u32 dib8000_wait_lock(struct dib8000_state *state, u32 internal, + u32 wait0_ms, u32 wait1_ms, u32 wait2_ms) +{ + u32 value = 0; /* P_search_end0 wait time */ + u16 reg = 11; /* P_search_end0 start addr */ + + for (reg = 11; reg < 16; reg += 2) { + if (reg == 11) { + if (state->revision == 0x8090) + value = internal * wait1_ms; + else + value = internal * wait0_ms; + } else if (reg == 13) + value = internal * wait1_ms; + else if (reg == 15) + value = internal * wait2_ms; + dib8000_write_word(state, reg, (u16)((value >> 16) & 0xffff)); + dib8000_write_word(state, (reg + 1), (u16)(value & 0xffff)); + } + return value; +} + +static int dib8000_autosearch_start(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u8 slist = 0; + u32 value, internal = state->cfg.pll->internal; + + if (state->revision == 0x8090) + internal = dib8000_read32(state, 23) / 1000; + + if ((state->revision >= 0x8002) && + (state->autosearch_state == AS_SEARCHING_FFT)) { + dib8000_write_word(state, 37, 0x0065); /* P_ctrl_pha_off_max default values */ + dib8000_write_word(state, 116, 0x0000); /* P_ana_gain to 0 */ + + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x1fff) | (0 << 13) | (1 << 15)); /* P_mode = 0, P_restart_search=1 */ + dib8000_write_word(state, 1, (dib8000_read_word(state, 1) & 0xfffc) | 0); /* P_guard = 0 */ + dib8000_write_word(state, 6, 0); /* P_lock0_mask = 0 */ + dib8000_write_word(state, 7, 0); /* P_lock1_mask = 0 */ + dib8000_write_word(state, 8, 0); /* P_lock2_mask = 0 */ + dib8000_write_word(state, 10, (dib8000_read_word(state, 10) & 0x200) | (16 << 4) | (0 << 0)); /* P_search_list=16, P_search_maxtrial=0 */ + + if (state->revision == 0x8090) + value = dib8000_wait_lock(state, internal, 10, 10, 10); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + else + value = dib8000_wait_lock(state, internal, 20, 20, 20); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + + dib8000_write_word(state, 17, 0); + dib8000_write_word(state, 18, 200); /* P_search_rstst = 200 */ + dib8000_write_word(state, 19, 0); + dib8000_write_word(state, 20, 400); /* P_search_rstend = 400 */ + dib8000_write_word(state, 21, (value >> 16) & 0xffff); /* P_search_checkst */ + dib8000_write_word(state, 22, value & 0xffff); + + if (state->revision == 0x8090) + dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (0 << 8)); /* P_corm_alpha = 0 */ + else + dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (9 << 8)); /* P_corm_alpha = 3 */ + dib8000_write_word(state, 355, 2); /* P_search_param_max = 2 */ + + /* P_search_param_select = (1 | 1<<4 | 1 << 8) */ + dib8000_write_word(state, 356, 0); + dib8000_write_word(state, 357, 0x111); + + dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (1 << 13)); /* P_restart_ccg = 1 */ + dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (0 << 13)); /* P_restart_ccg = 0 */ + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x7ff) | (0 << 15) | (1 << 13)); /* P_restart_search = 0; */ + } else if ((state->revision >= 0x8002) && + (state->autosearch_state == AS_SEARCHING_GUARD)) { + c->transmission_mode = TRANSMISSION_MODE_8K; + c->guard_interval = GUARD_INTERVAL_1_8; + c->inversion = 0; + c->layer[0].modulation = QAM_64; + c->layer[0].fec = FEC_2_3; + c->layer[0].interleaving = 0; + c->layer[0].segment_count = 13; + + slist = 16; + c->transmission_mode = state->found_nfft; + + dib8000_set_isdbt_common_channel(state, slist, 1); + + /* set lock_mask values */ + dib8000_write_word(state, 6, 0x4); + if (state->revision == 0x8090) + dib8000_write_word(state, 7, ((1 << 12) | (1 << 11) | (1 << 10)));/* tmcc_dec_lock, tmcc_sync_lock, tmcc_data_lock, tmcc_bch_uncor */ + else + dib8000_write_word(state, 7, 0x8); + dib8000_write_word(state, 8, 0x1000); + + /* set lock_mask wait time values */ + if (state->revision == 0x8090) + dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + else + dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + + dib8000_write_word(state, 355, 3); /* P_search_param_max = 3 */ + + /* P_search_param_select = 0xf; look for the 4 different guard intervals */ + dib8000_write_word(state, 356, 0); + dib8000_write_word(state, 357, 0xf); + + value = dib8000_read_word(state, 0); + dib8000_write_word(state, 0, (u16)((1 << 15) | value)); + dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */ + dib8000_write_word(state, 0, (u16)value); + } else { + c->inversion = 0; + c->layer[0].modulation = QAM_64; + c->layer[0].fec = FEC_2_3; + c->layer[0].interleaving = 0; + c->layer[0].segment_count = 13; + if (!c->isdbt_sb_mode) + c->layer[0].segment_count = 13; + + /* choose the right list, in sb, always do everything */ + if (c->isdbt_sb_mode) { + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); + } else { + if (c->guard_interval == GUARD_INTERVAL_AUTO) { + if (c->transmission_mode == TRANSMISSION_MODE_AUTO) { + c->transmission_mode = TRANSMISSION_MODE_8K; + c->guard_interval = GUARD_INTERVAL_1_8; + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 to have autosearch start ok with mode2 */ + } else { + c->guard_interval = GUARD_INTERVAL_1_8; + slist = 3; + } + } else { + if (c->transmission_mode == TRANSMISSION_MODE_AUTO) { + c->transmission_mode = TRANSMISSION_MODE_8K; + slist = 2; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 */ + } else + slist = 0; + } + } + dprintk("Using list for autosearch : %d\n", slist); + + dib8000_set_isdbt_common_channel(state, slist, 1); + + /* set lock_mask values */ + dib8000_write_word(state, 6, 0x4); + if (state->revision == 0x8090) + dib8000_write_word(state, 7, (1 << 12) | (1 << 11) | (1 << 10)); + else + dib8000_write_word(state, 7, 0x8); + dib8000_write_word(state, 8, 0x1000); + + /* set lock_mask wait time values */ + if (state->revision == 0x8090) + dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + else + dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + + value = dib8000_read_word(state, 0); + dib8000_write_word(state, 0, (u16)((1 << 15) | value)); + dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */ + dib8000_write_word(state, 0, (u16)value); + } + return 0; +} + +static int dib8000_autosearch_irq(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 irq_pending = dib8000_read_word(state, 1284); + + if ((state->revision >= 0x8002) && + (state->autosearch_state == AS_SEARCHING_FFT)) { + if (irq_pending & 0x1) { + dprintk("dib8000_autosearch_irq: max correlation result available\n"); + return 3; + } + } else { + if (irq_pending & 0x1) { /* failed */ + dprintk("dib8000_autosearch_irq failed\n"); + return 1; + } + + if (irq_pending & 0x2) { /* succeeded */ + dprintk("dib8000_autosearch_irq succeeded\n"); + return 2; + } + } + + return 0; // still pending +} + +static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff) +{ + u16 tmp; + + tmp = dib8000_read_word(state, 771); + if (onoff) /* start P_restart_chd : channel_decoder */ + dib8000_write_word(state, 771, tmp & 0xfffd); + else /* stop P_restart_chd : channel_decoder */ + dib8000_write_word(state, 771, tmp | (1<<1)); +} + +static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) +{ + s16 unit_khz_dds_val; + u32 abs_offset_khz = abs(offset_khz); + u32 dds = state->cfg.pll->ifreq & 0x1ffffff; + u8 invert = !!(state->cfg.pll->ifreq & (1 << 25)); + u8 ratio; + + if (state->revision == 0x8090) { + ratio = 4; + unit_khz_dds_val = (1<<26) / (dib8000_read32(state, 23) / 1000); + if (offset_khz < 0) + dds = (1 << 26) - (abs_offset_khz * unit_khz_dds_val); + else + dds = (abs_offset_khz * unit_khz_dds_val); + + if (invert) + dds = (1<<26) - dds; + } else { + ratio = 2; + unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal); + + if (offset_khz < 0) + unit_khz_dds_val *= -1; + + /* IF tuner */ + if (invert) + dds -= abs_offset_khz * unit_khz_dds_val; + else + dds += abs_offset_khz * unit_khz_dds_val; + } + + dprintk("setting a DDS frequency offset of %c%dkHz\n", invert ? '-' : ' ', dds / unit_khz_dds_val); + + if (abs_offset_khz <= (state->cfg.pll->internal / ratio)) { + /* Max dds offset is the half of the demod freq */ + dib8000_write_word(state, 26, invert); + dib8000_write_word(state, 27, (u16)(dds >> 16) & 0x1ff); + dib8000_write_word(state, 28, (u16)(dds & 0xffff)); + } +} + +static void dib8000_set_frequency_offset(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int i; + u32 current_rf; + int total_dds_offset_khz; + + if (state->fe[0]->ops.tuner_ops.get_frequency) + state->fe[0]->ops.tuner_ops.get_frequency(state->fe[0], ¤t_rf); + else + current_rf = c->frequency; + current_rf /= 1000; + total_dds_offset_khz = (int)current_rf - (int)c->frequency / 1000; + + if (c->isdbt_sb_mode) { + state->subchannel = c->isdbt_sb_subchannel; + + i = dib8000_read_word(state, 26) & 1; /* P_dds_invspec */ + dib8000_write_word(state, 26, c->inversion ^ i); + + if (state->cfg.pll->ifreq == 0) { /* low if tuner */ + if ((c->inversion ^ i) == 0) + dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); + } else { + if ((c->inversion ^ i) == 0) + total_dds_offset_khz *= -1; + } + } + + dprintk("%dkhz tuner offset (frequency = %dHz & current_rf = %dHz) total_dds_offset_hz = %d\n", c->frequency - current_rf, c->frequency, current_rf, total_dds_offset_khz); + + /* apply dds offset now */ + dib8000_set_dds(state, total_dds_offset_khz); +} + +static u16 LUT_isdbt_symbol_duration[4] = { 26, 101, 63 }; + +static u32 dib8000_get_symbol_duration(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u16 i; + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + i = 0; + break; + case TRANSMISSION_MODE_4K: + i = 2; + break; + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + i = 1; + break; + } + + return (LUT_isdbt_symbol_duration[i] / (c->bandwidth_hz / 1000)) + 1; +} + +static void dib8000_set_isdbt_loop_params(struct dib8000_state *state, enum param_loop_step loop_step) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u16 reg_32 = 0, reg_37 = 0; + + switch (loop_step) { + case LOOP_TUNE_1: + if (c->isdbt_sb_mode) { + if (c->isdbt_partial_reception == 0) { + reg_32 = ((11 - state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x40 */ + reg_37 = (3 << 5) | (0 << 4) | (10 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ + } else { /* Sound Broadcasting mode 3 seg */ + reg_32 = ((10 - state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x60 */ + reg_37 = (3 << 5) | (0 << 4) | (9 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (9-P_mode) */ + } + } else { /* 13-seg start conf offset loop parameters */ + reg_32 = ((9 - state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ + reg_37 = (3 << 5) | (0 << 4) | (8 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ + } + break; + case LOOP_TUNE_2: + if (c->isdbt_sb_mode) { + if (c->isdbt_partial_reception == 0) { /* Sound Broadcasting mode 1 seg */ + reg_32 = ((13-state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40*/ + reg_37 = (12-state->mode) | ((5 + state->mode) << 5); + } else { /* Sound Broadcasting mode 3 seg */ + reg_32 = ((12-state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 */ + reg_37 = (11-state->mode) | ((5 + state->mode) << 5); + } + } else { /* 13 seg */ + reg_32 = ((11-state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 */ + reg_37 = ((5+state->mode) << 5) | (10 - state->mode); + } + break; + } + dib8000_write_word(state, 32, reg_32); + dib8000_write_word(state, 37, reg_37); +} + +static void dib8000_demod_restart(struct dib8000_state *state) +{ + dib8000_write_word(state, 770, 0x4000); + dib8000_write_word(state, 770, 0x0000); + return; +} + +static void dib8000_set_sync_wait(struct dib8000_state *state) +{ + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + u16 sync_wait = 64; + + /* P_dvsy_sync_wait - reuse mode */ + switch (c->transmission_mode) { + case TRANSMISSION_MODE_8K: + sync_wait = 256; + break; + case TRANSMISSION_MODE_4K: + sync_wait = 128; + break; + default: + case TRANSMISSION_MODE_2K: + sync_wait = 64; + break; + } + + if (state->cfg.diversity_delay == 0) + sync_wait = (sync_wait * (1 << (c->guard_interval)) * 3) / 2 + 48; /* add 50% SFN margin + compensate for one DVSY-fifo */ + else + sync_wait = (sync_wait * (1 << (c->guard_interval)) * 3) / 2 + state->cfg.diversity_delay; /* add 50% SFN margin + compensate for DVSY-fifo */ + + dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | (sync_wait << 4)); +} + +static unsigned long dib8000_get_timeout(struct dib8000_state *state, u32 delay, enum timeout_mode mode) +{ + if (mode == SYMBOL_DEPENDENT_ON) + delay *= state->symbol_duration; + + return jiffies + usecs_to_jiffies(delay * 100); +} + +static s32 dib8000_get_status(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + return state->status; +} + +static enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + return state->tune_state; +} + +static int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + struct dib8000_state *state = fe->demodulator_priv; + + state->tune_state = tune_state; + return 0; +} + +static int dib8000_tune_restart_from_demod(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + state->status = FE_STATUS_TUNE_PENDING; + state->tune_state = CT_DEMOD_START; + return 0; +} + +static u16 dib8000_read_lock(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + if (state->revision == 0x8090) + return dib8000_read_word(state, 570); + return dib8000_read_word(state, 568); +} + +static int dib8090p_init_sdram(struct dib8000_state *state) +{ + u16 reg = 0; + dprintk("init sdram\n"); + + reg = dib8000_read_word(state, 274) & 0xfff0; + dib8000_write_word(state, 274, reg | 0x7); /* P_dintlv_delay_ram = 7 because of MobileSdram */ + + dib8000_write_word(state, 1803, (7 << 2)); + + reg = dib8000_read_word(state, 1280); + dib8000_write_word(state, 1280, reg | (1 << 2)); /* force restart P_restart_sdram */ + dib8000_write_word(state, 1280, reg); /* release restart P_restart_sdram */ + + return 0; +} + +/** + * is_manual_mode - Check if TMCC should be used for parameters settings + * @c: struct dvb_frontend_properties + * + * By default, TMCC table should be used for parameter settings on most + * usercases. However, sometimes it is desirable to lock the demod to + * use the manual parameters. + * + * On manual mode, the current dib8000_tune state machine is very restrict: + * It requires that both per-layer and per-transponder parameters to be + * properly specified, otherwise the device won't lock. + * + * Check if all those conditions are properly satisfied before allowing + * the device to use the manual frequency lock mode. + */ +static int is_manual_mode(struct dtv_frontend_properties *c) +{ + int i, n_segs = 0; + + /* Use auto mode on DVB-T compat mode */ + if (c->delivery_system != SYS_ISDBT) + return 0; + + /* + * Transmission mode is only detected on auto mode, currently + */ + if (c->transmission_mode == TRANSMISSION_MODE_AUTO) { + dprintk("transmission mode auto\n"); + return 0; + } + + /* + * Guard interval is only detected on auto mode, currently + */ + if (c->guard_interval == GUARD_INTERVAL_AUTO) { + dprintk("guard interval auto\n"); + return 0; + } + + /* + * If no layer is enabled, assume auto mode, as at least one + * layer should be enabled + */ + if (!c->isdbt_layer_enabled) { + dprintk("no layer modulation specified\n"); + return 0; + } + + /* + * Check if the per-layer parameters aren't auto and + * disable a layer if segment count is 0 or invalid. + */ + for (i = 0; i < 3; i++) { + if (!(c->isdbt_layer_enabled & 1 << i)) + continue; + + if ((c->layer[i].segment_count > 13) || + (c->layer[i].segment_count == 0)) { + c->isdbt_layer_enabled &= ~(1 << i); + continue; + } + + n_segs += c->layer[i].segment_count; + + if ((c->layer[i].modulation == QAM_AUTO) || + (c->layer[i].fec == FEC_AUTO)) { + dprintk("layer %c has either modulation or FEC auto\n", + 'A' + i); + return 0; + } + } + + /* + * Userspace specified a wrong number of segments. + * fallback to auto mode. + */ + if (n_segs == 0 || n_segs > 13) { + dprintk("number of segments is invalid\n"); + return 0; + } + + /* Everything looks ok for manual mode */ + return 1; +} + +static int dib8000_tune(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + enum frontend_tune_state *tune_state = &state->tune_state; + + u16 locks, deeper_interleaver = 0, i; + int ret = 1; /* 1 symbol duration (in 100us unit) delay most of the time */ + + unsigned long *timeout = &state->timeout; + unsigned long now = jiffies; + u16 init_prbs; +#ifdef DIB8000_AGC_FREEZE + u16 agc1, agc2; +#endif + + u32 corm[4] = {0, 0, 0, 0}; + u8 find_index, max_value; + +#if 0 + if (*tune_state < CT_DEMOD_STOP) + dprintk("IN: context status = %d, TUNE_STATE %d autosearch step = %u jiffies = %lu\n", + state->channel_parameters_set, *tune_state, state->autosearch_state, now); +#endif + + switch (*tune_state) { + case CT_DEMOD_START: /* 30 */ + dib8000_reset_stats(fe); + + if (state->revision == 0x8090) + dib8090p_init_sdram(state); + state->status = FE_STATUS_TUNE_PENDING; + state->channel_parameters_set = is_manual_mode(c); + + dprintk("Tuning channel on %s search mode\n", + state->channel_parameters_set ? "manual" : "auto"); + + dib8000_viterbi_state(state, 0); /* force chan dec in restart */ + + /* Layer monitor */ + dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); + + dib8000_set_frequency_offset(state); + dib8000_set_bandwidth(fe, c->bandwidth_hz / 1000); + + if (state->channel_parameters_set == 0) { /* The channel struct is unknown, search it ! */ +#ifdef DIB8000_AGC_FREEZE + if (state->revision != 0x8090) { + state->agc1_max = dib8000_read_word(state, 108); + state->agc1_min = dib8000_read_word(state, 109); + state->agc2_max = dib8000_read_word(state, 110); + state->agc2_min = dib8000_read_word(state, 111); + agc1 = dib8000_read_word(state, 388); + agc2 = dib8000_read_word(state, 389); + dib8000_write_word(state, 108, agc1); + dib8000_write_word(state, 109, agc1); + dib8000_write_word(state, 110, agc2); + dib8000_write_word(state, 111, agc2); + } +#endif + state->autosearch_state = AS_SEARCHING_FFT; + state->found_nfft = TRANSMISSION_MODE_AUTO; + state->found_guard = GUARD_INTERVAL_AUTO; + *tune_state = CT_DEMOD_SEARCH_NEXT; + } else { /* we already know the channel struct so TUNE only ! */ + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STEP_3; + } + state->symbol_duration = dib8000_get_symbol_duration(state); + break; + + case CT_DEMOD_SEARCH_NEXT: /* 51 */ + dib8000_autosearch_start(fe); + if (state->revision == 0x8090) + ret = 50; + else + ret = 15; + *tune_state = CT_DEMOD_STEP_1; + break; + + case CT_DEMOD_STEP_1: /* 31 */ + switch (dib8000_autosearch_irq(fe)) { + case 1: /* fail */ + state->status = FE_STATUS_TUNE_FAILED; + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STOP; /* else we are done here */ + break; + case 2: /* Success */ + state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */ + *tune_state = CT_DEMOD_STEP_3; + if (state->autosearch_state == AS_SEARCHING_GUARD) + *tune_state = CT_DEMOD_STEP_2; + else + state->autosearch_state = AS_DONE; + break; + case 3: /* Autosearch FFT max correlation endded */ + *tune_state = CT_DEMOD_STEP_2; + break; + } + break; + + case CT_DEMOD_STEP_2: + switch (state->autosearch_state) { + case AS_SEARCHING_FFT: + /* searching for the correct FFT */ + if (state->revision == 0x8090) { + corm[2] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); + corm[1] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); + corm[0] = (dib8000_read_word(state, 600) << 16) | (dib8000_read_word(state, 601)); + } else { + corm[2] = (dib8000_read_word(state, 594) << 16) | (dib8000_read_word(state, 595)); + corm[1] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); + corm[0] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); + } + /* dprintk("corm fft: %u %u %u\n", corm[0], corm[1], corm[2]); */ + + max_value = 0; + for (find_index = 1 ; find_index < 3 ; find_index++) { + if (corm[max_value] < corm[find_index]) + max_value = find_index ; + } + + switch (max_value) { + case 0: + state->found_nfft = TRANSMISSION_MODE_2K; + break; + case 1: + state->found_nfft = TRANSMISSION_MODE_4K; + break; + case 2: + default: + state->found_nfft = TRANSMISSION_MODE_8K; + break; + } + /* dprintk("Autosearch FFT has found Mode %d\n", max_value + 1); */ + + *tune_state = CT_DEMOD_SEARCH_NEXT; + state->autosearch_state = AS_SEARCHING_GUARD; + if (state->revision == 0x8090) + ret = 50; + else + ret = 10; + break; + case AS_SEARCHING_GUARD: + /* searching for the correct guard interval */ + if (state->revision == 0x8090) + state->found_guard = dib8000_read_word(state, 572) & 0x3; + else + state->found_guard = dib8000_read_word(state, 570) & 0x3; + /* dprintk("guard interval found=%i\n", state->found_guard); */ + + *tune_state = CT_DEMOD_STEP_3; + break; + default: + /* the demod should never be in this state */ + state->status = FE_STATUS_TUNE_FAILED; + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STOP; /* else we are done here */ + break; + } + break; + + case CT_DEMOD_STEP_3: /* 33 */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1); + dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */ + *tune_state = CT_DEMOD_STEP_4; + break; + + case CT_DEMOD_STEP_4: /* (34) */ + dib8000_demod_restart(state); + + dib8000_set_sync_wait(state); + dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); + + locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */ + /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this length to lock */ + *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON); + *tune_state = CT_DEMOD_STEP_5; + break; + + case CT_DEMOD_STEP_5: /* (35) */ + locks = dib8000_read_lock(fe); + if (locks & (0x3 << 11)) { /* coff-lock and off_cpil_lock achieved */ + dib8000_update_timf(state); /* we achieved a coff_cpil_lock - it's time to update the timf */ + if (!state->differential_constellation) { + /* 2 times lmod4_win_len + 10 symbols (pipe delay after coff + nb to compute a 1st correlation) */ + *timeout = dib8000_get_timeout(state, (20 * ((dib8000_read_word(state, 188)>>5)&0x1f)), SYMBOL_DEPENDENT_ON); + *tune_state = CT_DEMOD_STEP_7; + } else { + *tune_state = CT_DEMOD_STEP_8; + } + } else if (time_after(now, *timeout)) { + *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + } + break; + + case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */ + if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) { + /* if there is a diversity fe in input and this fe is has not already failed : wait here until this fe has succeeded or failed */ + if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */ + *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */ + else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failed also, break the current one */ + *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + state->status = FE_STATUS_TUNE_FAILED; + } + } else { + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ + state->status = FE_STATUS_TUNE_FAILED; + } + break; + + case CT_DEMOD_STEP_7: /* 37 */ + locks = dib8000_read_lock(fe); + if (locks & (1<<10)) { /* lmod4_lock */ + ret = 14; /* wait for 14 symbols */ + *tune_state = CT_DEMOD_STEP_8; + } else if (time_after(now, *timeout)) + *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + break; + + case CT_DEMOD_STEP_8: /* 38 */ + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + + /* mpeg will never lock on this condition because init_prbs is not set : search for it !*/ + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) { + state->subchannel = 0; + *tune_state = CT_DEMOD_STEP_11; + } else { + *tune_state = CT_DEMOD_STEP_9; + state->status = FE_STATUS_LOCKED; + } + break; + + case CT_DEMOD_STEP_9: /* 39 */ + if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */ + /* defines timeout for mpeg lock depending on interleaver length of longest layer */ + for (i = 0; i < 3; i++) { + if (c->layer[i].interleaving >= deeper_interleaver) { + dprintk("layer%i: time interleaver = %d\n", i, c->layer[i].interleaving); + if (c->layer[i].segment_count > 0) { /* valid layer */ + deeper_interleaver = c->layer[0].interleaving; + state->longest_intlv_layer = i; + } + } + } + + if (deeper_interleaver == 0) + locks = 2; /* locks is the tmp local variable name */ + else if (deeper_interleaver == 3) + locks = 8; + else + locks = 2 * deeper_interleaver; + + if (state->diversity_onoff != 0) /* because of diversity sync */ + locks *= 2; + + *timeout = now + msecs_to_jiffies(200 * locks); /* give the mpeg lock 800ms if sram is present */ + dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %ld\n", + deeper_interleaver, state->longest_intlv_layer, locks, *timeout); + + *tune_state = CT_DEMOD_STEP_10; + } else + *tune_state = CT_DEMOD_STOP; + break; + + case CT_DEMOD_STEP_10: /* 40 */ + locks = dib8000_read_lock(fe); + if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */ + dprintk("ISDB-T layer locks: Layer A %s, Layer B %s, Layer C %s\n", + c->layer[0].segment_count ? (locks >> 7) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[1].segment_count ? (locks >> 6) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[2].segment_count ? (locks >> 5) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled"); + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) + /* signal to the upper layer, that there was a channel found and the parameters can be read */ + state->status = FE_STATUS_DEMOD_SUCCESS; + else + state->status = FE_STATUS_DATA_LOCKED; + *tune_state = CT_DEMOD_STOP; + } else if (time_after(now, *timeout)) { + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) { /* continue to try init prbs autosearch */ + state->subchannel += 3; + *tune_state = CT_DEMOD_STEP_11; + } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */ + if (locks & (0x7 << 5)) { + dprintk("Not all ISDB-T layers locked in %d ms: Layer A %s, Layer B %s, Layer C %s\n", + jiffies_to_msecs(now - *timeout), + c->layer[0].segment_count ? (locks >> 7) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[1].segment_count ? (locks >> 6) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[2].segment_count ? (locks >> 5) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled"); + + state->status = FE_STATUS_DATA_LOCKED; + } else + state->status = FE_STATUS_TUNE_FAILED; + *tune_state = CT_DEMOD_STOP; + } + } + break; + + case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */ + init_prbs = dib8000_get_init_prbs(state, state->subchannel); + + if (init_prbs) { + dib8000_set_subchannel_prbs(state, init_prbs); + *tune_state = CT_DEMOD_STEP_9; + } else { + *tune_state = CT_DEMOD_STOP; + state->status = FE_STATUS_TUNE_FAILED; + } + break; + + default: + break; + } + + /* tuning is finished - cleanup the demod */ + switch (*tune_state) { + case CT_DEMOD_STOP: /* (42) */ +#ifdef DIB8000_AGC_FREEZE + if ((state->revision != 0x8090) && (state->agc1_max != 0)) { + dib8000_write_word(state, 108, state->agc1_max); + dib8000_write_word(state, 109, state->agc1_min); + dib8000_write_word(state, 110, state->agc2_max); + dib8000_write_word(state, 111, state->agc2_min); + state->agc1_max = 0; + state->agc1_min = 0; + state->agc2_max = 0; + state->agc2_min = 0; + } +#endif + ret = 0; + break; + default: + break; + } + + if ((ret > 0) && (*tune_state > CT_DEMOD_STEP_3)) + return ret * state->symbol_duration; + if ((ret > 0) && (ret < state->symbol_duration)) + return state->symbol_duration; /* at least one symbol */ + return ret; +} + +static int dib8000_wakeup(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + int ret; + + dib8000_set_power_mode(state, DIB8000_POWER_ALL); + dib8000_set_adc_state(state, DIBX000_ADC_ON); + if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) + dprintk("could not start Slow ADC\n"); + + if (state->revision == 0x8090) + dib8000_sad_calib(state); + + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int dib8000_sleep(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + int ret; + + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); + if (ret < 0) + return ret; + } + + if (state->revision != 0x8090) + dib8000_set_output_mode(fe, OUTMODE_HIGH_Z); + dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); + return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); +} + +static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat); + +static int dib8000_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 i, val = 0; + enum fe_status stat = 0; + u8 index_frontend, sub_index_frontend; + + c->bandwidth_hz = 6000000; + + /* + * If called to early, get_frontend makes dib8000_tune to either + * not lock or not sync. This causes dvbv5-scan/dvbv5-zap to fail. + * So, let's just return if frontend 0 has not locked. + */ + dib8000_read_status(fe, &stat); + if (!(stat & FE_HAS_SYNC)) + return 0; + + dprintk("dib8000_get_frontend: TMCC lock\n"); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); + if (stat&FE_HAS_SYNC) { + dprintk("TMCC lock on the slave%i\n", index_frontend); + /* synchronize the cache with the other frontends */ + state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c); + for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { + if (sub_index_frontend != index_frontend) { + state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; + state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[sub_index_frontend]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; + for (i = 0; i < 3; i++) { + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; + state->fe[sub_index_frontend]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; + } + } + } + return 0; + } + } + + c->isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1; + + if (state->revision == 0x8090) + val = dib8000_read_word(state, 572); + else + val = dib8000_read_word(state, 570); + c->inversion = (val & 0x40) >> 6; + switch ((val & 0x30) >> 4) { + case 1: + c->transmission_mode = TRANSMISSION_MODE_2K; + dprintk("dib8000_get_frontend: transmission mode 2K\n"); + break; + case 2: + c->transmission_mode = TRANSMISSION_MODE_4K; + dprintk("dib8000_get_frontend: transmission mode 4K\n"); + break; + case 3: + default: + c->transmission_mode = TRANSMISSION_MODE_8K; + dprintk("dib8000_get_frontend: transmission mode 8K\n"); + break; + } + + switch (val & 0x3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + dprintk("dib8000_get_frontend: Guard Interval = 1/32\n"); + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + dprintk("dib8000_get_frontend: Guard Interval = 1/16\n"); + break; + case 2: + dprintk("dib8000_get_frontend: Guard Interval = 1/8\n"); + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + dprintk("dib8000_get_frontend: Guard Interval = 1/4\n"); + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + val = dib8000_read_word(state, 505); + c->isdbt_partial_reception = val & 1; + dprintk("dib8000_get_frontend: partial_reception = %d\n", c->isdbt_partial_reception); + + for (i = 0; i < 3; i++) { + int show; + + val = dib8000_read_word(state, 493 + i) & 0x0f; + c->layer[i].segment_count = val; + + if (val == 0 || val > 13) + show = 0; + else + show = 1; + + if (show) + dprintk("dib8000_get_frontend: Layer %d segments = %d\n", + i, c->layer[i].segment_count); + + val = dib8000_read_word(state, 499 + i) & 0x3; + /* Interleaving can be 0, 1, 2 or 4 */ + if (val == 3) + val = 4; + c->layer[i].interleaving = val; + if (show) + dprintk("dib8000_get_frontend: Layer %d time_intlv = %d\n", + i, c->layer[i].interleaving); + + val = dib8000_read_word(state, 481 + i); + switch (val & 0x7) { + case 1: + c->layer[i].fec = FEC_1_2; + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 1/2\n", i); + break; + case 2: + c->layer[i].fec = FEC_2_3; + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 2/3\n", i); + break; + case 3: + c->layer[i].fec = FEC_3_4; + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 3/4\n", i); + break; + case 5: + c->layer[i].fec = FEC_5_6; + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 5/6\n", i); + break; + default: + c->layer[i].fec = FEC_7_8; + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 7/8\n", i); + break; + } + + val = dib8000_read_word(state, 487 + i); + switch (val & 0x3) { + case 0: + c->layer[i].modulation = DQPSK; + if (show) + dprintk("dib8000_get_frontend: Layer %d DQPSK\n", i); + break; + case 1: + c->layer[i].modulation = QPSK; + if (show) + dprintk("dib8000_get_frontend: Layer %d QPSK\n", i); + break; + case 2: + c->layer[i].modulation = QAM_16; + if (show) + dprintk("dib8000_get_frontend: Layer %d QAM16\n", i); + break; + case 3: + default: + c->layer[i].modulation = QAM_64; + if (show) + dprintk("dib8000_get_frontend: Layer %d QAM64\n", i); + break; + } + } + + /* synchronize the cache with the other frontends */ + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = c->isdbt_sb_mode; + state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion; + state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode; + state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval; + state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = c->isdbt_partial_reception; + for (i = 0; i < 3; i++) { + state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = c->layer[i].segment_count; + state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = c->layer[i].interleaving; + state->fe[index_frontend]->dtv_property_cache.layer[i].fec = c->layer[i].fec; + state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = c->layer[i].modulation; + } + } + return 0; +} + +static int dib8000_set_frontend(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int l, i, active, time, time_slave = 0; + u8 exit_condition, index_frontend; + unsigned long delay, callback_time; + + if (c->frequency == 0) { + dprintk("dib8000: must at least specify frequency\n"); + return 0; + } + + if (c->bandwidth_hz == 0) { + dprintk("dib8000: no bandwidth specified, set to default\n"); + c->bandwidth_hz = 6000000; + } + + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + /* synchronization of the cache */ + state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; + memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); + + /* set output mode and diversity input */ + if (state->revision != 0x8090) { + dib8000_set_diversity_in(state->fe[index_frontend], 1); + if (index_frontend != 0) + dib8000_set_output_mode(state->fe[index_frontend], + OUTMODE_DIVERSITY); + else + dib8000_set_output_mode(state->fe[0], OUTMODE_HIGH_Z); + } else { + dib8096p_set_diversity_in(state->fe[index_frontend], 1); + if (index_frontend != 0) + dib8096p_set_output_mode(state->fe[index_frontend], + OUTMODE_DIVERSITY); + else + dib8096p_set_output_mode(state->fe[0], OUTMODE_HIGH_Z); + } + + /* tune the tuner */ + if (state->fe[index_frontend]->ops.tuner_ops.set_params) + state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend]); + + dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START); + } + + /* turn off the diversity of the last chip */ + if (state->revision != 0x8090) + dib8000_set_diversity_in(state->fe[index_frontend - 1], 0); + else + dib8096p_set_diversity_in(state->fe[index_frontend - 1], 0); + + /* start up the AGC */ + do { + time = dib8000_agc_startup(state->fe[0]); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + time_slave = dib8000_agc_startup(state->fe[index_frontend]); + if (time == 0) + time = time_slave; + else if ((time_slave != 0) && (time_slave > time)) + time = time_slave; + } + if (time == 0) + break; + + /* + * Despite dib8000_agc_startup returns time at a 0.1 ms range, + * the actual sleep time depends on CONFIG_HZ. The worse case + * is when CONFIG_HZ=100. In such case, the minimum granularity + * is 10ms. On some real field tests, the tuner sometimes don't + * lock when this timer is lower than 10ms. So, enforce a 10ms + * granularity. + */ + time = 10 * (time + 99)/100; + usleep_range(time * 1000, (time + 1) * 1000); + exit_condition = 1; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) { + exit_condition = 0; + break; + } + } + } while (exit_condition == 0); + + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); + + active = 1; + do { + callback_time = 0; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + delay = dib8000_tune(state->fe[index_frontend]); + if (delay != 0) { + delay = jiffies + usecs_to_jiffies(100 * delay); + if (!callback_time || delay < callback_time) + callback_time = delay; + } + + /* we are in autosearch */ + if (state->channel_parameters_set == 0) { /* searching */ + if ((dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_DEMOD_SUCCESS) || (dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_FFT_SUCCESS)) { + dprintk("autosearch succeeded on fe%i\n", index_frontend); + dib8000_get_frontend(state->fe[index_frontend], c); /* we read the channel parameters from the frontend which was successful */ + state->channel_parameters_set = 1; + + for (l = 0; (l < MAX_NUMBER_OF_FRONTENDS) && (state->fe[l] != NULL); l++) { + if (l != index_frontend) { /* and for all frontend except the successful one */ + dprintk("Restarting frontend %d\n", l); + dib8000_tune_restart_from_demod(state->fe[l]); + + state->fe[l]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; + state->fe[l]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[l]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[l]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[l]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; + for (i = 0; i < 3; i++) { + state->fe[l]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; + state->fe[l]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; + state->fe[l]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; + state->fe[l]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; + } + + } + } + } + } + } + /* tuning is done when the master frontend is done (failed or success) */ + if (dib8000_get_status(state->fe[0]) == FE_STATUS_TUNE_FAILED || + dib8000_get_status(state->fe[0]) == FE_STATUS_LOCKED || + dib8000_get_status(state->fe[0]) == FE_STATUS_DATA_LOCKED) { + active = 0; + /* we need to wait for all frontends to be finished */ + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_DEMOD_STOP) + active = 1; + } + if (active == 0) + dprintk("tuning done with status %d\n", dib8000_get_status(state->fe[0])); + } + + if ((active == 1) && (callback_time == 0)) { + dprintk("strange callback time something went wrong\n"); + active = 0; + } + + while ((active == 1) && (time_before(jiffies, callback_time))) + msleep(100); + } while (active); + + /* set output mode */ + if (state->revision != 0x8090) + dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); + else { + dib8096p_set_output_mode(state->fe[0], state->cfg.output_mode); + if (state->cfg.enMpegOutput == 0) { + dib8096p_setDibTxMux(state, MPEG_ON_DIBTX); + dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); + } + } + + return 0; +} + +static int dib8000_get_stats(struct dvb_frontend *fe, enum fe_status stat); + +static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat) +{ + struct dib8000_state *state = fe->demodulator_priv; + u16 lock_slave = 0, lock; + u8 index_frontend; + + lock = dib8000_read_lock(fe); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + lock_slave |= dib8000_read_lock(state->fe[index_frontend]); + + *stat = 0; + + if (((lock >> 13) & 1) || ((lock_slave >> 13) & 1)) + *stat |= FE_HAS_SIGNAL; + + if (((lock >> 8) & 1) || ((lock_slave >> 8) & 1)) /* Equal */ + *stat |= FE_HAS_CARRIER; + + if ((((lock >> 1) & 0xf) == 0xf) || (((lock_slave >> 1) & 0xf) == 0xf)) /* TMCC_SYNC */ + *stat |= FE_HAS_SYNC; + + if ((((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) && ((lock >> 5) & 7)) /* FEC MPEG */ + *stat |= FE_HAS_LOCK; + + if (((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) { + lock = dib8000_read_word(state, 554); /* Viterbi Layer A */ + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + lock = dib8000_read_word(state, 555); /* Viterbi Layer B */ + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + + lock = dib8000_read_word(state, 556); /* Viterbi Layer C */ + if (lock & 0x01) + *stat |= FE_HAS_VITERBI; + } + dib8000_get_stats(fe, *stat); + + return 0; +} + +static int dib8000_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct dib8000_state *state = fe->demodulator_priv; + + /* 13 segments */ + if (state->revision == 0x8090) + *ber = (dib8000_read_word(state, 562) << 16) | + dib8000_read_word(state, 563); + else + *ber = (dib8000_read_word(state, 560) << 16) | + dib8000_read_word(state, 561); + return 0; +} + +static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct dib8000_state *state = fe->demodulator_priv; + + /* packet error on 13 seg */ + if (state->revision == 0x8090) + *unc = dib8000_read_word(state, 567); + else + *unc = dib8000_read_word(state, 565); + return 0; +} + +static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + u16 val; + + *strength = 0; + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + } + + val = 65535 - dib8000_read_word(state, 390); + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + return 0; +} + +static u32 dib8000_get_snr(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + u32 n, s, exp; + u16 val; + + if (state->revision != 0x8090) + val = dib8000_read_word(state, 542); + else + val = dib8000_read_word(state, 544); + n = (val >> 6) & 0xff; + exp = (val & 0x3f); + if ((exp & 0x20) != 0) + exp -= 0x40; + n <<= exp+16; + + if (state->revision != 0x8090) + val = dib8000_read_word(state, 543); + else + val = dib8000_read_word(state, 545); + s = (val >> 6) & 0xff; + exp = (val & 0x3f); + if ((exp & 0x20) != 0) + exp -= 0x40; + s <<= exp+16; + + if (n > 0) { + u32 t = (s/n) << 16; + return t + ((s << 16) - n*t) / n; + } + return 0xffffffff; +} + +static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend; + u32 snr_master; + + snr_master = dib8000_get_snr(fe); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + snr_master += dib8000_get_snr(state->fe[index_frontend]); + + if ((snr_master >> 16) != 0) { + snr_master = 10*intlog10(snr_master>>16); + *snr = snr_master / ((1 << 24) / 10); + } + else + *snr = 0; + + return 0; +} + +struct per_layer_regs { + u16 lock, ber, per; +}; + +static const struct per_layer_regs per_layer_regs[] = { + { 554, 560, 562 }, + { 555, 576, 578 }, + { 556, 581, 583 }, +}; + +struct linear_segments { + unsigned x; + signed y; +}; + +/* + * Table to estimate signal strength in dBm. + * This table was empirically determinated by measuring the signal + * strength generated by a DTA-2111 RF generator directly connected into + * a dib8076 device (a PixelView PV-D231U stick), using a good quality + * 3 meters RC6 cable and good RC6 connectors. + * The real value can actually be different on other devices, depending + * on several factors, like if LNA is enabled or not, if diversity is + * enabled, type of connectors, etc. + * Yet, it is better to use this measure in dB than a random non-linear + * percentage value, especially for antenna adjustments. + * On my tests, the precision of the measure using this table is about + * 0.5 dB, with sounds reasonable enough. + */ +static struct linear_segments strength_to_db_table[] = { + { 55953, 108500 }, /* -22.5 dBm */ + { 55394, 108000 }, + { 53834, 107000 }, + { 52863, 106000 }, + { 52239, 105000 }, + { 52012, 104000 }, + { 51803, 103000 }, + { 51566, 102000 }, + { 51356, 101000 }, + { 51112, 100000 }, + { 50869, 99000 }, + { 50600, 98000 }, + { 50363, 97000 }, + { 50117, 96000 }, /* -35 dBm */ + { 49889, 95000 }, + { 49680, 94000 }, + { 49493, 93000 }, + { 49302, 92000 }, + { 48929, 91000 }, + { 48416, 90000 }, + { 48035, 89000 }, + { 47593, 88000 }, + { 47282, 87000 }, + { 46953, 86000 }, + { 46698, 85000 }, + { 45617, 84000 }, + { 44773, 83000 }, + { 43845, 82000 }, + { 43020, 81000 }, + { 42010, 80000 }, /* -51 dBm */ + { 0, 0 }, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx; + s32 dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i - 1].y - segments[i].y; + dx = segments[i - 1].x - segments[i].x; + + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y + tmp64; + + return ret; +} + +static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int ini_layer, end_layer, i; + u64 time_us, tmp64; + u32 tmp, denom; + int guard, rate_num, rate_denum = 1, bits_per_symbol, nsegs; + int interleaving = 0, fft_div; + + if (layer >= 0) { + ini_layer = layer; + end_layer = layer + 1; + } else { + ini_layer = 0; + end_layer = 3; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_4: + guard = 4; + break; + case GUARD_INTERVAL_1_8: + guard = 8; + break; + case GUARD_INTERVAL_1_16: + guard = 16; + break; + default: + case GUARD_INTERVAL_1_32: + guard = 32; + break; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + fft_div = 4; + break; + case TRANSMISSION_MODE_4K: + fft_div = 2; + break; + default: + case TRANSMISSION_MODE_8K: + fft_div = 1; + break; + } + + denom = 0; + for (i = ini_layer; i < end_layer; i++) { + nsegs = c->layer[i].segment_count; + if (nsegs == 0 || nsegs > 13) + continue; + + switch (c->layer[i].modulation) { + case DQPSK: + case QPSK: + bits_per_symbol = 2; + break; + case QAM_16: + bits_per_symbol = 4; + break; + default: + case QAM_64: + bits_per_symbol = 6; + break; + } + + switch (c->layer[i].fec) { + case FEC_1_2: + rate_num = 1; + rate_denum = 2; + break; + case FEC_2_3: + rate_num = 2; + rate_denum = 3; + break; + case FEC_3_4: + rate_num = 3; + rate_denum = 4; + break; + case FEC_5_6: + rate_num = 5; + rate_denum = 6; + break; + default: + case FEC_7_8: + rate_num = 7; + rate_denum = 8; + break; + } + + interleaving = c->layer[i].interleaving; + + denom += bits_per_symbol * rate_num * fft_div * nsegs * 384; + } + + /* If all goes wrong, wait for 1s for the next stats */ + if (!denom) + return 0; + + /* Estimate the period for the total bit rate */ + time_us = rate_denum * (1008 * 1562500L); + tmp64 = time_us; + do_div(tmp64, guard); + time_us = time_us + tmp64; + time_us += denom / 2; + do_div(time_us, denom); + + tmp = 1008 * 96 * interleaving; + time_us += tmp + tmp / guard; + + return time_us; +} + +static int dib8000_get_stats(struct dvb_frontend *fe, enum fe_status stat) +{ + struct dib8000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + int i; + int show_per_stats = 0; + u32 time_us = 0, snr, val; + u64 blocks; + s32 db; + u16 strength; + + /* Get Signal strength */ + dib8000_read_signal_strength(fe, &strength); + val = strength; + db = interpolate_value(val, + strength_to_db_table, + ARRAY_SIZE(strength_to_db_table)) - 131000; + c->strength.stat[0].svalue = db; + + /* UCB/BER/CNR measures require lock */ + if (!(stat & FE_HAS_LOCK)) { + c->cnr.len = 1; + c->block_count.len = 1; + c->block_error.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + /* Check if time for stats was elapsed */ + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); + + /* Get SNR */ + snr = dib8000_get_snr(fe); + for (i = 1; i < MAX_NUMBER_OF_FRONTENDS; i++) { + if (state->fe[i]) + snr += dib8000_get_snr(state->fe[i]); + } + snr = snr >> 16; + + if (snr) { + snr = 10 * intlog10(snr); + snr = (1000L * snr) >> 24; + } else { + snr = 0; + } + c->cnr.stat[0].svalue = snr; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + + /* Get UCB measures */ + dib8000_read_unc_blocks(fe, &val); + if (val < state->init_ucb) + state->init_ucb += 0x100000000LL; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = val + state->init_ucb; + + /* Estimate the number of packets based on bitrate */ + if (!time_us) + time_us = dib8000_get_time_us(fe, -1); + + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + + show_per_stats = 1; + } + + /* Get post-BER measures */ + if (time_after(jiffies, state->ber_jiffies_stats)) { + time_us = dib8000_get_time_us(fe, -1); + state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + + dprintk("Next all layers stats available in %u us.\n", time_us); + + dib8000_read_ber(fe, &val); + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += val; + + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += 100000000; + } + + if (state->revision < 0x8002) + return 0; + + c->block_error.len = 4; + c->post_bit_error.len = 4; + c->post_bit_count.len = 4; + + for (i = 0; i < 3; i++) { + unsigned nsegs = c->layer[i].segment_count; + + if (nsegs == 0 || nsegs > 13) + continue; + + time_us = 0; + + if (time_after(jiffies, state->ber_jiffies_stats_layer[i])) { + time_us = dib8000_get_time_us(fe, i); + + state->ber_jiffies_stats_layer[i] = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + dprintk("Next layer %c stats will be available in %u us\n", + 'A' + i, time_us); + + val = dib8000_read_word(state, per_layer_regs[i].ber); + c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[1 + i].uvalue += val; + + c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[1 + i].uvalue += 100000000; + } + + if (show_per_stats) { + val = dib8000_read_word(state, per_layer_regs[i].per); + + c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->block_error.stat[1 + i].uvalue += val; + + if (!time_us) + time_us = dib8000_get_time_us(fe, i); + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + } + } + return 0; +} + +static int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + struct dib8000_state *state = fe->demodulator_priv; + u8 index_frontend = 1; + + while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) + index_frontend++; + if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { + dprintk("set slave fe %p to index %i\n", fe_slave, index_frontend); + state->fe[index_frontend] = fe_slave; + return 0; + } + + dprintk("too many slave frontend\n"); + return -ENOMEM; +} + +static struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ + struct dib8000_state *state = fe->demodulator_priv; + + if (slave_index >= MAX_NUMBER_OF_FRONTENDS) + return NULL; + return state->fe[slave_index]; +} + +static int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, + u8 default_addr, u8 first_addr, u8 is_dib8096p) +{ + int k = 0, ret = 0; + u8 new_addr = 0; + struct i2c_device client = {.adap = host }; + + client.i2c_write_buffer = kzalloc(4, GFP_KERNEL); + if (!client.i2c_write_buffer) { + dprintk("%s: not enough memory\n", __func__); + return -ENOMEM; + } + client.i2c_read_buffer = kzalloc(4, GFP_KERNEL); + if (!client.i2c_read_buffer) { + dprintk("%s: not enough memory\n", __func__); + ret = -ENOMEM; + goto error_memory_read; + } + client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL); + if (!client.i2c_buffer_lock) { + dprintk("%s: not enough memory\n", __func__); + ret = -ENOMEM; + goto error_memory_lock; + } + mutex_init(client.i2c_buffer_lock); + + for (k = no_of_demods - 1; k >= 0; k--) { + /* designated i2c address */ + new_addr = first_addr + (k << 1); + + client.addr = new_addr; + if (!is_dib8096p) + dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ + if (dib8000_identify(&client) == 0) { + /* sram lead in, rdy */ + if (!is_dib8096p) + dib8000_i2c_write16(&client, 1287, 0x0003); + client.addr = default_addr; + if (dib8000_identify(&client) == 0) { + dprintk("#%d: not identified\n", k); + ret = -EINVAL; + goto error; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib8000_i2c_write16(&client, 1286, (1 << 10) | (4 << 6)); + + /* set new i2c address and force divstart */ + dib8000_i2c_write16(&client, 1285, (new_addr << 2) | 0x2); + client.addr = new_addr; + dib8000_identify(&client); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + new_addr = first_addr | (k << 1); + client.addr = new_addr; + + // unforce divstr + dib8000_i2c_write16(&client, 1285, new_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib8000_i2c_write16(&client, 1286, 0); + } + +error: + kfree(client.i2c_buffer_lock); +error_memory_lock: + kfree(client.i2c_read_buffer); +error_memory_read: + kfree(client.i2c_write_buffer); + + return ret; +} + +static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + tune->step_size = 0; + tune->max_drift = 0; + return 0; +} + +static void dib8000_release(struct dvb_frontend *fe) +{ + struct dib8000_state *st = fe->demodulator_priv; + u8 index_frontend; + + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) + dvb_frontend_detach(st->fe[index_frontend]); + + dibx000_exit_i2c_master(&st->i2c_master); + i2c_del_adapter(&st->dib8096p_tuner_adap); + kfree(st->fe[0]); + kfree(st); +} + +static struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + struct dib8000_state *st = fe->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} + +static int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib8000_state *st = fe->demodulator_priv; + u16 val = dib8000_read_word(st, 299) & 0xffef; + val |= (onoff & 0x1) << 4; + + dprintk("pid filter enabled %d\n", onoff); + return dib8000_write_word(st, 299, val); +} + +static int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib8000_state *st = fe->demodulator_priv; + dprintk("Index %x, PID %d, OnOff %d\n", id, pid, onoff); + return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); +} + +static const struct dvb_frontend_ops dib8000_ops = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "DiBcom 8000 ISDB-T", + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib8000_release, + + .init = dib8000_wakeup, + .sleep = dib8000_sleep, + + .set_frontend = dib8000_set_frontend, + .get_tune_settings = dib8000_fe_get_tune_settings, + .get_frontend = dib8000_get_frontend, + + .read_status = dib8000_read_status, + .read_ber = dib8000_read_ber, + .read_signal_strength = dib8000_read_signal_strength, + .read_snr = dib8000_read_snr, + .read_ucblocks = dib8000_read_unc_blocks, +}; + +static struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +{ + struct dvb_frontend *fe; + struct dib8000_state *state; + + dprintk("dib8000_init\n"); + + state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); + if (state == NULL) + return NULL; + fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); + if (fe == NULL) + goto error; + + memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); + state->i2c.adap = i2c_adap; + state->i2c.addr = i2c_addr; + state->i2c.i2c_write_buffer = state->i2c_write_buffer; + state->i2c.i2c_read_buffer = state->i2c_read_buffer; + mutex_init(&state->i2c_buffer_lock); + state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock; + state->gpio_val = cfg->gpio_val; + state->gpio_dir = cfg->gpio_dir; + + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + state->cfg.output_mode = OUTMODE_MPEG2_FIFO; + + state->fe[0] = fe; + fe->demodulator_priv = state; + memcpy(&state->fe[0]->ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); + + state->timf_default = cfg->pll->timf; + + if (dib8000_identify(&state->i2c) == 0) { + kfree(fe); + goto error; + } + + dibx000_init_i2c_master(&state->i2c_master, DIB8000, state->i2c.adap, state->i2c.addr); + + /* init 8096p tuner adapter */ + strscpy(state->dib8096p_tuner_adap.name, "DiB8096P tuner interface", + sizeof(state->dib8096p_tuner_adap.name)); + state->dib8096p_tuner_adap.algo = &dib8096p_tuner_xfer_algo; + state->dib8096p_tuner_adap.algo_data = NULL; + state->dib8096p_tuner_adap.dev.parent = state->i2c.adap->dev.parent; + i2c_set_adapdata(&state->dib8096p_tuner_adap, state); + i2c_add_adapter(&state->dib8096p_tuner_adap); + + dib8000_reset(fe); + + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ + state->current_demod_bw = 6000; + + return fe; + +error: + kfree(state); + return NULL; +} + +void *dib8000_attach(struct dib8000_ops *ops) +{ + if (!ops) + return NULL; + + ops->pwm_agc_reset = dib8000_pwm_agc_reset; + ops->get_dc_power = dib8090p_get_dc_power; + ops->set_gpio = dib8000_set_gpio; + ops->get_slave_frontend = dib8000_get_slave_frontend; + ops->set_tune_state = dib8000_set_tune_state; + ops->pid_filter_ctrl = dib8000_pid_filter_ctrl; + ops->get_adc_power = dib8000_get_adc_power; + ops->update_pll = dib8000_update_pll; + ops->tuner_sleep = dib8096p_tuner_sleep; + ops->get_tune_state = dib8000_get_tune_state; + ops->get_i2c_tuner = dib8096p_get_i2c_tuner; + ops->set_slave_frontend = dib8000_set_slave_frontend; + ops->pid_filter = dib8000_pid_filter; + ops->ctrl_timf = dib8000_ctrl_timf; + ops->init = dib8000_init; + ops->get_i2c_master = dib8000_get_i2c_master; + ops->i2c_enumeration = dib8000_i2c_enumeration; + ops->set_wbd_ref = dib8000_set_wbd_ref; + + return ops; +} +EXPORT_SYMBOL_GPL(dib8000_attach); + +MODULE_AUTHOR("Olivier Grenie <Olivier.Grenie@parrot.com, Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h new file mode 100644 index 0000000000..b920fe7690 --- /dev/null +++ b/drivers/media/dvb-frontends/dib8000.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DIB8000_H +#define DIB8000_H + +#include "dibx000_common.h" + +struct dib8000_config { + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + struct dibx000_bandwidth_config *pll; + +#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB8000_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + u16 pwm_freq_div; + + void (*agc_control) (struct dvb_frontend *, u8 before); + + u16 drives; + u16 diversity_delay; + u8 div_cfg; + u8 output_mode; + u8 refclksel; + u8 enMpegOutput:1; + + struct dibx000_bandwidth_config *plltable; +}; + +#define DEFAULT_DIB8000_I2C_ADDRESS 18 + +struct dib8000_ops { + int (*set_wbd_ref)(struct dvb_frontend *fe, u16 value); + int (*update_pll)(struct dvb_frontend *fe, + struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio); + int (*set_gpio)(struct dvb_frontend *fe, u8 num, u8 dir, u8 val); + void (*pwm_agc_reset)(struct dvb_frontend *fe); + struct i2c_adapter *(*get_i2c_tuner)(struct dvb_frontend *fe); + int (*tuner_sleep)(struct dvb_frontend *fe, int onoff); + s32 (*get_adc_power)(struct dvb_frontend *fe, u8 mode); + int (*get_dc_power)(struct dvb_frontend *fe, u8 IQ); + u32 (*ctrl_timf)(struct dvb_frontend *fe, uint8_t op, uint32_t timf); + enum frontend_tune_state (*get_tune_state)(struct dvb_frontend *fe); + int (*set_tune_state)(struct dvb_frontend *fe, enum frontend_tune_state tune_state); + int (*set_slave_frontend)(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); + struct dvb_frontend *(*get_slave_frontend)(struct dvb_frontend *fe, int slave_index); + int (*i2c_enumeration)(struct i2c_adapter *host, int no_of_demods, + u8 default_addr, u8 first_addr, u8 is_dib8096p); + struct i2c_adapter *(*get_i2c_master)(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating); + int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff); + int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); + struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); +}; + +#if IS_REACHABLE(CONFIG_DVB_DIB8000) +void *dib8000_attach(struct dib8000_ops *ops); +#else +static inline void *dib8000_attach(struct dib8000_ops *ops) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c new file mode 100644 index 0000000000..83cf6eadd4 --- /dev/null +++ b/drivers/media/dvb-frontends/dib9000.c @@ -0,0 +1,2584 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family. + * + * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +#include <linux/int_log.h> +#include <media/dvb_frontend.h> + +#include "dib9000.h" +#include "dibx000_common.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +#define MAX_NUMBER_OF_FRONTENDS 6 + +struct i2c_device { + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + u8 *i2c_read_buffer; + u8 *i2c_write_buffer; +}; + +struct dib9000_pid_ctrl { +#define DIB9000_PID_FILTER_CTRL 0 +#define DIB9000_PID_FILTER 1 + u8 cmd; + u8 id; + u16 pid; + u8 onoff; +}; + +struct dib9000_state { + struct i2c_device i2c; + + struct dibx000_i2c_master i2c_master; + struct i2c_adapter tuner_adap; + struct i2c_adapter component_bus; + + u16 revision; + u8 reg_offs; + + enum frontend_tune_state tune_state; + u32 status; + struct dvb_frontend_parametersContext channel_status; + + u8 fe_id; + +#define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB9000_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB9000_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + union { /* common for all chips */ + struct { + u8 mobile_mode:1; + } host; + + struct { + struct dib9000_fe_memory_map { + u16 addr; + u16 size; + } fe_mm[18]; + u8 memcmd; + + struct mutex mbx_if_lock; /* to protect read/write operations */ + struct mutex mbx_lock; /* to protect the whole mailbox handling */ + + struct mutex mem_lock; /* to protect the memory accesses */ + struct mutex mem_mbx_lock; /* to protect the memory-based mailbox */ + +#define MBX_MAX_WORDS (256 - 200 - 2) +#define DIB9000_MSG_CACHE_SIZE 2 + u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS]; + u8 fw_is_running; + } risc; + } platform; + + union { /* common for all platforms */ + struct { + struct dib9000_config cfg; + } d9; + } chip; + + struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; + u16 component_bus_speed; + + /* for the I2C transfer */ + struct i2c_msg msg[2]; + u8 i2c_write_buffer[255]; + u8 i2c_read_buffer[255]; + struct mutex demod_lock; + u8 get_frontend_internal; + struct dib9000_pid_ctrl pid_ctrl[10]; + s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */ +}; + +static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +enum dib9000_power_mode { + DIB9000_POWER_ALL = 0, + + DIB9000_POWER_NO, + DIB9000_POWER_INTERF_ANALOG_AGC, + DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, + DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD, + DIB9000_POWER_INTERFACE_ONLY, +}; + +enum dib9000_out_messages { + OUT_MSG_HBM_ACK, + OUT_MSG_HOST_BUF_FAIL, + OUT_MSG_REQ_VERSION, + OUT_MSG_BRIDGE_I2C_W, + OUT_MSG_BRIDGE_I2C_R, + OUT_MSG_BRIDGE_APB_W, + OUT_MSG_BRIDGE_APB_R, + OUT_MSG_SCAN_CHANNEL, + OUT_MSG_MONIT_DEMOD, + OUT_MSG_CONF_GPIO, + OUT_MSG_DEBUG_HELP, + OUT_MSG_SUBBAND_SEL, + OUT_MSG_ENABLE_TIME_SLICE, + OUT_MSG_FE_FW_DL, + OUT_MSG_FE_CHANNEL_SEARCH, + OUT_MSG_FE_CHANNEL_TUNE, + OUT_MSG_FE_SLEEP, + OUT_MSG_FE_SYNC, + OUT_MSG_CTL_MONIT, + + OUT_MSG_CONF_SVC, + OUT_MSG_SET_HBM, + OUT_MSG_INIT_DEMOD, + OUT_MSG_ENABLE_DIVERSITY, + OUT_MSG_SET_OUTPUT_MODE, + OUT_MSG_SET_PRIORITARY_CHANNEL, + OUT_MSG_ACK_FRG, + OUT_MSG_INIT_PMU, +}; + +enum dib9000_in_messages { + IN_MSG_DATA, + IN_MSG_FRAME_INFO, + IN_MSG_CTL_MONIT, + IN_MSG_ACK_FREE_ITEM, + IN_MSG_DEBUG_BUF, + IN_MSG_MPE_MONITOR, + IN_MSG_RAWTS_MONITOR, + IN_MSG_END_BRIDGE_I2C_RW, + IN_MSG_END_BRIDGE_APB_RW, + IN_MSG_VERSION, + IN_MSG_END_OF_SCAN, + IN_MSG_MONIT_DEMOD, + IN_MSG_ERROR, + IN_MSG_FE_FW_DL_DONE, + IN_MSG_EVENT, + IN_MSG_ACK_CHANGE_SVC, + IN_MSG_HBM_PROF, +}; + +/* memory_access requests */ +#define FE_MM_W_CHANNEL 0 +#define FE_MM_W_FE_INFO 1 +#define FE_MM_RW_SYNC 2 + +#define FE_SYNC_CHANNEL 1 +#define FE_SYNC_W_GENERIC_MONIT 2 +#define FE_SYNC_COMPONENT_ACCESS 3 + +#define FE_MM_R_CHANNEL_SEARCH_STATE 3 +#define FE_MM_R_CHANNEL_UNION_CONTEXT 4 +#define FE_MM_R_FE_INFO 5 +#define FE_MM_R_FE_MONITOR 6 + +#define FE_MM_W_CHANNEL_HEAD 7 +#define FE_MM_W_CHANNEL_UNION 8 +#define FE_MM_W_CHANNEL_CONTEXT 9 +#define FE_MM_R_CHANNEL_UNION 10 +#define FE_MM_R_CHANNEL_CONTEXT 11 +#define FE_MM_R_CHANNEL_TUNE_STATE 12 + +#define FE_MM_R_GENERIC_MONITORING_SIZE 13 +#define FE_MM_W_GENERIC_MONITORING 14 +#define FE_MM_R_GENERIC_MONITORING 15 + +#define FE_MM_W_COMPONENT_ACCESS 16 +#define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17 +static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len); +static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len); + +static u16 to_fw_output_mode(u16 mode) +{ + switch (mode) { + case OUTMODE_HIGH_Z: + return 0; + case OUTMODE_MPEG2_PAR_GATED_CLK: + return 4; + case OUTMODE_MPEG2_PAR_CONT_CLK: + return 8; + case OUTMODE_MPEG2_SERIAL: + return 16; + case OUTMODE_DIVERSITY: + return 128; + case OUTMODE_MPEG2_FIFO: + return 2; + case OUTMODE_ANALOG_ADC: + return 1; + default: + return 0; + } +} + +static int dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 *b, u32 len, u16 attribute) +{ + u32 chunk_size = 126; + u32 l; + int ret; + + if (state->platform.risc.fw_is_running && (reg < 1024)) + return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len); + + memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c.i2c_addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = 2; + state->msg[1].addr = state->i2c.i2c_addr >> 1; + state->msg[1].flags = I2C_M_RD; + state->msg[1].buf = b; + state->msg[1].len = len; + + state->i2c_write_buffer[0] = reg >> 8; + state->i2c_write_buffer[1] = reg & 0xff; + + if (attribute & DATA_BUS_ACCESS_MODE_8BIT) + state->i2c_write_buffer[0] |= (1 << 5); + if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + state->i2c_write_buffer[0] |= (1 << 4); + + do { + l = min(len, chunk_size); + state->msg[1].len = l; + state->msg[1].buf = b; + ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 2) != 2 ? -EREMOTEIO : 0; + if (ret != 0) { + dprintk("i2c read error on %d\n", reg); + return -EREMOTEIO; + } + + b += l; + len -= l; + + if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) + reg += l / 2; + } while ((ret == 0) && len); + + return 0; +} + +static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg) +{ + struct i2c_msg msg[2] = { + {.addr = i2c->i2c_addr >> 1, .flags = 0, + .buf = i2c->i2c_write_buffer, .len = 2}, + {.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD, + .buf = i2c->i2c_read_buffer, .len = 2}, + }; + + i2c->i2c_write_buffer[0] = reg >> 8; + i2c->i2c_write_buffer[1] = reg & 0xff; + + if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) { + dprintk("read register %x error\n", reg); + return 0; + } + + return (i2c->i2c_read_buffer[0] << 8) | i2c->i2c_read_buffer[1]; +} + +static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg) +{ + if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, 0) != 0) + return 0; + return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +} + +static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute) +{ + if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, + attribute) != 0) + return 0; + return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +} + +#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + +static int dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 *buf, u32 len, u16 attribute) +{ + u32 chunk_size = 126; + u32 l; + int ret; + + if (state->platform.risc.fw_is_running && (reg < 1024)) { + if (dib9000_risc_apb_access_write + (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0) + return -EINVAL; + return 0; + } + + memset(&state->msg[0], 0, sizeof(struct i2c_msg)); + state->msg[0].addr = state->i2c.i2c_addr >> 1; + state->msg[0].flags = 0; + state->msg[0].buf = state->i2c_write_buffer; + state->msg[0].len = len + 2; + + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; + state->i2c_write_buffer[1] = (reg) & 0xff; + + if (attribute & DATA_BUS_ACCESS_MODE_8BIT) + state->i2c_write_buffer[0] |= (1 << 5); + if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + state->i2c_write_buffer[0] |= (1 << 4); + + do { + l = min(len, chunk_size); + state->msg[0].len = l + 2; + memcpy(&state->i2c_write_buffer[2], buf, l); + + ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0; + + buf += l; + len -= l; + + if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) + reg += l / 2; + } while ((ret == 0) && len); + + return ret; +} + +static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +{ + struct i2c_msg msg = { + .addr = i2c->i2c_addr >> 1, .flags = 0, + .buf = i2c->i2c_write_buffer, .len = 4 + }; + + i2c->i2c_write_buffer[0] = (reg >> 8) & 0xff; + i2c->i2c_write_buffer[1] = reg & 0xff; + i2c->i2c_write_buffer[2] = (val >> 8) & 0xff; + i2c->i2c_write_buffer[3] = val & 0xff; + + return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} + +static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val) +{ + u8 b[2] = { val >> 8, val & 0xff }; + return dib9000_write16_attr(state, reg, b, 2, 0); +} + +static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute) +{ + u8 b[2] = { val >> 8, val & 0xff }; + return dib9000_write16_attr(state, reg, b, 2, attribute); +} + +#define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0) +#define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +#define dib9000_write16_noinc_attr(state, reg, buf, len, attribute) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | (attribute)) + +#define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0) +#define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0) + +#define MAC_IRQ (1 << 1) +#define IRQ_POL_MSK (1 << 4) + +#define dib9000_risc_mem_read_chunks(state, b, len) dib9000_read16_attr(state, 1063, b, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +#define dib9000_risc_mem_write_chunks(state, buf, len) dib9000_write16_attr(state, 1063, buf, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) + +static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading) +{ + u8 b[14] = { 0 }; + +/* dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */ +/* b[0] = 0 << 7; */ + b[1] = 1; + +/* b[2] = 0; */ +/* b[3] = 0; */ + b[4] = (u8) (addr >> 8); + b[5] = (u8) (addr & 0xff); + +/* b[10] = 0; */ +/* b[11] = 0; */ + b[12] = (u8) (addr >> 8); + b[13] = (u8) (addr & 0xff); + + addr += len; +/* b[6] = 0; */ +/* b[7] = 0; */ + b[8] = (u8) (addr >> 8); + b[9] = (u8) (addr & 0xff); + + dib9000_write(state, 1056, b, 14); + if (reading) + dib9000_write_word(state, 1056, (1 << 15) | 1); + state->platform.risc.memcmd = -1; /* if it was called directly reset it - to force a future setup-call to set it */ +} + +static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd) +{ + struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f]; + /* decide whether we need to "refresh" the memory controller */ + if (state->platform.risc.memcmd == cmd && /* same command */ + !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */ + return; + dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80); + state->platform.risc.memcmd = cmd; +} + +static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len) +{ + if (!state->platform.risc.fw_is_running) + return -EIO; + + if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + dib9000_risc_mem_setup(state, cmd | 0x80); + dib9000_risc_mem_read_chunks(state, b, len); + mutex_unlock(&state->platform.risc.mem_lock); + return 0; +} + +static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b) +{ + struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd]; + if (!state->platform.risc.fw_is_running) + return -EIO; + + if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + dib9000_risc_mem_setup(state, cmd); + dib9000_risc_mem_write_chunks(state, b, m->size); + mutex_unlock(&state->platform.risc.mem_lock); + return 0; +} + +static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len) +{ + u16 offs; + + if (risc_id == 1) + offs = 16; + else + offs = 0; + + /* config crtl reg */ + dib9000_write_word(state, 1024 + offs, 0x000f); + dib9000_write_word(state, 1025 + offs, 0); + dib9000_write_word(state, 1031 + offs, key); + + dprintk("going to download %dB of microcode\n", len); + if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) { + dprintk("error while downloading microcode for RISC %c\n", 'A' + risc_id); + return -EIO; + } + + dprintk("Microcode for RISC %c loaded\n", 'A' + risc_id); + + return 0; +} + +static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id) +{ + u16 mbox_offs; + u16 reset_reg; + u16 tries = 1000; + + if (risc_id == 1) + mbox_offs = 16; + else + mbox_offs = 0; + + /* Reset mailbox */ + dib9000_write_word(state, 1027 + mbox_offs, 0x8000); + + /* Read reset status */ + do { + reset_reg = dib9000_read_word(state, 1027 + mbox_offs); + msleep(100); + } while ((reset_reg & 0x8000) && --tries); + + if (reset_reg & 0x8000) { + dprintk("MBX: init ERROR, no response from RISC %c\n", 'A' + risc_id); + return -EIO; + } + dprintk("MBX: initialized\n"); + return 0; +} + +#define MAX_MAILBOX_TRY 100 +static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr) +{ + u8 *d, b[2]; + u16 tmp; + u16 size; + u32 i; + int ret = 0; + + if (!state->platform.risc.fw_is_running) + return -EINVAL; + + if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + tmp = MAX_MAILBOX_TRY; + do { + size = dib9000_read_word_attr(state, 1043, attr) & 0xff; + if ((size + len + 1) > MBX_MAX_WORDS && --tmp) { + dprintk("MBX: RISC mbx full, retrying\n"); + msleep(100); + } else + break; + } while (1); + + /*dprintk( "MBX: size: %d\n", size); */ + + if (tmp == 0) { + ret = -EINVAL; + goto out; + } +#ifdef DUMP_MSG + dprintk("--> %02x %d %*ph\n", id, len + 1, len, data); +#endif + + /* byte-order conversion - works on big (where it is not necessary) or little endian */ + d = (u8 *) data; + for (i = 0; i < len; i++) { + tmp = data[i]; + *d++ = tmp >> 8; + *d++ = tmp & 0xff; + } + + /* write msg */ + b[0] = id; + b[1] = len + 1; + if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) { + ret = -EIO; + goto out; + } + + /* update register nb_mes_in_RX */ + ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); + +out: + mutex_unlock(&state->platform.risc.mbx_if_lock); + + return ret; +} + +static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr) +{ +#ifdef DUMP_MSG + u16 *d = data; +#endif + + u16 tmp, i; + u8 size; + u8 mc_base; + + if (!state->platform.risc.fw_is_running) + return 0; + + if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { + dprintk("could not get the lock\n"); + return 0; + } + if (risc_id == 1) + mc_base = 16; + else + mc_base = 0; + + /* Length and type in the first word */ + *data = dib9000_read_word_attr(state, 1029 + mc_base, attr); + + size = *data & 0xff; + if (size <= MBX_MAX_WORDS) { + data++; + size--; /* Initial word already read */ + + dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr); + + /* to word conversion */ + for (i = 0; i < size; i++) { + tmp = *data; + *data = (tmp >> 8) | (tmp << 8); + data++; + } + +#ifdef DUMP_MSG + dprintk("<--\n"); + for (i = 0; i < size + 1; i++) + dprintk("%04x\n", d[i]); + dprintk("\n"); +#endif + } else { + dprintk("MBX: message is too big for message cache (%d), flushing message\n", size); + size--; /* Initial word already read */ + while (size--) + dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr); + } + /* Update register nb_mes_in_TX */ + dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr); + + mutex_unlock(&state->platform.risc.mbx_if_lock); + + return size + 1; +} + +static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size) +{ + u32 ts = data[1] << 16 | data[0]; + char *b = (char *)&data[2]; + + b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */ + if (*b == '~') { + b++; + dprintk("%s\n", b); + } else + dprintk("RISC%d: %d.%04d %s\n", + state->fe_id, + ts / 10000, ts % 10000, *b ? b : "<empty>"); + return 1; +} + +static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr) +{ + int i; + u8 size; + u16 *block; + /* find a free slot */ + for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { + block = state->platform.risc.message_cache[i]; + if (*block == 0) { + size = dib9000_mbx_read(state, block, 1, attr); + +/* dprintk( "MBX: fetched %04x message to cache\n", *block); */ + + switch (*block >> 8) { + case IN_MSG_DEBUG_BUF: + dib9000_risc_debug_buf(state, block + 1, size); /* debug-messages are going to be printed right away */ + *block = 0; /* free the block */ + break; +#if 0 + case IN_MSG_DATA: /* FE-TRACE */ + dib9000_risc_data_process(state, block + 1, size); + *block = 0; + break; +#endif + default: + break; + } + + return 1; + } + } + dprintk("MBX: no free cache-slot found for new message...\n"); + return -1; +} + +static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr) +{ + if (risc_id == 0) + return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f; /* 5 bit field */ + else + return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f; /* 7 bit field */ +} + +static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) +{ + int ret = 0; + + if (!state->platform.risc.fw_is_running) + return -1; + + if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) { + dprintk("could not get the lock\n"); + return -1; + } + + if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ + ret = dib9000_mbx_fetch_to_cache(state, attr); + + dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ +/* if (tmp) */ +/* dprintk( "cleared IRQ: %x\n", tmp); */ + mutex_unlock(&state->platform.risc.mbx_lock); + + return ret; +} + +static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr) +{ + u8 i; + u16 *block; + u16 timeout = 30; + + *msg = 0; + do { + /* dib9000_mbx_get_from_cache(); */ + for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { + block = state->platform.risc.message_cache[i]; + if ((*block >> 8) == id) { + *size = (*block & 0xff) - 1; + memcpy(msg, block + 1, (*size) * 2); + *block = 0; /* free the block */ + i = 0; /* signal that we found a message */ + break; + } + } + + if (i == 0) + break; + + if (dib9000_mbx_process(state, attr) == -1) /* try to fetch one message - if any */ + return -1; + + } while (--timeout); + + if (timeout == 0) { + dprintk("waiting for message %d timed out\n", id); + return -1; + } + + return i == 0; +} + +static int dib9000_risc_check_version(struct dib9000_state *state) +{ + u8 r[4]; + u8 size; + u16 fw_version = 0; + + if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0) + return -EIO; + + if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0) + return -EIO; + + fw_version = (r[0] << 8) | r[1]; + dprintk("RISC: ver: %d.%02d (IC: %d)\n", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]); + + if ((fw_version >> 10) != 7) + return -EINVAL; + + switch (fw_version & 0x3ff) { + case 11: + case 12: + case 14: + case 15: + case 16: + case 17: + break; + default: + dprintk("RISC: invalid firmware version"); + return -EINVAL; + } + + dprintk("RISC: valid firmware version"); + return 0; +} + +static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB) +{ + /* Reconfig pool mac ram */ + dib9000_write_word(state, 1225, 0x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */ + dib9000_write_word(state, 1226, 0x05); + + /* Toggles IP crypto to Host APB interface. */ + dib9000_write_word(state, 1542, 1); + + /* Set jump and no jump in the dma box */ + dib9000_write_word(state, 1074, 0); + dib9000_write_word(state, 1075, 0); + + /* Set MAC as APB Master. */ + dib9000_write_word(state, 1237, 0); + + /* Reset the RISCs */ + if (codeA != NULL) + dib9000_write_word(state, 1024, 2); + else + dib9000_write_word(state, 1024, 15); + if (codeB != NULL) + dib9000_write_word(state, 1040, 2); + + if (codeA != NULL) + dib9000_firmware_download(state, 0, 0x1234, codeA, lenA); + if (codeB != NULL) + dib9000_firmware_download(state, 1, 0x1234, codeB, lenB); + + /* Run the RISCs */ + if (codeA != NULL) + dib9000_write_word(state, 1024, 0); + if (codeB != NULL) + dib9000_write_word(state, 1040, 0); + + if (codeA != NULL) + if (dib9000_mbx_host_init(state, 0) != 0) + return -EIO; + if (codeB != NULL) + if (dib9000_mbx_host_init(state, 1) != 0) + return -EIO; + + msleep(100); + state->platform.risc.fw_is_running = 1; + + if (dib9000_risc_check_version(state) != 0) + return -EINVAL; + + state->platform.risc.memcmd = 0xff; + return 0; +} + +static u16 dib9000_identify(struct i2c_device *client) +{ + u16 value; + + value = dib9000_i2c_read16(client, 896); + if (value != 0x01b3) { + dprintk("wrong Vendor ID (0x%x)\n", value); + return 0; + } + + value = dib9000_i2c_read16(client, 897); + if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) { + dprintk("wrong Device ID (0x%x)\n", value); + return 0; + } + + /* protect this driver to be used with 7000PC */ + if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) { + dprintk("this driver does not work with DiB7000PC\n"); + return 0; + } + + switch (value) { + case 0x4000: + dprintk("found DiB7000MA/PA/MB/PB\n"); + break; + case 0x4001: + dprintk("found DiB7000HC\n"); + break; + case 0x4002: + dprintk("found DiB7000MC\n"); + break; + case 0x4003: + dprintk("found DiB9000A\n"); + break; + case 0x4004: + dprintk("found DiB9000H\n"); + break; + case 0x4005: + dprintk("found DiB9000M\n"); + break; + } + + return value; +} + +static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906; + u8 offset; + + if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005) + offset = 1; + else + offset = 0; + + reg_906 = dib9000_read_word(state, 906 + offset) | 0x3; /* keep settings for RISC */ + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB9000_POWER_ALL: + reg_903 = 0x0000; + reg_904 = 0x0000; + reg_905 = 0x0000; + reg_906 = 0x0000; + break; + + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ + case DIB9000_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); + break; + + case DIB9000_POWER_INTERF_ANALOG_AGC: + reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); + reg_906 &= ~((1 << 0)); + break; + + case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: + reg_903 = 0x0000; + reg_904 = 0x801f; + reg_905 = 0x0000; + reg_906 &= ~((1 << 0)); + break; + + case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD: + reg_903 = 0x0000; + reg_904 = 0x8000; + reg_905 = 0x010b; + reg_906 &= ~((1 << 0)); + break; + default: + case DIB9000_POWER_NO: + break; + } + + /* always power down unused parts */ + if (!state->platform.host.mobile_mode) + reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); + + /* P_sdio_select_clk = 0 on MC and after */ + if (state->revision != 0x4000) + reg_906 <<= 1; + + dib9000_write_word(state, 903 + offset, reg_903); + dib9000_write_word(state, 904 + offset, reg_904); + dib9000_write_word(state, 905 + offset, reg_905); + dib9000_write_word(state, 906 + offset, reg_906); +} + +static int dib9000_fw_reset(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + + dib9000_write_word(state, 1817, 0x0003); + + dib9000_write_word(state, 1227, 1); + dib9000_write_word(state, 1227, 0); + + switch ((state->revision = dib9000_identify(&state->i2c))) { + case 0x4003: + case 0x4004: + case 0x4005: + state->reg_offs = 1; + break; + default: + return -EINVAL; + } + + /* reset the i2c-master to use the host interface */ + dibx000_reset_i2c_master(&state->i2c_master); + + dib9000_set_power_mode(state, DIB9000_POWER_ALL); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1)); + dib9000_write_word(state, 1796, 0); + dib9000_write_word(state, 1805, 0x805); + + /* restart all parts */ + dib9000_write_word(state, 898, 0xffff); + dib9000_write_word(state, 899, 0xffff); + dib9000_write_word(state, 900, 0x0001); + dib9000_write_word(state, 901, 0xff19); + dib9000_write_word(state, 902, 0x003c); + + dib9000_write_word(state, 898, 0); + dib9000_write_word(state, 899, 0); + dib9000_write_word(state, 900, 0); + dib9000_write_word(state, 901, 0); + dib9000_write_word(state, 902, 0); + + dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives); + + dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY); + + return 0; +} + +static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len) +{ + u16 mb[10]; + u8 i, s; + + if (address >= 1024 || !state->platform.risc.fw_is_running) + return -EINVAL; + + /* dprintk( "APB access through rd fw %d %x\n", address, attribute); */ + + mb[0] = (u16) address; + mb[1] = len / 2; + dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute); + switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) { + case 1: + s--; + for (i = 0; i < s; i++) { + b[i * 2] = (mb[i + 1] >> 8) & 0xff; + b[i * 2 + 1] = (mb[i + 1]) & 0xff; + } + return 0; + default: + return -EIO; + } + return -EIO; +} + +static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len) +{ + u16 mb[10]; + u8 s, i; + + if (address >= 1024 || !state->platform.risc.fw_is_running) + return -EINVAL; + + if (len > 18) + return -EINVAL; + + /* dprintk( "APB access through wr fw %d %x\n", address, attribute); */ + + mb[0] = (u16)address; + for (i = 0; i + 1 < len; i += 2) + mb[1 + i / 2] = b[i] << 8 | b[i + 1]; + if (len & 1) + mb[1 + len / 2] = b[len - 1] << 8; + + dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, (3 + len) / 2, attribute); + return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL; +} + +static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i) +{ + u8 index_loop = 10; + + if (!state->platform.risc.fw_is_running) + return 0; + dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i); + do { + dib9000_risc_mem_read(state, FE_MM_RW_SYNC, state->i2c_read_buffer, 1); + } while (state->i2c_read_buffer[0] && index_loop--); + + if (index_loop > 0) + return 0; + return -EIO; +} + +static int dib9000_fw_init(struct dib9000_state *state) +{ + struct dibGPIOFunction *f; + u16 b[40] = { 0 }; + u8 i; + u8 size; + + if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0) + return -EIO; + + /* initialize the firmware */ + for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) { + f = &state->chip.d9.cfg.gpio_function[i]; + if (f->mask) { + switch (f->function) { + case BOARD_GPIO_FUNCTION_COMPONENT_ON: + b[0] = (u16) f->mask; + b[1] = (u16) f->direction; + b[2] = (u16) f->value; + break; + case BOARD_GPIO_FUNCTION_COMPONENT_OFF: + b[3] = (u16) f->mask; + b[4] = (u16) f->direction; + b[5] = (u16) f->value; + break; + } + } + } + if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0) + return -EIO; + + /* subband */ + b[0] = state->chip.d9.cfg.subband.size; /* type == 0 -> GPIO - PWM not yet supported */ + for (i = 0; i < state->chip.d9.cfg.subband.size; i++) { + b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz; + b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask; + b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction; + b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value; + } + b[1 + i * 4] = 0; /* fe_id */ + if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0) + return -EIO; + + /* 0 - id, 1 - no_of_frontends */ + b[0] = (0 << 8) | 1; + /* 0 = i2c-address demod, 0 = tuner */ + b[1] = (0 << 8) | (0); + b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff); + b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff); + b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff); + b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff); + b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff); + b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff); + b[29] = state->chip.d9.cfg.if_drives; + if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0) + return -EIO; + + if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0) + return -EIO; + + if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0) + return -EIO; + + if (size > ARRAY_SIZE(b)) { + dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size, + (int)ARRAY_SIZE(b)); + return -EINVAL; + } + + for (i = 0; i < size; i += 2) { + state->platform.risc.fe_mm[i / 2].addr = b[i + 0]; + state->platform.risc.fe_mm[i / 2].size = b[i + 1]; + } + + return 0; +} + +static void dib9000_fw_set_channel_head(struct dib9000_state *state) +{ + u8 b[9]; + u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000; + if (state->fe_id % 2) + freq += 101; + + b[0] = (u8) ((freq >> 0) & 0xff); + b[1] = (u8) ((freq >> 8) & 0xff); + b[2] = (u8) ((freq >> 16) & 0xff); + b[3] = (u8) ((freq >> 24) & 0xff); + b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff); + b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff); + b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff); + b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff); + b[8] = 0x80; /* do not wait for CELL ID when doing autosearch */ + if (state->fe[0]->dtv_property_cache.delivery_system == SYS_DVBT) + b[8] |= 1; + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b); +} + +static int dib9000_fw_get_channel(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + struct dibDVBTChannel { + s8 spectrum_inversion; + + s8 nfft; + s8 guard; + s8 constellation; + + s8 hrch; + s8 alpha; + s8 code_rate_hp; + s8 code_rate_lp; + s8 select_hp; + + s8 intlv_native; + }; + struct dibDVBTChannel *ch; + int ret = 0; + + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + ret = -EIO; + goto error; + } + + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, + state->i2c_read_buffer, sizeof(struct dibDVBTChannel)); + ch = (struct dibDVBTChannel *)state->i2c_read_buffer; + + + switch (ch->spectrum_inversion & 0x7) { + case 1: + state->fe[0]->dtv_property_cache.inversion = INVERSION_ON; + break; + case 0: + state->fe[0]->dtv_property_cache.inversion = INVERSION_OFF; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.inversion = INVERSION_AUTO; + break; + } + switch (ch->nfft) { + case 0: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 2: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K; + break; + case 1: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO; + break; + } + switch (ch->guard) { + case 0: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO; + break; + } + switch (ch->constellation) { + case 2: + state->fe[0]->dtv_property_cache.modulation = QAM_64; + break; + case 1: + state->fe[0]->dtv_property_cache.modulation = QAM_16; + break; + case 0: + state->fe[0]->dtv_property_cache.modulation = QPSK; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.modulation = QAM_AUTO; + break; + } + switch (ch->hrch) { + case 0: + state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_NONE; + break; + case 1: + state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_1; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_AUTO; + break; + } + switch (ch->code_rate_hp) { + case 1: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_1_2; + break; + case 2: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_2_3; + break; + case 3: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_3_4; + break; + case 5: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_5_6; + break; + case 7: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_7_8; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.code_rate_HP = FEC_AUTO; + break; + } + switch (ch->code_rate_lp) { + case 1: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_1_2; + break; + case 2: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_2_3; + break; + case 3: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_3_4; + break; + case 5: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_5_6; + break; + case 7: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_7_8; + break; + default: + case -1: + state->fe[0]->dtv_property_cache.code_rate_LP = FEC_AUTO; + break; + } + +error: + mutex_unlock(&state->platform.risc.mem_mbx_lock); + return ret; +} + +static int dib9000_fw_set_channel_union(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + struct dibDVBTChannel { + s8 spectrum_inversion; + + s8 nfft; + s8 guard; + s8 constellation; + + s8 hrch; + s8 alpha; + s8 code_rate_hp; + s8 code_rate_lp; + s8 select_hp; + + s8 intlv_native; + }; + struct dibDVBTChannel ch; + + switch (state->fe[0]->dtv_property_cache.inversion) { + case INVERSION_ON: + ch.spectrum_inversion = 1; + break; + case INVERSION_OFF: + ch.spectrum_inversion = 0; + break; + default: + case INVERSION_AUTO: + ch.spectrum_inversion = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + ch.nfft = 0; + break; + case TRANSMISSION_MODE_4K: + ch.nfft = 2; + break; + case TRANSMISSION_MODE_8K: + ch.nfft = 1; + break; + default: + case TRANSMISSION_MODE_AUTO: + ch.nfft = 1; + break; + } + switch (state->fe[0]->dtv_property_cache.guard_interval) { + case GUARD_INTERVAL_1_32: + ch.guard = 0; + break; + case GUARD_INTERVAL_1_16: + ch.guard = 1; + break; + case GUARD_INTERVAL_1_8: + ch.guard = 2; + break; + case GUARD_INTERVAL_1_4: + ch.guard = 3; + break; + default: + case GUARD_INTERVAL_AUTO: + ch.guard = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.modulation) { + case QAM_64: + ch.constellation = 2; + break; + case QAM_16: + ch.constellation = 1; + break; + case QPSK: + ch.constellation = 0; + break; + default: + case QAM_AUTO: + ch.constellation = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.hierarchy) { + case HIERARCHY_NONE: + ch.hrch = 0; + break; + case HIERARCHY_1: + case HIERARCHY_2: + case HIERARCHY_4: + ch.hrch = 1; + break; + default: + case HIERARCHY_AUTO: + ch.hrch = -1; + break; + } + ch.alpha = 1; + switch (state->fe[0]->dtv_property_cache.code_rate_HP) { + case FEC_1_2: + ch.code_rate_hp = 1; + break; + case FEC_2_3: + ch.code_rate_hp = 2; + break; + case FEC_3_4: + ch.code_rate_hp = 3; + break; + case FEC_5_6: + ch.code_rate_hp = 5; + break; + case FEC_7_8: + ch.code_rate_hp = 7; + break; + default: + case FEC_AUTO: + ch.code_rate_hp = -1; + break; + } + switch (state->fe[0]->dtv_property_cache.code_rate_LP) { + case FEC_1_2: + ch.code_rate_lp = 1; + break; + case FEC_2_3: + ch.code_rate_lp = 2; + break; + case FEC_3_4: + ch.code_rate_lp = 3; + break; + case FEC_5_6: + ch.code_rate_lp = 5; + break; + case FEC_7_8: + ch.code_rate_lp = 7; + break; + default: + case FEC_AUTO: + ch.code_rate_lp = -1; + break; + } + ch.select_hp = 1; + ch.intlv_native = 1; + + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch); + + return 0; +} + +static int dib9000_fw_tune(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + int ret = 10, search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN; + s8 i; + + switch (state->tune_state) { + case CT_DEMOD_START: + dib9000_fw_set_channel_head(state); + + /* write the channel context - a channel is initialized to 0, so it is OK */ + dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info); + dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info); + + if (search) + dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0); + else { + dib9000_fw_set_channel_union(fe); + dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0); + } + state->tune_state = CT_DEMOD_STEP_1; + break; + case CT_DEMOD_STEP_1: + if (search) + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, state->i2c_read_buffer, 1); + else + dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, state->i2c_read_buffer, 1); + i = (s8)state->i2c_read_buffer[0]; + switch (i) { /* something happened */ + case 0: + break; + case -2: /* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */ + if (search) + state->status = FE_STATUS_DEMOD_SUCCESS; + else { + state->tune_state = CT_DEMOD_STOP; + state->status = FE_STATUS_LOCKED; + } + break; + default: + state->status = FE_STATUS_TUNE_FAILED; + state->tune_state = CT_DEMOD_STOP; + break; + } + break; + default: + ret = FE_CALLBACK_TIME_NEVER; + break; + } + + return ret; +} + +static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 mode = (u16) onoff; + return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1); +} + +static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 outreg, smo_mode; + + dprintk("setting output mode for demod %p to %d\n", fe, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + break; + case OUTMODE_MPEG2_FIFO: + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->fe[0]); + return -EINVAL; + } + + dib9000_write_word(state, 1795, outreg); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: + case OUTMODE_MPEG2_PAR_CONT_CLK: + case OUTMODE_MPEG2_SERIAL: + case OUTMODE_MPEG2_FIFO: + smo_mode = (dib9000_read_word(state, 295) & 0x0010) | (1 << 1); + if (state->chip.d9.cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5); + dib9000_write_word(state, 295, smo_mode); + break; + } + + outreg = to_fw_output_mode(mode); + return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1); +} + +static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib9000_state *state = i2c_get_adapdata(i2c_adap); + u16 i, len, t, index_msg; + + for (index_msg = 0; index_msg < num; index_msg++) { + if (msg[index_msg].flags & I2C_M_RD) { /* read */ + len = msg[index_msg].len; + if (len > 16) + len = 16; + + if (dib9000_read_word(state, 790) != 0) + dprintk("TunerITF: read busy\n"); + + dib9000_write_word(state, 784, (u16) (msg[index_msg].addr)); + dib9000_write_word(state, 787, (len / 2) - 1); + dib9000_write_word(state, 786, 1); /* start read */ + + i = 1000; + while (dib9000_read_word(state, 790) != (len / 2) && i) + i--; + + if (i == 0) + dprintk("TunerITF: read failed\n"); + + for (i = 0; i < len; i += 2) { + t = dib9000_read_word(state, 785); + msg[index_msg].buf[i] = (t >> 8) & 0xff; + msg[index_msg].buf[i + 1] = (t) & 0xff; + } + if (dib9000_read_word(state, 790) != 0) + dprintk("TunerITF: read more data than expected\n"); + } else { + i = 1000; + while (dib9000_read_word(state, 789) && i) + i--; + if (i == 0) + dprintk("TunerITF: write busy\n"); + + len = msg[index_msg].len; + if (len > 16) + len = 16; + + for (i = 0; i < len; i += 2) + dib9000_write_word(state, 785, (msg[index_msg].buf[i] << 8) | msg[index_msg].buf[i + 1]); + dib9000_write_word(state, 784, (u16) msg[index_msg].addr); + dib9000_write_word(state, 787, (len / 2) - 1); + dib9000_write_word(state, 786, 0); /* start write */ + + i = 1000; + while (dib9000_read_word(state, 791) > 0 && i) + i--; + if (i == 0) + dprintk("TunerITF: write failed\n"); + } + } + return num; +} + +int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) +{ + struct dib9000_state *state = fe->demodulator_priv; + + state->component_bus_speed = speed; + return 0; +} +EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed); + +static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dib9000_state *state = i2c_get_adapdata(i2c_adap); + u8 type = 0; /* I2C */ + u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4; + u16 scl = state->component_bus_speed; /* SCL frequency */ + struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER]; + u8 p[13] = { 0 }; + + p[0] = type; + p[1] = port; + p[2] = msg[0].addr << 1; + + p[3] = (u8) scl & 0xff; /* scl */ + p[4] = (u8) (scl >> 8); + + p[7] = 0; + p[8] = 0; + + p[9] = (u8) (msg[0].len); + p[10] = (u8) (msg[0].len >> 8); + if ((num > 1) && (msg[1].flags & I2C_M_RD)) { + p[11] = (u8) (msg[1].len); + p[12] = (u8) (msg[1].len >> 8); + } else { + p[11] = 0; + p[12] = 0; + } + + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock\n"); + return 0; + } + + dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p); + + { /* write-part */ + dib9000_risc_mem_setup_cmd(state, m->addr, msg[0].len, 0); + dib9000_risc_mem_write_chunks(state, msg[0].buf, msg[0].len); + } + + /* do the transaction */ + if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) { + mutex_unlock(&state->platform.risc.mem_mbx_lock); + return 0; + } + + /* read back any possible result */ + if ((num > 1) && (msg[1].flags & I2C_M_RD)) + dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len); + + mutex_unlock(&state->platform.risc.mem_mbx_lock); + + return num; +} + +static u32 dib9000_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm dib9000_tuner_algo = { + .master_xfer = dib9000_tuner_xfer, + .functionality = dib9000_i2c_func, +}; + +static const struct i2c_algorithm dib9000_component_bus_algo = { + .master_xfer = dib9000_fw_component_bus_xfer, + .functionality = dib9000_i2c_func, +}; + +struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) +{ + struct dib9000_state *st = fe->demodulator_priv; + return &st->tuner_adap; +} +EXPORT_SYMBOL(dib9000_get_tuner_interface); + +struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) +{ + struct dib9000_state *st = fe->demodulator_priv; + return &st->component_bus; +} +EXPORT_SYMBOL(dib9000_get_component_bus_interface); + +struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + struct dib9000_state *st = fe->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} +EXPORT_SYMBOL(dib9000_get_i2c_master); + +int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) +{ + struct dib9000_state *st = fe->demodulator_priv; + + st->i2c.i2c_adap = i2c; + return 0; +} +EXPORT_SYMBOL(dib9000_set_i2c_adapter); + +static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val) +{ + st->gpio_dir = dib9000_read_word(st, 773); + st->gpio_dir &= ~(1 << num); /* reset the direction bit */ + st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ + dib9000_write_word(st, 773, st->gpio_dir); + + st->gpio_val = dib9000_read_word(st, 774); + st->gpio_val &= ~(1 << num); /* reset the direction bit */ + st->gpio_val |= (val & 0x01) << num; /* set the new value */ + dib9000_write_word(st, 774, st->gpio_val); + + dprintk("gpio dir: %04x: gpio val: %04x\n", st->gpio_dir, st->gpio_val); + + return 0; +} + +int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + struct dib9000_state *state = fe->demodulator_priv; + return dib9000_cfg_gpio(state, num, dir, val); +} +EXPORT_SYMBOL(dib9000_set_gpio); + +int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 val; + int ret; + + if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) { + /* postpone the pid filtering cmd */ + dprintk("pid filter cmd postpone\n"); + state->pid_ctrl_index++; + state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL; + state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; + return 0; + } + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + + val = dib9000_read_word(state, 294 + 1) & 0xffef; + val |= (onoff & 0x1) << 4; + + dprintk("PID filter enabled %d\n", onoff); + ret = dib9000_write_word(state, 294 + 1, val); + mutex_unlock(&state->demod_lock); + return ret; + +} +EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl); + +int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib9000_state *state = fe->demodulator_priv; + int ret; + + if (state->pid_ctrl_index != -2) { + /* postpone the pid filtering cmd */ + dprintk("pid filter postpone\n"); + if (state->pid_ctrl_index < 9) { + state->pid_ctrl_index++; + state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER; + state->pid_ctrl[state->pid_ctrl_index].id = id; + state->pid_ctrl[state->pid_ctrl_index].pid = pid; + state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; + } else + dprintk("can not add any more pid ctrl cmd\n"); + return 0; + } + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + dprintk("Index %x, PID %d, OnOff %d\n", id, pid, onoff); + ret = dib9000_write_word(state, 300 + 1 + id, + onoff ? (1 << 13) | pid : 0); + mutex_unlock(&state->demod_lock); + return ret; +} +EXPORT_SYMBOL(dib9000_fw_pid_filter); + +int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + return dib9000_fw_init(state); +} +EXPORT_SYMBOL(dib9000_firmware_post_pll_init); + +static void dib9000_release(struct dvb_frontend *demod) +{ + struct dib9000_state *st = demod->demodulator_priv; + u8 index_frontend; + + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) + dvb_frontend_detach(st->fe[index_frontend]); + + dibx000_exit_i2c_master(&st->i2c_master); + + i2c_del_adapter(&st->tuner_adap); + i2c_del_adapter(&st->component_bus); + kfree(st->fe[0]); + kfree(st); +} + +static int dib9000_wakeup(struct dvb_frontend *fe) +{ + return 0; +} + +static int dib9000_sleep(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + int ret = 0; + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); + if (ret < 0) + goto error; + } + ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); + +error: + mutex_unlock(&state->demod_lock); + return ret; +} + +static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static int dib9000_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend, sub_index_frontend; + enum fe_status stat; + int ret = 0; + + if (state->get_frontend_internal == 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + } + + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); + if (stat & FE_HAS_SYNC) { + dprintk("TPS lock on the slave%i\n", index_frontend); + + /* synchronize the cache with the other frontends */ + state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c); + for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); + sub_index_frontend++) { + if (sub_index_frontend != index_frontend) { + state->fe[sub_index_frontend]->dtv_property_cache.modulation = + state->fe[index_frontend]->dtv_property_cache.modulation; + state->fe[sub_index_frontend]->dtv_property_cache.inversion = + state->fe[index_frontend]->dtv_property_cache.inversion; + state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = + state->fe[index_frontend]->dtv_property_cache.transmission_mode; + state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = + state->fe[index_frontend]->dtv_property_cache.guard_interval; + state->fe[sub_index_frontend]->dtv_property_cache.hierarchy = + state->fe[index_frontend]->dtv_property_cache.hierarchy; + state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP = + state->fe[index_frontend]->dtv_property_cache.code_rate_HP; + state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP = + state->fe[index_frontend]->dtv_property_cache.code_rate_LP; + state->fe[sub_index_frontend]->dtv_property_cache.rolloff = + state->fe[index_frontend]->dtv_property_cache.rolloff; + } + } + ret = 0; + goto return_value; + } + } + + /* get the channel from master chip */ + ret = dib9000_fw_get_channel(fe); + if (ret != 0) + goto return_value; + + /* synchronize the cache with the other frontends */ + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion; + state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode; + state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval; + state->fe[index_frontend]->dtv_property_cache.modulation = c->modulation; + state->fe[index_frontend]->dtv_property_cache.hierarchy = c->hierarchy; + state->fe[index_frontend]->dtv_property_cache.code_rate_HP = c->code_rate_HP; + state->fe[index_frontend]->dtv_property_cache.code_rate_LP = c->code_rate_LP; + state->fe[index_frontend]->dtv_property_cache.rolloff = c->rolloff; + } + ret = 0; + +return_value: + if (state->get_frontend_internal == 0) + mutex_unlock(&state->demod_lock); + return ret; +} + +static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + struct dib9000_state *state = fe->demodulator_priv; + state->tune_state = tune_state; + if (tune_state == CT_DEMOD_START) + state->status = FE_STATUS_TUNE_PENDING; + + return 0; +} + +static u32 dib9000_get_status(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + return state->status; +} + +static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status) +{ + struct dib9000_state *state = fe->demodulator_priv; + + memcpy(&state->channel_status, channel_status, sizeof(struct dvb_frontend_parametersContext)); + return 0; +} + +static int dib9000_set_frontend(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + int sleep_time, sleep_time_slave; + u32 frontend_status; + u8 nbr_pending, exit_condition, index_frontend, index_frontend_success; + struct dvb_frontend_parametersContext channel_status; + + /* check that the correct parameters are set */ + if (state->fe[0]->dtv_property_cache.frequency == 0) { + dprintk("dib9000: must specify frequency\n"); + return 0; + } + + if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { + dprintk("dib9000: must specify bandwidth\n"); + return 0; + } + + state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return 0; + } + + fe->dtv_property_cache.delivery_system = SYS_DVBT; + + /* set the master status */ + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO || + state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO || + state->fe[0]->dtv_property_cache.modulation == QAM_AUTO || + state->fe[0]->dtv_property_cache.code_rate_HP == FEC_AUTO) { + /* no channel specified, autosearch the channel */ + state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN; + } else + state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; + + /* set mode and status for the different frontends */ + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + dib9000_fw_set_diversity_in(state->fe[index_frontend], 1); + + /* synchronization of the cache */ + memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); + + state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT; + dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z); + + dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status); + dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); + } + + /* actual tune */ + exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ + index_frontend_success = 0; + do { + sleep_time = dib9000_fw_tune(state->fe[0]); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]); + if (sleep_time == FE_CALLBACK_TIME_NEVER) + sleep_time = sleep_time_slave; + else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) + sleep_time = sleep_time_slave; + } + if (sleep_time != FE_CALLBACK_TIME_NEVER) + msleep(sleep_time / 10); + else + break; + + nbr_pending = 0; + exit_condition = 0; + index_frontend_success = 0; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + frontend_status = -dib9000_get_status(state->fe[index_frontend]); + if (frontend_status > -FE_STATUS_TUNE_PENDING) { + exit_condition = 2; /* tune success */ + index_frontend_success = index_frontend; + break; + } + if (frontend_status == -FE_STATUS_TUNE_PENDING) + nbr_pending++; /* some frontends are still tuning */ + } + if ((exit_condition != 2) && (nbr_pending == 0)) + exit_condition = 1; /* if all tune are done and no success, exit: tune failed */ + + } while (exit_condition == 0); + + /* check the tune result */ + if (exit_condition == 1) { /* tune failed */ + dprintk("tune failed\n"); + mutex_unlock(&state->demod_lock); + /* tune failed; put all the pid filtering cmd to junk */ + state->pid_ctrl_index = -1; + return 0; + } + + dprintk("tune success on frontend%i\n", index_frontend_success); + + /* synchronize all the channel cache */ + state->get_frontend_internal = 1; + dib9000_get_frontend(state->fe[0], &state->fe[0]->dtv_property_cache); + state->get_frontend_internal = 0; + + /* retune the other frontends with the found channel */ + channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + /* only retune the frontends which was not tuned success */ + if (index_frontend != index_frontend_success) { + dib9000_set_channel_status(state->fe[index_frontend], &channel_status); + dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); + } + } + do { + sleep_time = FE_CALLBACK_TIME_NEVER; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (index_frontend != index_frontend_success) { + sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]); + if (sleep_time == FE_CALLBACK_TIME_NEVER) + sleep_time = sleep_time_slave; + else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) + sleep_time = sleep_time_slave; + } + } + if (sleep_time != FE_CALLBACK_TIME_NEVER) + msleep(sleep_time / 10); + else + break; + + nbr_pending = 0; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + if (index_frontend != index_frontend_success) { + frontend_status = -dib9000_get_status(state->fe[index_frontend]); + if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING)) + nbr_pending++; /* some frontends are still tuning */ + } + } + } while (nbr_pending != 0); + + /* set the output mode */ + dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); + + /* turn off the diversity for the last frontend */ + dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); + + mutex_unlock(&state->demod_lock); + if (state->pid_ctrl_index >= 0) { + u8 index_pid_filter_cmd; + u8 pid_ctrl_index = state->pid_ctrl_index; + + state->pid_ctrl_index = -2; + for (index_pid_filter_cmd = 0; + index_pid_filter_cmd <= pid_ctrl_index; + index_pid_filter_cmd++) { + if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL) + dib9000_fw_pid_filter_ctrl(state->fe[0], + state->pid_ctrl[index_pid_filter_cmd].onoff); + else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER) + dib9000_fw_pid_filter(state->fe[0], + state->pid_ctrl[index_pid_filter_cmd].id, + state->pid_ctrl[index_pid_filter_cmd].pid, + state->pid_ctrl[index_pid_filter_cmd].onoff); + } + } + /* do not postpone any more the pid filtering */ + state->pid_ctrl_index = -2; + + return 0; +} + +static u16 dib9000_read_lock(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + + return dib9000_read_word(state, 535); +} + +static int dib9000_read_status(struct dvb_frontend *fe, enum fe_status *stat) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + u16 lock = 0, lock_slave = 0; + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + lock_slave |= dib9000_read_lock(state->fe[index_frontend]); + + lock = dib9000_read_word(state, 535); + + *stat = 0; + + if ((lock & 0x8000) || (lock_slave & 0x8000)) + *stat |= FE_HAS_SIGNAL; + if ((lock & 0x3000) || (lock_slave & 0x3000)) + *stat |= FE_HAS_CARRIER; + if ((lock & 0x0100) || (lock_slave & 0x0100)) + *stat |= FE_HAS_VITERBI; + if (((lock & 0x0038) == 0x38) || ((lock_slave & 0x0038) == 0x38)) + *stat |= FE_HAS_SYNC; + if ((lock & 0x0008) || (lock_slave & 0x0008)) + *stat |= FE_HAS_LOCK; + + mutex_unlock(&state->demod_lock); + + return 0; +} + +static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 *c; + int ret = 0; + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock\n"); + ret = -EINTR; + goto error; + } + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + mutex_unlock(&state->platform.risc.mem_mbx_lock); + ret = -EIO; + goto error; + } + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, + state->i2c_read_buffer, 16 * 2); + mutex_unlock(&state->platform.risc.mem_mbx_lock); + + c = (u16 *)state->i2c_read_buffer; + + *ber = c[10] << 16 | c[11]; + +error: + mutex_unlock(&state->demod_lock); + return ret; +} + +static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + u16 *c = (u16 *)state->i2c_read_buffer; + u16 val; + int ret = 0; + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + *strength = 0; + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { + state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + } + + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock\n"); + ret = -EINTR; + goto error; + } + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + mutex_unlock(&state->platform.risc.mem_mbx_lock); + ret = -EIO; + goto error; + } + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); + mutex_unlock(&state->platform.risc.mem_mbx_lock); + + val = 65535 - c[4]; + if (val > 65535 - *strength) + *strength = 65535; + else + *strength += val; + +error: + mutex_unlock(&state->demod_lock); + return ret; +} + +static u32 dib9000_get_snr(struct dvb_frontend *fe) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 *c = (u16 *)state->i2c_read_buffer; + u32 n, s, exp; + u16 val; + + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock\n"); + return 0; + } + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + mutex_unlock(&state->platform.risc.mem_mbx_lock); + return 0; + } + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); + mutex_unlock(&state->platform.risc.mem_mbx_lock); + + val = c[7]; + n = (val >> 4) & 0xff; + exp = ((val & 0xf) << 2); + val = c[8]; + exp += ((val >> 14) & 0x3); + if ((exp & 0x20) != 0) + exp -= 0x40; + n <<= exp + 16; + + s = (val >> 6) & 0xFF; + exp = (val & 0x3F); + if ((exp & 0x20) != 0) + exp -= 0x40; + s <<= exp + 16; + + if (n > 0) { + u32 t = (s / n) << 16; + return t + ((s << 16) - n * t) / n; + } + return 0xffffffff; +} + +static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend; + u32 snr_master; + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + snr_master = dib9000_get_snr(fe); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + snr_master += dib9000_get_snr(state->fe[index_frontend]); + + if ((snr_master >> 16) != 0) { + snr_master = 10 * intlog10(snr_master >> 16); + *snr = snr_master / ((1 << 24) / 10); + } else + *snr = 0; + + mutex_unlock(&state->demod_lock); + + return 0; +} + +static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct dib9000_state *state = fe->demodulator_priv; + u16 *c = (u16 *)state->i2c_read_buffer; + int ret = 0; + + if (mutex_lock_interruptible(&state->demod_lock) < 0) { + dprintk("could not get the lock\n"); + return -EINTR; + } + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { + dprintk("could not get the lock\n"); + ret = -EINTR; + goto error; + } + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + mutex_unlock(&state->platform.risc.mem_mbx_lock); + ret = -EIO; + goto error; + } + dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); + mutex_unlock(&state->platform.risc.mem_mbx_lock); + + *unc = c[12]; + +error: + mutex_unlock(&state->demod_lock); + return ret; +} + +int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr) +{ + int k = 0, ret = 0; + u8 new_addr = 0; + struct i2c_device client = {.i2c_adap = i2c }; + + client.i2c_write_buffer = kzalloc(4, GFP_KERNEL); + if (!client.i2c_write_buffer) { + dprintk("%s: not enough memory\n", __func__); + return -ENOMEM; + } + client.i2c_read_buffer = kzalloc(4, GFP_KERNEL); + if (!client.i2c_read_buffer) { + dprintk("%s: not enough memory\n", __func__); + ret = -ENOMEM; + goto error_memory; + } + + client.i2c_addr = default_addr + 16; + dib9000_i2c_write16(&client, 1796, 0x0); + + for (k = no_of_demods - 1; k >= 0; k--) { + /* designated i2c address */ + new_addr = first_addr + (k << 1); + client.i2c_addr = default_addr; + + dib9000_i2c_write16(&client, 1817, 3); + dib9000_i2c_write16(&client, 1796, 0); + dib9000_i2c_write16(&client, 1227, 1); + dib9000_i2c_write16(&client, 1227, 0); + + client.i2c_addr = new_addr; + dib9000_i2c_write16(&client, 1817, 3); + dib9000_i2c_write16(&client, 1796, 0); + dib9000_i2c_write16(&client, 1227, 1); + dib9000_i2c_write16(&client, 1227, 0); + + if (dib9000_identify(&client) == 0) { + client.i2c_addr = default_addr; + if (dib9000_identify(&client) == 0) { + dprintk("DiB9000 #%d: not identified\n", k); + ret = -EIO; + goto error; + } + } + + dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6)); + dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + new_addr = first_addr | (k << 1); + client.i2c_addr = new_addr; + + dib9000_i2c_write16(&client, 1794, (new_addr << 2)); + dib9000_i2c_write16(&client, 1795, 0); + } + +error: + kfree(client.i2c_read_buffer); +error_memory: + kfree(client.i2c_write_buffer); + + return ret; +} +EXPORT_SYMBOL(dib9000_i2c_enumeration); + +int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + struct dib9000_state *state = fe->demodulator_priv; + u8 index_frontend = 1; + + while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) + index_frontend++; + if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { + dprintk("set slave fe %p to index %i\n", fe_slave, index_frontend); + state->fe[index_frontend] = fe_slave; + return 0; + } + + dprintk("too many slave frontend\n"); + return -ENOMEM; +} +EXPORT_SYMBOL(dib9000_set_slave_frontend); + +struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ + struct dib9000_state *state = fe->demodulator_priv; + + if (slave_index >= MAX_NUMBER_OF_FRONTENDS) + return NULL; + return state->fe[slave_index]; +} +EXPORT_SYMBOL(dib9000_get_slave_frontend); + +static const struct dvb_frontend_ops dib9000_ops; +struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg) +{ + struct dvb_frontend *fe; + struct dib9000_state *st; + st = kzalloc(sizeof(struct dib9000_state), GFP_KERNEL); + if (st == NULL) + return NULL; + fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); + if (fe == NULL) { + kfree(st); + return NULL; + } + + memcpy(&st->chip.d9.cfg, cfg, sizeof(struct dib9000_config)); + st->i2c.i2c_adap = i2c_adap; + st->i2c.i2c_addr = i2c_addr; + st->i2c.i2c_write_buffer = st->i2c_write_buffer; + st->i2c.i2c_read_buffer = st->i2c_read_buffer; + + st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS; + st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES; + st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS; + + mutex_init(&st->platform.risc.mbx_if_lock); + mutex_init(&st->platform.risc.mbx_lock); + mutex_init(&st->platform.risc.mem_lock); + mutex_init(&st->platform.risc.mem_mbx_lock); + mutex_init(&st->demod_lock); + st->get_frontend_internal = 0; + + st->pid_ctrl_index = -2; + + st->fe[0] = fe; + fe->demodulator_priv = st; + memcpy(&st->fe[0]->ops, &dib9000_ops, sizeof(struct dvb_frontend_ops)); + + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) + st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO; + + if (dib9000_identify(&st->i2c) == 0) + goto error; + + dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr); + + st->tuner_adap.dev.parent = i2c_adap->dev.parent; + strscpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS", + sizeof(st->tuner_adap.name)); + st->tuner_adap.algo = &dib9000_tuner_algo; + st->tuner_adap.algo_data = NULL; + i2c_set_adapdata(&st->tuner_adap, st); + if (i2c_add_adapter(&st->tuner_adap) < 0) + goto error; + + st->component_bus.dev.parent = i2c_adap->dev.parent; + strscpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS", + sizeof(st->component_bus.name)); + st->component_bus.algo = &dib9000_component_bus_algo; + st->component_bus.algo_data = NULL; + st->component_bus_speed = 340; + i2c_set_adapdata(&st->component_bus, st); + if (i2c_add_adapter(&st->component_bus) < 0) + goto component_bus_add_error; + + dib9000_fw_reset(fe); + + return fe; + +component_bus_add_error: + i2c_del_adapter(&st->tuner_adap); +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL_GPL(dib9000_attach); + +static const struct dvb_frontend_ops dib9000_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "DiBcom 9000", + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib9000_release, + + .init = dib9000_wakeup, + .sleep = dib9000_sleep, + + .set_frontend = dib9000_set_frontend, + .get_tune_settings = dib9000_fe_get_tune_settings, + .get_frontend = dib9000_get_frontend, + + .read_status = dib9000_read_status, + .read_ber = dib9000_read_ber, + .read_signal_strength = dib9000_read_signal_strength, + .read_snr = dib9000_read_snr, + .read_ucblocks = dib9000_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>"); +MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h new file mode 100644 index 0000000000..bb03362ac7 --- /dev/null +++ b/drivers/media/dvb-frontends/dib9000.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DIB9000_H +#define DIB9000_H + +#include "dibx000_common.h" + +struct dib9000_config { + u8 dvbt_mode; + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + struct dibx000_bandwidth_config *bw; + + u16 if_drives; + + u32 timing_frequency; + u32 xtal_clock_khz; + u32 vcxo_timer; + u32 demod_clock_khz; + + const u8 *microcode_B_fe_buffer; + u32 microcode_B_fe_size; + + struct dibGPIOFunction gpio_function[2]; + struct dibSubbandSelection subband; + + u8 output_mode; +}; + +#define DEFAULT_DIB9000_I2C_ADDRESS 18 + +#if IS_REACHABLE(CONFIG_DVB_DIB9000) +extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg); +extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); +extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe); +extern struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating); +extern int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val); +extern int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); +extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe); +extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); +extern struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe); +extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c); +extern int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed); +#else +static inline struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib9000_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/dibx000_common.c b/drivers/media/dvb-frontends/dibx000_common.c new file mode 100644 index 0000000000..63a4c6a4af --- /dev/null +++ b/drivers/media/dvb-frontends/dibx000_common.c @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/module.h> + +#include "dibx000_common.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) +{ + int ret; + + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + mst->i2c_write_buffer[0] = (reg >> 8) & 0xff; + mst->i2c_write_buffer[1] = reg & 0xff; + mst->i2c_write_buffer[2] = (val >> 8) & 0xff; + mst->i2c_write_buffer[3] = val & 0xff; + + memset(mst->msg, 0, sizeof(struct i2c_msg)); + mst->msg[0].addr = mst->i2c_addr; + mst->msg[0].flags = 0; + mst->msg[0].buf = mst->i2c_write_buffer; + mst->msg[0].len = 4; + + ret = i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0; + mutex_unlock(&mst->i2c_buffer_lock); + + return ret; +} + +static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) +{ + u16 ret; + + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return 0; + } + + mst->i2c_write_buffer[0] = reg >> 8; + mst->i2c_write_buffer[1] = reg & 0xff; + + memset(mst->msg, 0, 2 * sizeof(struct i2c_msg)); + mst->msg[0].addr = mst->i2c_addr; + mst->msg[0].flags = 0; + mst->msg[0].buf = mst->i2c_write_buffer; + mst->msg[0].len = 2; + mst->msg[1].addr = mst->i2c_addr; + mst->msg[1].flags = I2C_M_RD; + mst->msg[1].buf = mst->i2c_read_buffer; + mst->msg[1].len = 2; + + if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2) + dprintk("i2c read error on %d\n", reg); + + ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1]; + mutex_unlock(&mst->i2c_buffer_lock); + + return ret; +} + +static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst) +{ + int i = 100; + u16 status; + + while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0) + ; + + /* i2c timed out */ + if (i == 0) + return -EREMOTEIO; + + /* no acknowledge */ + if ((status & 0x0080) == 0) + return -EREMOTEIO; + + return 0; +} + +static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_msg *msg, u8 stop) +{ + u16 data; + u16 da; + u16 i; + u16 txlen = msg->len, len; + const u8 *b = msg->buf; + + while (txlen) { + dibx000_read_word(mst, mst->base_reg + 2); + + len = txlen > 8 ? 8 : txlen; + for (i = 0; i < len; i += 2) { + data = *b++ << 8; + if (i+1 < len) + data |= *b++; + dibx000_write_word(mst, mst->base_reg, data); + } + da = (((u8) (msg->addr)) << 9) | + (1 << 8) | + (1 << 7) | + (0 << 6) | + (0 << 5) | + ((len & 0x7) << 2) | + (0 << 1) | + (0 << 0); + + if (txlen == msg->len) + da |= 1 << 5; /* start */ + + if (txlen-len == 0 && stop) + da |= 1 << 6; /* stop */ + + dibx000_write_word(mst, mst->base_reg+1, da); + + if (dibx000_is_i2c_done(mst) != 0) + return -EREMOTEIO; + txlen -= len; + } + + return 0; +} + +static int dibx000_master_i2c_read(struct dibx000_i2c_master *mst, struct i2c_msg *msg) +{ + u16 da; + u8 *b = msg->buf; + u16 rxlen = msg->len, len; + + while (rxlen) { + len = rxlen > 8 ? 8 : rxlen; + da = (((u8) (msg->addr)) << 9) | + (1 << 8) | + (1 << 7) | + (0 << 6) | + (0 << 5) | + ((len & 0x7) << 2) | + (1 << 1) | + (0 << 0); + + if (rxlen == msg->len) + da |= 1 << 5; /* start */ + + if (rxlen-len == 0) + da |= 1 << 6; /* stop */ + dibx000_write_word(mst, mst->base_reg+1, da); + + if (dibx000_is_i2c_done(mst) != 0) + return -EREMOTEIO; + + rxlen -= len; + + while (len) { + da = dibx000_read_word(mst, mst->base_reg); + *b++ = (da >> 8) & 0xff; + len--; + if (len >= 1) { + *b++ = da & 0xff; + len--; + } + } + } + + return 0; +} + +int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + + if (mst->device_rev < DIB7000MC && speed < 235) + speed = 235; + return dibx000_write_word(mst, mst->base_reg + 3, (u16)(60000 / speed)); + +} +EXPORT_SYMBOL(dibx000_i2c_set_speed); + +static u32 dibx000_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, + enum dibx000_i2c_interface intf) +{ + if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { + dprintk("selecting interface: %d\n", intf); + mst->selected_interface = intf; + return dibx000_write_word(mst, mst->base_reg + 4, intf); + } + return 0; +} + +static int dibx000_i2c_master_xfer_gpio12(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int msg_index; + int ret = 0; + + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_1_2); + for (msg_index = 0; msg_index < num; msg_index++) { + if (msg[msg_index].flags & I2C_M_RD) { + ret = dibx000_master_i2c_read(mst, &msg[msg_index]); + if (ret != 0) + return 0; + } else { + ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1); + if (ret != 0) + return 0; + } + } + + return num; +} + +static int dibx000_i2c_master_xfer_gpio34(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int msg_index; + int ret = 0; + + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_3_4); + for (msg_index = 0; msg_index < num; msg_index++) { + if (msg[msg_index].flags & I2C_M_RD) { + ret = dibx000_master_i2c_read(mst, &msg[msg_index]); + if (ret != 0) + return 0; + } else { + ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1); + if (ret != 0) + return 0; + } + } + + return num; +} + +static struct i2c_algorithm dibx000_i2c_master_gpio12_xfer_algo = { + .master_xfer = dibx000_i2c_master_xfer_gpio12, + .functionality = dibx000_i2c_func, +}; + +static struct i2c_algorithm dibx000_i2c_master_gpio34_xfer_algo = { + .master_xfer = dibx000_i2c_master_xfer_gpio34, + .functionality = dibx000_i2c_func, +}; + +static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], + u8 addr, int onoff) +{ + u16 val; + + + if (onoff) + val = addr << 8; // bit 7 = use master or not, if 0, the gate is open + else + val = 1 << 7; + + if (mst->device_rev > DIB7000) + val <<= 1; + + tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); + tx[1] = ((mst->base_reg + 1) & 0xff); + tx[2] = val >> 8; + tx[3] = val & 0xff; + + return 0; +} + +static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int ret; + + if (num > 32) { + dprintk("%s: too much I2C message to be transmitted (%i). Maximum is 32", + __func__, num); + return -ENOMEM; + } + + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7); + + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + + memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); + + /* open the gate */ + dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); + mst->msg[0].addr = mst->i2c_addr; + mst->msg[0].buf = &mst->i2c_write_buffer[0]; + mst->msg[0].len = 4; + + memcpy(&mst->msg[1], msg, sizeof(struct i2c_msg) * num); + + /* close the gate */ + dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[4], 0, 0); + mst->msg[num + 1].addr = mst->i2c_addr; + mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; + mst->msg[num + 1].len = 4; + + ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? + num : -EIO); + + mutex_unlock(&mst->i2c_buffer_lock); + return ret; +} + +static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = { + .master_xfer = dibx000_i2c_gated_gpio67_xfer, + .functionality = dibx000_i2c_func, +}; + +static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msg[], int num) +{ + struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int ret; + + if (num > 32) { + dprintk("%s: too much I2C message to be transmitted (%i). Maximum is 32", + __func__, num); + return -ENOMEM; + } + + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); + + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); + + /* open the gate */ + dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); + mst->msg[0].addr = mst->i2c_addr; + mst->msg[0].buf = &mst->i2c_write_buffer[0]; + mst->msg[0].len = 4; + + memcpy(&mst->msg[1], msg, sizeof(struct i2c_msg) * num); + + /* close the gate */ + dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[4], 0, 0); + mst->msg[num + 1].addr = mst->i2c_addr; + mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; + mst->msg[num + 1].len = 4; + + ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? + num : -EIO); + mutex_unlock(&mst->i2c_buffer_lock); + return ret; +} + +static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { + .master_xfer = dibx000_i2c_gated_tuner_xfer, + .functionality = dibx000_i2c_func, +}; + +struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, + enum dibx000_i2c_interface intf, + int gating) +{ + struct i2c_adapter *i2c = NULL; + + switch (intf) { + case DIBX000_I2C_INTERFACE_TUNER: + if (gating) + i2c = &mst->gated_tuner_i2c_adap; + break; + case DIBX000_I2C_INTERFACE_GPIO_1_2: + if (!gating) + i2c = &mst->master_i2c_adap_gpio12; + break; + case DIBX000_I2C_INTERFACE_GPIO_3_4: + if (!gating) + i2c = &mst->master_i2c_adap_gpio34; + break; + case DIBX000_I2C_INTERFACE_GPIO_6_7: + if (gating) + i2c = &mst->master_i2c_adap_gpio67; + break; + default: + pr_err("incorrect I2C interface selected\n"); + break; + } + + return i2c; +} + +EXPORT_SYMBOL(dibx000_get_i2c_adapter); + +void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) +{ + /* initialize the i2c-master by closing the gate */ + u8 tx[4]; + struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 }; + + dibx000_i2c_gate_ctrl(mst, tx, 0, 0); + i2c_transfer(mst->i2c_adap, &m, 1); + mst->selected_interface = 0xff; // the first time force a select of the I2C + dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); +} + +EXPORT_SYMBOL(dibx000_reset_i2c_master); + +static int i2c_adapter_init(struct i2c_adapter *i2c_adap, + struct i2c_algorithm *algo, const char *name, + struct dibx000_i2c_master *mst) +{ + strscpy(i2c_adap->name, name, sizeof(i2c_adap->name)); + i2c_adap->algo = algo; + i2c_adap->algo_data = NULL; + i2c_set_adapdata(i2c_adap, mst); + if (i2c_add_adapter(i2c_adap) < 0) + return -ENODEV; + return 0; +} + +int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, + struct i2c_adapter *i2c_adap, u8 i2c_addr) +{ + int ret; + + mutex_init(&mst->i2c_buffer_lock); + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock\n"); + return -EINVAL; + } + memset(mst->msg, 0, sizeof(struct i2c_msg)); + mst->msg[0].addr = i2c_addr >> 1; + mst->msg[0].flags = 0; + mst->msg[0].buf = mst->i2c_write_buffer; + mst->msg[0].len = 4; + + mst->device_rev = device_rev; + mst->i2c_adap = i2c_adap; + mst->i2c_addr = i2c_addr >> 1; + + if (device_rev == DIB7000P || device_rev == DIB8000) + mst->base_reg = 1024; + else + mst->base_reg = 768; + + mst->gated_tuner_i2c_adap.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, + "DiBX000 tuner I2C bus", mst) != 0) + pr_err("could not initialize the tuner i2c_adapter\n"); + + mst->master_i2c_adap_gpio12.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->master_i2c_adap_gpio12, &dibx000_i2c_master_gpio12_xfer_algo, + "DiBX000 master GPIO12 I2C bus", mst) != 0) + pr_err("could not initialize the master i2c_adapter\n"); + + mst->master_i2c_adap_gpio34.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->master_i2c_adap_gpio34, &dibx000_i2c_master_gpio34_xfer_algo, + "DiBX000 master GPIO34 I2C bus", mst) != 0) + pr_err("could not initialize the master i2c_adapter\n"); + + mst->master_i2c_adap_gpio67.dev.parent = mst->i2c_adap->dev.parent; + if (i2c_adapter_init + (&mst->master_i2c_adap_gpio67, &dibx000_i2c_gated_gpio67_algo, + "DiBX000 master GPIO67 I2C bus", mst) != 0) + pr_err("could not initialize the master i2c_adapter\n"); + + /* initialize the i2c-master by closing the gate */ + dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0); + + ret = (i2c_transfer(i2c_adap, mst->msg, 1) == 1); + mutex_unlock(&mst->i2c_buffer_lock); + + return ret; +} + +EXPORT_SYMBOL(dibx000_init_i2c_master); + +void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) +{ + i2c_del_adapter(&mst->gated_tuner_i2c_adap); + i2c_del_adapter(&mst->master_i2c_adap_gpio12); + i2c_del_adapter(&mst->master_i2c_adap_gpio34); + i2c_del_adapter(&mst->master_i2c_adap_gpio67); +} +EXPORT_SYMBOL(dibx000_exit_i2c_master); + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); +MODULE_DESCRIPTION("Common function the DiBcom demodulator family"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h new file mode 100644 index 0000000000..12b58f5c67 --- /dev/null +++ b/drivers/media/dvb-frontends/dibx000_common.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DIBX000_COMMON_H +#define DIBX000_COMMON_H + +enum dibx000_i2c_interface { + DIBX000_I2C_INTERFACE_TUNER = 0, + DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, + DIBX000_I2C_INTERFACE_GPIO_3_4 = 2, + DIBX000_I2C_INTERFACE_GPIO_6_7 = 3 +}; + +struct dibx000_i2c_master { +#define DIB3000MC 1 +#define DIB7000 2 +#define DIB7000P 11 +#define DIB7000MC 12 +#define DIB8000 13 + u16 device_rev; + + enum dibx000_i2c_interface selected_interface; + +/* struct i2c_adapter tuner_i2c_adap; */ + struct i2c_adapter gated_tuner_i2c_adap; + struct i2c_adapter master_i2c_adap_gpio12; + struct i2c_adapter master_i2c_adap_gpio34; + struct i2c_adapter master_i2c_adap_gpio67; + + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + + u16 base_reg; + + /* for the I2C transfer */ + struct i2c_msg msg[34]; + u8 i2c_write_buffer[8]; + u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; +}; + +extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, + u16 device_rev, struct i2c_adapter *i2c_adap, + u8 i2c_addr); +extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master + *mst, + enum dibx000_i2c_interface + intf, int gating); +extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); +extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst); +extern int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed); + +#define BAND_LBAND 0x01 +#define BAND_UHF 0x02 +#define BAND_VHF 0x04 +#define BAND_SBAND 0x08 +#define BAND_FM 0x10 +#define BAND_CBAND 0x20 + +#define BAND_OF_FREQUENCY(freq_kHz) ((freq_kHz) <= 170000 ? BAND_CBAND : \ + (freq_kHz) <= 115000 ? BAND_FM : \ + (freq_kHz) <= 250000 ? BAND_VHF : \ + (freq_kHz) <= 863000 ? BAND_UHF : \ + (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) + +struct dibx000_agc_config { + /* defines the capabilities of this AGC-setting - using the BAND_-defines */ + u8 band_caps; + + u16 setup; + + u16 inv_gain; + u16 time_stabiliz; + + u8 alpha_level; + u16 thlock; + + u8 wbd_inv; + u16 wbd_ref; + u8 wbd_sel; + u8 wbd_alpha; + + u16 agc1_max; + u16 agc1_min; + u16 agc2_max; + u16 agc2_min; + + u8 agc1_pt1; + u8 agc1_pt2; + u8 agc1_pt3; + + u8 agc1_slope1; + u8 agc1_slope2; + + u8 agc2_pt1; + u8 agc2_pt2; + + u8 agc2_slope1; + u8 agc2_slope2; + + u8 alpha_mant; + u8 alpha_exp; + + u8 beta_mant; + u8 beta_exp; + + u8 perform_agc_softsplit; + + struct { + u16 min; + u16 max; + u16 min_thres; + u16 max_thres; + } split; +}; + +struct dibx000_bandwidth_config { + u32 internal; + u32 sampling; + + u8 pll_prediv; + u8 pll_ratio; + u8 pll_range; + u8 pll_reset; + u8 pll_bypass; + + u8 enable_refdiv; + u8 bypclk_div; + u8 IO_CLK_en_core; + u8 ADClkSrc; + u8 modulo; + + u16 sad_cfg; + + u32 ifreq; + u32 timf; + + u32 xtal_hz; +}; + +enum dibx000_adc_states { + DIBX000_SLOW_ADC_ON = 0, + DIBX000_SLOW_ADC_OFF, + DIBX000_ADC_ON, + DIBX000_ADC_OFF, + DIBX000_VBG_ENABLE, + DIBX000_VBG_DISABLE, +}; + +#define BANDWIDTH_TO_KHZ(v) ((v) / 1000) +#define BANDWIDTH_TO_HZ(v) ((v) * 1000) + +/* Chip output mode. */ +#define OUTMODE_HIGH_Z 0 +#define OUTMODE_MPEG2_PAR_GATED_CLK 1 +#define OUTMODE_MPEG2_PAR_CONT_CLK 2 +#define OUTMODE_MPEG2_SERIAL 7 +#define OUTMODE_DIVERSITY 4 +#define OUTMODE_MPEG2_FIFO 5 +#define OUTMODE_ANALOG_ADC 6 + +#define INPUT_MODE_OFF 0x11 +#define INPUT_MODE_DIVERSITY 0x12 +#define INPUT_MODE_MPEG 0x13 + +enum frontend_tune_state { + CT_TUNER_START = 10, + CT_TUNER_STEP_0, + CT_TUNER_STEP_1, + CT_TUNER_STEP_2, + CT_TUNER_STEP_3, + CT_TUNER_STEP_4, + CT_TUNER_STEP_5, + CT_TUNER_STEP_6, + CT_TUNER_STEP_7, + CT_TUNER_STOP, + + CT_AGC_START = 20, + CT_AGC_STEP_0, + CT_AGC_STEP_1, + CT_AGC_STEP_2, + CT_AGC_STEP_3, + CT_AGC_STEP_4, + CT_AGC_STOP, + + CT_DEMOD_START = 30, + CT_DEMOD_STEP_1, + CT_DEMOD_STEP_2, + CT_DEMOD_STEP_3, + CT_DEMOD_STEP_4, + CT_DEMOD_STEP_5, + CT_DEMOD_STEP_6, + CT_DEMOD_STEP_7, + CT_DEMOD_STEP_8, + CT_DEMOD_STEP_9, + CT_DEMOD_STEP_10, + CT_DEMOD_STEP_11, + CT_DEMOD_SEARCH_NEXT = 51, + CT_DEMOD_STEP_LOCKED, + CT_DEMOD_STOP, + + CT_DONE = 100, + CT_SHUTDOWN, + +}; + +struct dvb_frontend_parametersContext { +#define CHANNEL_STATUS_PARAMETERS_UNKNOWN 0x01 +#define CHANNEL_STATUS_PARAMETERS_SET 0x02 + u8 status; + u32 tune_time_estimation[2]; + s32 tps_available; + u16 tps[9]; +}; + +#define FE_STATUS_TUNE_FAILED 0 +#define FE_STATUS_TUNE_TIMED_OUT -1 +#define FE_STATUS_TUNE_TIME_TOO_SHORT -2 +#define FE_STATUS_TUNE_PENDING -3 +#define FE_STATUS_STD_SUCCESS -4 +#define FE_STATUS_FFT_SUCCESS -5 +#define FE_STATUS_DEMOD_SUCCESS -6 +#define FE_STATUS_LOCKED -7 +#define FE_STATUS_DATA_LOCKED -8 + +#define FE_CALLBACK_TIME_NEVER 0xffffffff + +#define DATA_BUS_ACCESS_MODE_8BIT 0x01 +#define DATA_BUS_ACCESS_MODE_16BIT 0x02 +#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10 + +struct dibGPIOFunction { +#define BOARD_GPIO_COMPONENT_BUS_ADAPTER 1 +#define BOARD_GPIO_COMPONENT_DEMOD 2 + u8 component; + +#define BOARD_GPIO_FUNCTION_BOARD_ON 1 +#define BOARD_GPIO_FUNCTION_BOARD_OFF 2 +#define BOARD_GPIO_FUNCTION_COMPONENT_ON 3 +#define BOARD_GPIO_FUNCTION_COMPONENT_OFF 4 +#define BOARD_GPIO_FUNCTION_SUBBAND_PWM 5 +#define BOARD_GPIO_FUNCTION_SUBBAND_GPIO 6 + u8 function; + +/* mask, direction and value are used specify which GPIO to change GPIO0 + * is LSB and possible GPIO31 is MSB. The same bit-position as in the + * mask is used for the direction and the value. Direction == 1 is OUT, + * 0 == IN. For direction "OUT" value is either 1 or 0, for direction IN + * value has no meaning. + * + * In case of BOARD_GPIO_FUNCTION_PWM mask is giving the GPIO to be + * used to do the PWM. Direction gives the PWModulator to be used. + * Value gives the PWM value in device-dependent scale. + */ + u32 mask; + u32 direction; + u32 value; +}; + +#define MAX_NB_SUBBANDS 8 +struct dibSubbandSelection { + u8 size; /* Actual number of subbands. */ + struct { + u16 f_mhz; + struct dibGPIOFunction gpio; + } subband[MAX_NB_SUBBANDS]; +}; + +#define DEMOD_TIMF_SET 0x00 +#define DEMOD_TIMF_GET 0x01 +#define DEMOD_TIMF_UPDATE 0x02 + +#define MPEG_ON_DIBTX 1 +#define DIV_ON_DIBTX 2 +#define ADC_ON_DIBTX 3 +#define DEMOUT_ON_HOSTBUS 4 +#define DIBTX_ON_HOSTBUS 5 +#define MPEG_ON_HOSTBUS 6 + +#endif diff --git a/drivers/media/dvb-frontends/drx39xyj/Kconfig b/drivers/media/dvb-frontends/drx39xyj/Kconfig new file mode 100644 index 0000000000..e29c835178 --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DVB_DRX39XYJ + tristate "Micronas DRX-J demodulator" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. diff --git a/drivers/media/dvb-frontends/drx39xyj/Makefile b/drivers/media/dvb-frontends/drx39xyj/Makefile new file mode 100644 index 0000000000..e97ab6130a --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +drx39xyj-objs := drxj.o + +obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj.o + +ccflags-y += -I$(srctree)/drivers/media/tuners/ diff --git a/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h new file mode 100644 index 0000000000..4d83ce5e85 --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Micronas DRX39xx family (drx3933j) + * + * Written by Devin Heitmueller <devin.heitmueller@kernellabs.com> + */ + +#ifndef DRX39XXJ_H +#define DRX39XXJ_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> +#include "drx_driver.h" + +struct drx39xxj_state { + struct i2c_adapter *i2c; + struct drx_demod_instance *demod; + struct dvb_frontend frontend; + unsigned int i2c_gate_open:1; + const struct firmware *fw; +}; + +#if IS_REACHABLE(CONFIG_DVB_DRX39XYJ) +struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) { + return NULL; +}; +#endif + +#endif /* DVB_DUMMY_FE_H */ diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h new file mode 100644 index 0000000000..9df34c10d2 --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h @@ -0,0 +1,254 @@ +/* + 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. +*/ + +/******************************************************************************* +* FILENAME: $Id: drx_dap_fasi.h,v 1.5 2009/07/07 14:21:40 justin Exp $ +* +* DESCRIPTION: +* Part of DRX driver. +* Data access protocol: Fast Access Sequential Interface (fasi) +* Fast access, because of short addressing format (16 instead of 32 bits addr) +* Sequential, because of I2C. +* +* USAGE: +* Include. +* +* NOTES: +* +* +*******************************************************************************/ + +/*-------- compilation control switches --------------------------------------*/ + +#ifndef __DRX_DAP_FASI_H__ +#define __DRX_DAP_FASI_H__ + +/*-------- Required includes -------------------------------------------------*/ + +#include "drx_driver.h" + +/*-------- Defines, configuring the API --------------------------------------*/ + +/******************************************** +* Allowed address formats +********************************************/ + +/* +* Comments about short/long addressing format: +* +* The DAP FASI offers long address format (4 bytes) and short address format +* (2 bytes). The DAP can operate in 3 modes: +* (1) only short +* (2) only long +* (3) both long and short but short preferred and long only when necessary +* +* These modes must be selected compile time via compile switches. +* Compile switch settings for the different modes: +* (1) DRXDAPFASI_LONG_ADDR_ALLOWED=0, DRXDAPFASI_SHORT_ADDR_ALLOWED=1 +* (2) DRXDAPFASI_LONG_ADDR_ALLOWED=1, DRXDAPFASI_SHORT_ADDR_ALLOWED=0 +* (3) DRXDAPFASI_LONG_ADDR_ALLOWED=1, DRXDAPFASI_SHORT_ADDR_ALLOWED=1 +* +* The default setting will be (3) both long and short. +* The default setting will need no compile switches. +* The default setting must be overridden if compile switches are already +* defined. +* +*/ + +/* set default */ +#if !defined(DRXDAPFASI_LONG_ADDR_ALLOWED) +#define DRXDAPFASI_LONG_ADDR_ALLOWED 1 +#endif + +/* set default */ +#if !defined(DRXDAPFASI_SHORT_ADDR_ALLOWED) +#define DRXDAPFASI_SHORT_ADDR_ALLOWED 1 +#endif + +/* check */ +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && \ + (DRXDAPFASI_SHORT_ADDR_ALLOWED == 0)) +#error At least one of short- or long-addressing format must be allowed. +*; /* illegal statement to force compiler error */ +#endif + +/******************************************** +* Single/master multi master setting +********************************************/ +/* +* Comments about SINGLE MASTER/MULTI MASTER modes: +* +* Consider the two sides:1) the master and 2)the slave. +* +* Master: +* Single/multimaster operation set via DRXDAP_SINGLE_MASTER compile switch +* + single master mode means no use of repeated starts +* + multi master mode means use of repeated starts +* Default is single master. +* Default can be overridden by setting the compile switch DRXDAP_SINGLE_MASTER. +* +* Slave: +* Single/multi master selected via the flags in the FASI protocol. +* + single master means remember memory address between i2c packets +* + multimaster means flush memory address between i2c packets +* Default is single master, DAP FASI changes multi-master setting silently +* into single master setting. This cannot be overridden. +* +*/ +/* set default */ +#ifndef DRXDAP_SINGLE_MASTER +#define DRXDAP_SINGLE_MASTER 0 +#endif + +/******************************************** +* Chunk/mode checking +********************************************/ +/* +* Comments about DRXDAP_MAX_WCHUNKSIZE in single or multi master mode and +* in combination with short and long addressing format. All text below +* assumes long addressing format. The table also includes information +* for short ADDRessing format. +* +* In single master mode, data can be written by sending the register address +* first, then two or four bytes of data in the next packet. +* Because the device address plus a register address equals five bytes, +* the minimum chunk size must be five. +* If ten-bit I2C device addresses are used, the minimum chunk size must be six, +* because the I2C device address will then occupy two bytes when writing. +* +* Data in single master mode is transferred as follows: +* <S> <devW> a0 a1 a2 a3 <P> +* <S> <devW> d0 d1 [d2 d3] <P> +* .. +* or +* .. +* <S> <devW> a0 a1 a2 a3 <P> +* <S> <devR> --- <P> +* +* In multi-master mode, the data must immediately follow the address (an I2C +* stop resets the internal address), and hence the minimum chunk size is +* 1 <I2C address> + 4 (register address) + 2 (data to send) = 7 bytes (8 if +* 10-bit I2C device addresses are used). +* +* The 7-bit or 10-bit i2c address parameters is a runtime parameter. +* The other parameters can be limited via compile time switches. +* +*------------------------------------------------------------------------------- +* +* Minimum chunk size table (in bytes): +* +* +----------------+----------------+ +* | 7b i2c addr | 10b i2c addr | +* +----------------+----------------+ +* | single | multi | single | multi | +* ------+--------+-------+--------+-------+ +* short | 3 | 5 | 4 | 6 | +* long | 5 | 7 | 6 | 8 | +* ------+--------+-------+--------+-------+ +* +*/ + +/* set default */ +#if !defined(DRXDAP_MAX_WCHUNKSIZE) +#define DRXDAP_MAX_WCHUNKSIZE 254 +#endif + +/* check */ +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) +#if DRXDAP_SINGLE_MASTER +#define DRXDAP_MAX_WCHUNKSIZE_MIN 3 +#else +#define DRXDAP_MAX_WCHUNKSIZE_MIN 5 +#endif +#else +#if DRXDAP_SINGLE_MASTER +#define DRXDAP_MAX_WCHUNKSIZE_MIN 5 +#else +#define DRXDAP_MAX_WCHUNKSIZE_MIN 7 +#endif +#endif + +#if DRXDAP_MAX_WCHUNKSIZE < DRXDAP_MAX_WCHUNKSIZE_MIN +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) +#if DRXDAP_SINGLE_MASTER +#error DRXDAP_MAX_WCHUNKSIZE must be at least 3 in single master mode +*; /* illegal statement to force compiler error */ +#else +#error DRXDAP_MAX_WCHUNKSIZE must be at least 5 in multi master mode +*; /* illegal statement to force compiler error */ +#endif +#else +#if DRXDAP_SINGLE_MASTER +#error DRXDAP_MAX_WCHUNKSIZE must be at least 5 in single master mode +*; /* illegal statement to force compiler error */ +#else +#error DRXDAP_MAX_WCHUNKSIZE must be at least 7 in multi master mode +*; /* illegal statement to force compiler error */ +#endif +#endif +#endif + +/* set default */ +#if !defined(DRXDAP_MAX_RCHUNKSIZE) +#define DRXDAP_MAX_RCHUNKSIZE 254 +#endif + +/* check */ +#if DRXDAP_MAX_RCHUNKSIZE < 2 +#error DRXDAP_MAX_RCHUNKSIZE must be at least 2 +*; /* illegal statement to force compiler error */ +#endif + +/* check */ +#if DRXDAP_MAX_RCHUNKSIZE & 1 +#error DRXDAP_MAX_RCHUNKSIZE must be even +*; /* illegal statement to force compiler error */ +#endif + +/*-------- Public API functions ----------------------------------------------*/ + +#define DRXDAP_FASI_RMW 0x10000000 +#define DRXDAP_FASI_BROADCAST 0x20000000 +#define DRXDAP_FASI_CLEARCRC 0x80000000 +#define DRXDAP_FASI_SINGLE_MASTER 0xC0000000 +#define DRXDAP_FASI_MULTI_MASTER 0x40000000 +#define DRXDAP_FASI_SMM_SWITCH 0x40000000 /* single/multi master switch */ +#define DRXDAP_FASI_MODEFLAGS 0xC0000000 +#define DRXDAP_FASI_FLAGS 0xF0000000 + +#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr)>>22)&0x3F) +#define DRXDAP_FASI_ADDR2BANK(addr) (((addr)>>16)&0x3F) +#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr)&0x7FFF) + +#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0) +#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0) +#define DRXDAP_FASI_OFFSET_TOO_LARGE(addr) (((addr) & 0x00008000) != 0) + +#endif /* __DRX_DAP_FASI_H__ */ diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h new file mode 100644 index 0000000000..15f7e58c5a --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h @@ -0,0 +1,2328 @@ +/* + 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. +*/ + +#ifndef __DRXDRIVER_H__ +#define __DRXDRIVER_H__ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/i2c.h> + +/* + * This structure contains the I2C address, the device ID and a user_data pointer. + * The user_data pointer can be used for application specific purposes. + */ +struct i2c_device_addr { + u16 i2c_addr; /* The I2C address of the device. */ + u16 i2c_dev_id; /* The device identifier. */ + void *user_data; /* User data pointer */ +}; + +/* +* \def IS_I2C_10BIT( addr ) +* \brief Determine if I2C address 'addr' is a 10 bits address or not. +* \param addr The I2C address. +* \return int. +* \retval 0 if address is not a 10 bits I2C address. +* \retval 1 if address is a 10 bits I2C address. +*/ +#define IS_I2C_10BIT(addr) \ + (((addr) & 0xF8) == 0xF0) + +/*------------------------------------------------------------------------------ +Exported FUNCTIONS +------------------------------------------------------------------------------*/ + +/* +* \fn drxbsp_i2c_init() +* \brief Initialize I2C communication module. +* \return int Return status. +* \retval 0 Initialization successful. +* \retval -EIO Initialization failed. +*/ +int drxbsp_i2c_init(void); + +/* +* \fn drxbsp_i2c_term() +* \brief Terminate I2C communication module. +* \return int Return status. +* \retval 0 Termination successful. +* \retval -EIO Termination failed. +*/ +int drxbsp_i2c_term(void); + +/* +* \fn 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) +* \brief Read and/or write count bytes from I2C bus, store them in data[]. +* \param w_dev_addr The device i2c address and the device ID to write to +* \param w_count The number of bytes to write +* \param wData The array to write the data to +* \param r_dev_addr The device i2c address and the device ID to read from +* \param r_count The number of bytes to read +* \param r_data The array to read the data from +* \return int Return status. +* \retval 0 Success. +* \retval -EIO Failure. +* \retval -EINVAL Parameter 'wcount' is not zero but parameter +* 'wdata' contains NULL. +* Idem for 'rcount' and 'rdata'. +* Both w_dev_addr and r_dev_addr are NULL. +* +* This function must implement an atomic write and/or read action on the I2C bus +* No other process may use the I2C bus when this function is executing. +* The critical section of this function runs from and including the I2C +* write, up to and including the I2C read action. +* +* The device ID can be useful if several devices share an I2C address. +* It can be used to control a "switch" on the I2C bus to the correct device. +*/ +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); + +/* +* \fn drxbsp_i2c_error_text() +* \brief Returns a human readable error. +* Counter part of numerical drx_i2c_error_g. +* +* \return char* Pointer to human readable error text. +*/ +char *drxbsp_i2c_error_text(void); + +/* +* \var drx_i2c_error_g; +* \brief I2C specific error codes, platform dependent. +*/ +extern int drx_i2c_error_g; + +#define TUNER_MODE_SUB0 0x0001 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB1 0x0002 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB2 0x0004 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB3 0x0008 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB4 0x0010 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB5 0x0020 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB6 0x0040 /* for sub-mode (e.g. RF-AGC setting) */ +#define TUNER_MODE_SUB7 0x0080 /* for sub-mode (e.g. RF-AGC setting) */ + +#define TUNER_MODE_DIGITAL 0x0100 /* for digital channel (e.g. DVB-T) */ +#define TUNER_MODE_ANALOG 0x0200 /* for analog channel (e.g. PAL) */ +#define TUNER_MODE_SWITCH 0x0400 /* during channel switch & scanning */ +#define TUNER_MODE_LOCK 0x0800 /* after tuner has locked */ +#define TUNER_MODE_6MHZ 0x1000 /* for 6MHz bandwidth channels */ +#define TUNER_MODE_7MHZ 0x2000 /* for 7MHz bandwidth channels */ +#define TUNER_MODE_8MHZ 0x4000 /* for 8MHz bandwidth channels */ + +#define TUNER_MODE_SUB_MAX 8 +#define TUNER_MODE_SUBALL (TUNER_MODE_SUB0 | TUNER_MODE_SUB1 | \ + TUNER_MODE_SUB2 | TUNER_MODE_SUB3 | \ + TUNER_MODE_SUB4 | TUNER_MODE_SUB5 | \ + TUNER_MODE_SUB6 | TUNER_MODE_SUB7) + + +enum tuner_lock_status { + TUNER_LOCKED, + TUNER_NOT_LOCKED +}; + +struct tuner_common { + char *name; /* Tuner brand & type name */ + s32 min_freq_rf; /* Lowest RF input frequency, in kHz */ + s32 max_freq_rf; /* Highest RF input frequency, in kHz */ + + u8 sub_mode; /* Index to sub-mode in use */ + char ***sub_mode_descriptions; /* Pointer to description of sub-modes */ + u8 sub_modes; /* Number of available sub-modes */ + + /* The following fields will be either 0, NULL or false and do not need + initialisation */ + void *self_check; /* gives proof of initialization */ + bool programmed; /* only valid if self_check is OK */ + s32 r_ffrequency; /* only valid if programmed */ + s32 i_ffrequency; /* only valid if programmed */ + + void *my_user_data; /* pointer to associated demod instance */ + u16 my_capabilities; /* value for storing application flags */ +}; + +struct tuner_instance; + +typedef int(*tuner_open_func_t) (struct tuner_instance *tuner); +typedef int(*tuner_close_func_t) (struct tuner_instance *tuner); + +typedef int(*tuner_set_frequency_func_t) (struct tuner_instance *tuner, + u32 mode, + s32 + frequency); + +typedef int(*tuner_get_frequency_func_t) (struct tuner_instance *tuner, + u32 mode, + s32 * + r_ffrequency, + s32 * + i_ffrequency); + +typedef int(*tuner_lock_status_func_t) (struct tuner_instance *tuner, + enum tuner_lock_status * + lock_stat); + +typedef int(*tune_ri2c_write_read_func_t) (struct tuner_instance *tuner, + 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 tuner_ops { + tuner_open_func_t open_func; + tuner_close_func_t close_func; + tuner_set_frequency_func_t set_frequency_func; + tuner_get_frequency_func_t get_frequency_func; + tuner_lock_status_func_t lock_status_func; + tune_ri2c_write_read_func_t i2c_write_read_func; + +}; + +struct tuner_instance { + struct i2c_device_addr my_i2c_dev_addr; + struct tuner_common *my_common_attr; + void *my_ext_attr; + struct tuner_ops *my_funct; +}; + +int drxbsp_tuner_set_frequency(struct tuner_instance *tuner, + u32 mode, + s32 frequency); + +int drxbsp_tuner_get_frequency(struct tuner_instance *tuner, + u32 mode, + s32 *r_ffrequency, + s32 *i_ffrequency); + +int drxbsp_tuner_default_i2c_write_read(struct tuner_instance *tuner, + 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); + +/************* +* +* This section configures the DRX Data Access Protocols (DAPs). +* +**************/ + +/* +* \def DRXDAP_SINGLE_MASTER +* \brief Enable I2C single or I2C multimaster mode on host. +* +* Set to 1 to enable single master mode +* Set to 0 to enable multi master mode +* +* The actual DAP implementation may be restricted to only one of the modes. +* A compiler warning or error will be generated if the DAP implementation +* overrides or cannot handle the mode defined below. +*/ +#ifndef DRXDAP_SINGLE_MASTER +#define DRXDAP_SINGLE_MASTER 1 +#endif + +/* +* \def DRXDAP_MAX_WCHUNKSIZE +* \brief Defines maximum chunksize of an i2c write action by host. +* +* This indicates the maximum size of data the I2C device driver is able to +* write at a time. This includes I2C device address and register addressing. +* +* This maximum size may be restricted by the actual DAP implementation. +* A compiler warning or error will be generated if the DAP implementation +* overrides or cannot handle the chunksize defined below. +* +* Beware that the DAP uses DRXDAP_MAX_WCHUNKSIZE to create a temporary data +* buffer. Do not undefine or choose too large, unless your system is able to +* handle a stack buffer of that size. +* +*/ +#ifndef DRXDAP_MAX_WCHUNKSIZE +#define DRXDAP_MAX_WCHUNKSIZE 60 +#endif + +/* +* \def DRXDAP_MAX_RCHUNKSIZE +* \brief Defines maximum chunksize of an i2c read action by host. +* +* This indicates the maximum size of data the I2C device driver is able to read +* at a time. Minimum value is 2. Also, the read chunk size must be even. +* +* This maximum size may be restricted by the actual DAP implementation. +* A compiler warning or error will be generated if the DAP implementation +* overrides or cannot handle the chunksize defined below. +*/ +#ifndef DRXDAP_MAX_RCHUNKSIZE +#define DRXDAP_MAX_RCHUNKSIZE 60 +#endif + +/************* +* +* This section describes drxdriver defines. +* +**************/ + +/* +* \def DRX_UNKNOWN +* \brief Generic UNKNOWN value for DRX enumerated types. +* +* Used to indicate that the parameter value is unknown or not yet initialized. +*/ +#ifndef DRX_UNKNOWN +#define DRX_UNKNOWN (254) +#endif + +/* +* \def DRX_AUTO +* \brief Generic AUTO value for DRX enumerated types. +* +* Used to instruct the driver to automatically determine the value of the +* parameter. +*/ +#ifndef DRX_AUTO +#define DRX_AUTO (255) +#endif + +/************* +* +* This section describes flag definitions for the device capbilities. +* +**************/ + +/* +* \brief LNA capability flag +* +* Device has a Low Noise Amplifier +* +*/ +#define DRX_CAPABILITY_HAS_LNA (1UL << 0) +/* +* \brief OOB-RX capability flag +* +* Device has OOB-RX +* +*/ +#define DRX_CAPABILITY_HAS_OOBRX (1UL << 1) +/* +* \brief ATV capability flag +* +* Device has ATV +* +*/ +#define DRX_CAPABILITY_HAS_ATV (1UL << 2) +/* +* \brief DVB-T capability flag +* +* Device has DVB-T +* +*/ +#define DRX_CAPABILITY_HAS_DVBT (1UL << 3) +/* +* \brief ITU-B capability flag +* +* Device has ITU-B +* +*/ +#define DRX_CAPABILITY_HAS_ITUB (1UL << 4) +/* +* \brief Audio capability flag +* +* Device has Audio +* +*/ +#define DRX_CAPABILITY_HAS_AUD (1UL << 5) +/* +* \brief SAW switch capability flag +* +* Device has SAW switch +* +*/ +#define DRX_CAPABILITY_HAS_SAWSW (1UL << 6) +/* +* \brief GPIO1 capability flag +* +* Device has GPIO1 +* +*/ +#define DRX_CAPABILITY_HAS_GPIO1 (1UL << 7) +/* +* \brief GPIO2 capability flag +* +* Device has GPIO2 +* +*/ +#define DRX_CAPABILITY_HAS_GPIO2 (1UL << 8) +/* +* \brief IRQN capability flag +* +* Device has IRQN +* +*/ +#define DRX_CAPABILITY_HAS_IRQN (1UL << 9) +/* +* \brief 8VSB capability flag +* +* Device has 8VSB +* +*/ +#define DRX_CAPABILITY_HAS_8VSB (1UL << 10) +/* +* \brief SMA-TX capability flag +* +* Device has SMATX +* +*/ +#define DRX_CAPABILITY_HAS_SMATX (1UL << 11) +/* +* \brief SMA-RX capability flag +* +* Device has SMARX +* +*/ +#define DRX_CAPABILITY_HAS_SMARX (1UL << 12) +/* +* \brief ITU-A/C capability flag +* +* Device has ITU-A/C +* +*/ +#define DRX_CAPABILITY_HAS_ITUAC (1UL << 13) + +/*------------------------------------------------------------------------- +MACROS +-------------------------------------------------------------------------*/ +/* Macros to stringify the version number */ +#define DRX_VERSIONSTRING(MAJOR, MINOR, PATCH) \ + DRX_VERSIONSTRING_HELP(MAJOR)"." \ + DRX_VERSIONSTRING_HELP(MINOR)"." \ + DRX_VERSIONSTRING_HELP(PATCH) +#define DRX_VERSIONSTRING_HELP(NUM) #NUM + +/* +* \brief Macro to create byte array elements from 16 bit integers. +* 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 DRX_16TO8(x) ((u8) (((u16)x) & 0xFF)), \ + ((u8)((((u16)x)>>8)&0xFF)) + +/* +* \brief Macro to convert 16 bit register value to a s32 +*/ +#define DRX_U16TODRXFREQ(x) ((x & 0x8000) ? \ + ((s32) \ + (((u32) x) | 0xFFFF0000)) : \ + ((s32) x)) + +/*------------------------------------------------------------------------- +ENUM +-------------------------------------------------------------------------*/ + +/* +* \enum enum drx_standard +* \brief Modulation standards. +*/ +enum drx_standard { + DRX_STANDARD_DVBT = 0, /*< Terrestrial DVB-T. */ + DRX_STANDARD_8VSB, /*< Terrestrial 8VSB. */ + DRX_STANDARD_NTSC, /*< Terrestrial\Cable analog NTSC. */ + DRX_STANDARD_PAL_SECAM_BG, + /*< Terrestrial analog PAL/SECAM B/G */ + DRX_STANDARD_PAL_SECAM_DK, + /*< Terrestrial analog PAL/SECAM D/K */ + DRX_STANDARD_PAL_SECAM_I, + /*< Terrestrial analog PAL/SECAM I */ + DRX_STANDARD_PAL_SECAM_L, + /*< Terrestrial analog PAL/SECAM L + with negative modulation */ + DRX_STANDARD_PAL_SECAM_LP, + /*< Terrestrial analog PAL/SECAM L + with positive modulation */ + DRX_STANDARD_ITU_A, /*< Cable ITU ANNEX A. */ + DRX_STANDARD_ITU_B, /*< Cable ITU ANNEX B. */ + DRX_STANDARD_ITU_C, /*< Cable ITU ANNEX C. */ + DRX_STANDARD_ITU_D, /*< Cable ITU ANNEX D. */ + DRX_STANDARD_FM, /*< Terrestrial\Cable FM radio */ + DRX_STANDARD_DTMB, /*< Terrestrial DTMB standard (China)*/ + DRX_STANDARD_UNKNOWN = DRX_UNKNOWN, + /*< Standard unknown. */ + DRX_STANDARD_AUTO = DRX_AUTO + /*< Autodetect standard. */ +}; + +/* +* \enum enum drx_standard +* \brief Modulation sub-standards. +*/ +enum drx_substandard { + DRX_SUBSTANDARD_MAIN = 0, /*< Main subvariant of standard */ + DRX_SUBSTANDARD_ATV_BG_SCANDINAVIA, + DRX_SUBSTANDARD_ATV_DK_POLAND, + DRX_SUBSTANDARD_ATV_DK_CHINA, + DRX_SUBSTANDARD_UNKNOWN = DRX_UNKNOWN, + /*< Sub-standard unknown. */ + DRX_SUBSTANDARD_AUTO = DRX_AUTO + /*< Auto (default) sub-standard */ +}; + +/* +* \enum enum drx_bandwidth +* \brief Channel bandwidth or channel spacing. +*/ +enum drx_bandwidth { + DRX_BANDWIDTH_8MHZ = 0, /*< Bandwidth 8 MHz. */ + DRX_BANDWIDTH_7MHZ, /*< Bandwidth 7 MHz. */ + DRX_BANDWIDTH_6MHZ, /*< Bandwidth 6 MHz. */ + DRX_BANDWIDTH_UNKNOWN = DRX_UNKNOWN, + /*< Bandwidth unknown. */ + DRX_BANDWIDTH_AUTO = DRX_AUTO + /*< Auto Set Bandwidth */ +}; + +/* +* \enum enum drx_mirror +* \brief Indicate if channel spectrum is mirrored or not. +*/ +enum drx_mirror { + DRX_MIRROR_NO = 0, /*< Spectrum is not mirrored. */ + DRX_MIRROR_YES, /*< Spectrum is mirrored. */ + DRX_MIRROR_UNKNOWN = DRX_UNKNOWN, + /*< Unknown if spectrum is mirrored. */ + DRX_MIRROR_AUTO = DRX_AUTO + /*< Autodetect if spectrum is mirrored. */ +}; + +/* +* \enum enum drx_modulation +* \brief Constellation type of the channel. +*/ +enum drx_modulation { + DRX_CONSTELLATION_BPSK = 0, /*< Modulation is BPSK. */ + DRX_CONSTELLATION_QPSK, /*< Constellation is QPSK. */ + DRX_CONSTELLATION_PSK8, /*< Constellation is PSK8. */ + DRX_CONSTELLATION_QAM16, /*< Constellation is QAM16. */ + DRX_CONSTELLATION_QAM32, /*< Constellation is QAM32. */ + DRX_CONSTELLATION_QAM64, /*< Constellation is QAM64. */ + DRX_CONSTELLATION_QAM128, /*< Constellation is QAM128. */ + DRX_CONSTELLATION_QAM256, /*< Constellation is QAM256. */ + DRX_CONSTELLATION_QAM512, /*< Constellation is QAM512. */ + DRX_CONSTELLATION_QAM1024, /*< Constellation is QAM1024. */ + DRX_CONSTELLATION_QPSK_NR, /*< Constellation is QPSK_NR */ + DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN, + /*< Constellation unknown. */ + DRX_CONSTELLATION_AUTO = DRX_AUTO + /*< Autodetect constellation. */ +}; + +/* +* \enum enum drx_hierarchy +* \brief Hierarchy of the channel. +*/ +enum drx_hierarchy { + DRX_HIERARCHY_NONE = 0, /*< None hierarchical channel. */ + DRX_HIERARCHY_ALPHA1, /*< Hierarchical channel, alpha=1. */ + DRX_HIERARCHY_ALPHA2, /*< Hierarchical channel, alpha=2. */ + DRX_HIERARCHY_ALPHA4, /*< Hierarchical channel, alpha=4. */ + DRX_HIERARCHY_UNKNOWN = DRX_UNKNOWN, + /*< Hierarchy unknown. */ + DRX_HIERARCHY_AUTO = DRX_AUTO + /*< Autodetect hierarchy. */ +}; + +/* +* \enum enum drx_priority +* \brief Channel priority in case of hierarchical transmission. +*/ +enum drx_priority { + DRX_PRIORITY_LOW = 0, /*< Low priority channel. */ + DRX_PRIORITY_HIGH, /*< High priority channel. */ + DRX_PRIORITY_UNKNOWN = DRX_UNKNOWN + /*< Priority unknown. */ +}; + +/* +* \enum enum drx_coderate +* \brief Channel priority in case of hierarchical transmission. +*/ +enum drx_coderate { + DRX_CODERATE_1DIV2 = 0, /*< Code rate 1/2nd. */ + DRX_CODERATE_2DIV3, /*< Code rate 2/3nd. */ + DRX_CODERATE_3DIV4, /*< Code rate 3/4nd. */ + DRX_CODERATE_5DIV6, /*< Code rate 5/6nd. */ + DRX_CODERATE_7DIV8, /*< Code rate 7/8nd. */ + DRX_CODERATE_UNKNOWN = DRX_UNKNOWN, + /*< Code rate unknown. */ + DRX_CODERATE_AUTO = DRX_AUTO + /*< Autodetect code rate. */ +}; + +/* +* \enum enum drx_guard +* \brief Guard interval of a channel. +*/ +enum drx_guard { + DRX_GUARD_1DIV32 = 0, /*< Guard interval 1/32nd. */ + DRX_GUARD_1DIV16, /*< Guard interval 1/16th. */ + DRX_GUARD_1DIV8, /*< Guard interval 1/8th. */ + DRX_GUARD_1DIV4, /*< Guard interval 1/4th. */ + DRX_GUARD_UNKNOWN = DRX_UNKNOWN, + /*< Guard interval unknown. */ + DRX_GUARD_AUTO = DRX_AUTO + /*< Autodetect guard interval. */ +}; + +/* +* \enum enum drx_fft_mode +* \brief FFT mode. +*/ +enum drx_fft_mode { + DRX_FFTMODE_2K = 0, /*< 2K FFT mode. */ + DRX_FFTMODE_4K, /*< 4K FFT mode. */ + DRX_FFTMODE_8K, /*< 8K FFT mode. */ + DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN, + /*< FFT mode unknown. */ + DRX_FFTMODE_AUTO = DRX_AUTO + /*< Autodetect FFT mode. */ +}; + +/* +* \enum enum drx_classification +* \brief Channel classification. +*/ +enum drx_classification { + DRX_CLASSIFICATION_GAUSS = 0, /*< Gaussion noise. */ + DRX_CLASSIFICATION_HVY_GAUSS, /*< Heavy Gaussion noise. */ + DRX_CLASSIFICATION_COCHANNEL, /*< Co-channel. */ + DRX_CLASSIFICATION_STATIC, /*< Static echo. */ + DRX_CLASSIFICATION_MOVING, /*< Moving echo. */ + DRX_CLASSIFICATION_ZERODB, /*< Zero dB echo. */ + DRX_CLASSIFICATION_UNKNOWN = DRX_UNKNOWN, + /*< Unknown classification */ + DRX_CLASSIFICATION_AUTO = DRX_AUTO + /*< Autodetect classification. */ +}; + +/* +* /enum enum drx_interleave_mode +* /brief Interleave modes +*/ +enum drx_interleave_mode { + DRX_INTERLEAVEMODE_I128_J1 = 0, + DRX_INTERLEAVEMODE_I128_J1_V2, + DRX_INTERLEAVEMODE_I128_J2, + DRX_INTERLEAVEMODE_I64_J2, + DRX_INTERLEAVEMODE_I128_J3, + DRX_INTERLEAVEMODE_I32_J4, + DRX_INTERLEAVEMODE_I128_J4, + DRX_INTERLEAVEMODE_I16_J8, + DRX_INTERLEAVEMODE_I128_J5, + DRX_INTERLEAVEMODE_I8_J16, + DRX_INTERLEAVEMODE_I128_J6, + DRX_INTERLEAVEMODE_RESERVED_11, + DRX_INTERLEAVEMODE_I128_J7, + DRX_INTERLEAVEMODE_RESERVED_13, + DRX_INTERLEAVEMODE_I128_J8, + DRX_INTERLEAVEMODE_RESERVED_15, + DRX_INTERLEAVEMODE_I12_J17, + DRX_INTERLEAVEMODE_I5_J4, + DRX_INTERLEAVEMODE_B52_M240, + DRX_INTERLEAVEMODE_B52_M720, + DRX_INTERLEAVEMODE_B52_M48, + DRX_INTERLEAVEMODE_B52_M0, + DRX_INTERLEAVEMODE_UNKNOWN = DRX_UNKNOWN, + /*< Unknown interleave mode */ + DRX_INTERLEAVEMODE_AUTO = DRX_AUTO + /*< Autodetect interleave mode */ +}; + +/* +* \enum enum drx_carrier_mode +* \brief Channel Carrier Mode. +*/ +enum drx_carrier_mode { + DRX_CARRIER_MULTI = 0, /*< Multi carrier mode */ + DRX_CARRIER_SINGLE, /*< Single carrier mode */ + DRX_CARRIER_UNKNOWN = DRX_UNKNOWN, + /*< Carrier mode unknown. */ + DRX_CARRIER_AUTO = DRX_AUTO /*< Autodetect carrier mode */ +}; + +/* +* \enum enum drx_frame_mode +* \brief Channel Frame Mode. +*/ +enum drx_frame_mode { + DRX_FRAMEMODE_420 = 0, /*< 420 with variable PN */ + DRX_FRAMEMODE_595, /*< 595 */ + DRX_FRAMEMODE_945, /*< 945 with variable PN */ + DRX_FRAMEMODE_420_FIXED_PN, + /*< 420 with fixed PN */ + DRX_FRAMEMODE_945_FIXED_PN, + /*< 945 with fixed PN */ + DRX_FRAMEMODE_UNKNOWN = DRX_UNKNOWN, + /*< Frame mode unknown. */ + DRX_FRAMEMODE_AUTO = DRX_AUTO + /*< Autodetect frame mode */ +}; + +/* +* \enum enum drx_tps_frame +* \brief Frame number in current super-frame. +*/ +enum drx_tps_frame { + DRX_TPS_FRAME1 = 0, /*< TPS frame 1. */ + DRX_TPS_FRAME2, /*< TPS frame 2. */ + DRX_TPS_FRAME3, /*< TPS frame 3. */ + DRX_TPS_FRAME4, /*< TPS frame 4. */ + DRX_TPS_FRAME_UNKNOWN = DRX_UNKNOWN + /*< TPS frame unknown. */ +}; + +/* +* \enum enum drx_ldpc +* \brief TPS LDPC . +*/ +enum drx_ldpc { + DRX_LDPC_0_4 = 0, /*< LDPC 0.4 */ + DRX_LDPC_0_6, /*< LDPC 0.6 */ + DRX_LDPC_0_8, /*< LDPC 0.8 */ + DRX_LDPC_UNKNOWN = DRX_UNKNOWN, + /*< LDPC unknown. */ + DRX_LDPC_AUTO = DRX_AUTO /*< Autodetect LDPC */ +}; + +/* +* \enum enum drx_pilot_mode +* \brief Pilot modes in DTMB. +*/ +enum drx_pilot_mode { + DRX_PILOT_ON = 0, /*< Pilot On */ + DRX_PILOT_OFF, /*< Pilot Off */ + DRX_PILOT_UNKNOWN = DRX_UNKNOWN, + /*< Pilot unknown. */ + DRX_PILOT_AUTO = DRX_AUTO /*< Autodetect Pilot */ +}; + +/* + * enum drxu_code_action - indicate if firmware has to be uploaded or verified. + * @UCODE_UPLOAD: Upload the microcode image to device + * @UCODE_VERIFY: Compare microcode image with code on device + */ +enum drxu_code_action { + UCODE_UPLOAD, + UCODE_VERIFY +}; + +/* +* \enum enum drx_lock_status * \brief Used to reflect current lock status of demodulator. +* +* The generic lock states have device dependent semantics. + + DRX_NEVER_LOCK = 0, + **< Device will never lock on this signal * + DRX_NOT_LOCKED, + **< Device has no lock at all * + DRX_LOCK_STATE_1, + **< Generic lock state * + DRX_LOCK_STATE_2, + **< Generic lock state * + DRX_LOCK_STATE_3, + **< Generic lock state * + DRX_LOCK_STATE_4, + **< Generic lock state * + DRX_LOCK_STATE_5, + **< Generic lock state * + DRX_LOCK_STATE_6, + **< Generic lock state * + DRX_LOCK_STATE_7, + **< Generic lock state * + DRX_LOCK_STATE_8, + **< Generic lock state * + DRX_LOCK_STATE_9, + **< Generic lock state * + DRX_LOCKED **< Device is in lock * +*/ + +enum drx_lock_status { + DRX_NEVER_LOCK = 0, + DRX_NOT_LOCKED, + DRX_LOCK_STATE_1, + DRX_LOCK_STATE_2, + DRX_LOCK_STATE_3, + DRX_LOCK_STATE_4, + DRX_LOCK_STATE_5, + DRX_LOCK_STATE_6, + DRX_LOCK_STATE_7, + DRX_LOCK_STATE_8, + DRX_LOCK_STATE_9, + DRX_LOCKED +}; + +/* +* \enum enum drx_uio* \brief Used to address a User IO (UIO). +*/ +enum drx_uio { + DRX_UIO1, + DRX_UIO2, + DRX_UIO3, + DRX_UIO4, + DRX_UIO5, + DRX_UIO6, + DRX_UIO7, + DRX_UIO8, + DRX_UIO9, + DRX_UIO10, + DRX_UIO11, + DRX_UIO12, + DRX_UIO13, + DRX_UIO14, + DRX_UIO15, + DRX_UIO16, + DRX_UIO17, + DRX_UIO18, + DRX_UIO19, + DRX_UIO20, + DRX_UIO21, + DRX_UIO22, + DRX_UIO23, + DRX_UIO24, + DRX_UIO25, + DRX_UIO26, + DRX_UIO27, + DRX_UIO28, + DRX_UIO29, + DRX_UIO30, + DRX_UIO31, + DRX_UIO32, + DRX_UIO_MAX = DRX_UIO32 +}; + +/* +* \enum enum drxuio_mode * \brief Used to configure the modus oprandi of a UIO. +* +* DRX_UIO_MODE_FIRMWARE is an old uio mode. +* It is replaced by the modes DRX_UIO_MODE_FIRMWARE0 .. DRX_UIO_MODE_FIRMWARE9. +* To be backward compatible DRX_UIO_MODE_FIRMWARE is equivalent to +* DRX_UIO_MODE_FIRMWARE0. +*/ +enum drxuio_mode { + DRX_UIO_MODE_DISABLE = 0x01, + /*< not used, pin is configured as input */ + DRX_UIO_MODE_READWRITE = 0x02, + /*< used for read/write by application */ + DRX_UIO_MODE_FIRMWARE = 0x04, + /*< controlled by firmware, function 0 */ + DRX_UIO_MODE_FIRMWARE0 = DRX_UIO_MODE_FIRMWARE, + /*< same as above */ + DRX_UIO_MODE_FIRMWARE1 = 0x08, + /*< controlled by firmware, function 1 */ + DRX_UIO_MODE_FIRMWARE2 = 0x10, + /*< controlled by firmware, function 2 */ + DRX_UIO_MODE_FIRMWARE3 = 0x20, + /*< controlled by firmware, function 3 */ + DRX_UIO_MODE_FIRMWARE4 = 0x40, + /*< controlled by firmware, function 4 */ + DRX_UIO_MODE_FIRMWARE5 = 0x80 + /*< controlled by firmware, function 5 */ +}; + +/* +* \enum enum drxoob_downstream_standard * \brief Used to select OOB standard. +* +* Based on ANSI 55-1 and 55-2 +*/ +enum drxoob_downstream_standard { + DRX_OOB_MODE_A = 0, + /*< ANSI 55-1 */ + DRX_OOB_MODE_B_GRADE_A, + /*< ANSI 55-2 A */ + DRX_OOB_MODE_B_GRADE_B + /*< ANSI 55-2 B */ +}; + +/*------------------------------------------------------------------------- +STRUCTS +-------------------------------------------------------------------------*/ + +/*============================================================================*/ +/*============================================================================*/ +/*== CTRL CFG related data structures ========================================*/ +/*============================================================================*/ +/*============================================================================*/ + +#ifndef DRX_CFG_BASE +#define DRX_CFG_BASE 0 +#endif + +#define DRX_CFG_MPEG_OUTPUT (DRX_CFG_BASE + 0) /* MPEG TS output */ +#define DRX_CFG_PKTERR (DRX_CFG_BASE + 1) /* Packet Error */ +#define DRX_CFG_SYMCLK_OFFS (DRX_CFG_BASE + 2) /* Symbol Clk Offset */ +#define DRX_CFG_SMA (DRX_CFG_BASE + 3) /* Smart Antenna */ +#define DRX_CFG_PINSAFE (DRX_CFG_BASE + 4) /* Pin safe mode */ +#define DRX_CFG_SUBSTANDARD (DRX_CFG_BASE + 5) /* substandard */ +#define DRX_CFG_AUD_VOLUME (DRX_CFG_BASE + 6) /* volume */ +#define DRX_CFG_AUD_RDS (DRX_CFG_BASE + 7) /* rds */ +#define DRX_CFG_AUD_AUTOSOUND (DRX_CFG_BASE + 8) /* ASS & ASC */ +#define DRX_CFG_AUD_ASS_THRES (DRX_CFG_BASE + 9) /* ASS Thresholds */ +#define DRX_CFG_AUD_DEVIATION (DRX_CFG_BASE + 10) /* Deviation */ +#define DRX_CFG_AUD_PRESCALE (DRX_CFG_BASE + 11) /* Prescale */ +#define DRX_CFG_AUD_MIXER (DRX_CFG_BASE + 12) /* Mixer */ +#define DRX_CFG_AUD_AVSYNC (DRX_CFG_BASE + 13) /* AVSync */ +#define DRX_CFG_AUD_CARRIER (DRX_CFG_BASE + 14) /* Audio carriers */ +#define DRX_CFG_I2S_OUTPUT (DRX_CFG_BASE + 15) /* I2S output */ +#define DRX_CFG_ATV_STANDARD (DRX_CFG_BASE + 16) /* ATV standard */ +#define DRX_CFG_SQI_SPEED (DRX_CFG_BASE + 17) /* SQI speed */ +#define DRX_CTRL_CFG_MAX (DRX_CFG_BASE + 18) /* never to be used */ + +#define DRX_CFG_PINS_SAFE_MODE DRX_CFG_PINSAFE +/*============================================================================*/ +/*============================================================================*/ +/*== CTRL related data structures ============================================*/ +/*============================================================================*/ +/*============================================================================*/ + +/* + * struct drxu_code_info Parameters for microcode upload and verfiy. + * + * @mc_file: microcode file name + * + * Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE + */ +struct drxu_code_info { + char *mc_file; +}; + +/* +* \struct drx_mc_version_rec_t +* \brief Microcode version record +* Version numbers are stored in BCD format, as usual: +* o major number = bits 31-20 (first three nibbles of MSW) +* o minor number = bits 19-16 (fourth nibble of MSW) +* o patch number = bits 15-0 (remaining nibbles in LSW) +* +* The device type indicates for which the device is meant. It is based on the +* JTAG ID, using everything except the bond ID and the metal fix. +* +* Special values: +* - mc_dev_type == 0 => any device allowed +* - mc_base_version == 0.0.0 => full microcode (mc_version is the version) +* - mc_base_version != 0.0.0 => patch microcode, the base microcode version +* (mc_version is the version) +*/ +#define AUX_VER_RECORD 0x8000 + +struct drx_mc_version_rec { + u16 aux_type; /* type of aux data - 0x8000 for version record */ + u32 mc_dev_type; /* device type, based on JTAG ID */ + u32 mc_version; /* version of microcode */ + u32 mc_base_version; /* in case of patch: the original microcode version */ +}; + +/*========================================*/ + +/* +* \struct drx_filter_info_t +* \brief Parameters for loading filter coefficients +* +* Used by DRX_CTRL_LOAD_FILTER +*/ +struct drx_filter_info { + u8 *data_re; + /*< pointer to coefficients for RE */ + u8 *data_im; + /*< pointer to coefficients for IM */ + u16 size_re; + /*< size of coefficients for RE */ + u16 size_im; + /*< size of coefficients for IM */ +}; + +/*========================================*/ + +/* +* \struct struct drx_channel * \brief The set of parameters describing a single channel. +* +* Used by DRX_CTRL_SET_CHANNEL and DRX_CTRL_GET_CHANNEL. +* Only certain fields need to be used for a specific standard. +* +*/ +struct drx_channel { + s32 frequency; + /*< frequency in kHz */ + enum drx_bandwidth bandwidth; + /*< bandwidth */ + enum drx_mirror mirror; /*< mirrored or not on RF */ + enum drx_modulation constellation; + /*< constellation */ + enum drx_hierarchy hierarchy; + /*< hierarchy */ + enum drx_priority priority; /*< priority */ + enum drx_coderate coderate; /*< coderate */ + enum drx_guard guard; /*< guard interval */ + enum drx_fft_mode fftmode; /*< fftmode */ + enum drx_classification classification; + /*< classification */ + u32 symbolrate; + /*< symbolrate in symbols/sec */ + enum drx_interleave_mode interleavemode; + /*< interleaveMode QAM */ + enum drx_ldpc ldpc; /*< ldpc */ + enum drx_carrier_mode carrier; /*< carrier */ + enum drx_frame_mode framemode; + /*< frame mode */ + enum drx_pilot_mode pilot; /*< pilot mode */ +}; + +/*========================================*/ + +enum drx_cfg_sqi_speed { + DRX_SQI_SPEED_FAST = 0, + DRX_SQI_SPEED_MEDIUM, + DRX_SQI_SPEED_SLOW, + DRX_SQI_SPEED_UNKNOWN = DRX_UNKNOWN +}; + +/*========================================*/ + +/* +* \struct struct drx_complex * A complex number. +* +* Used by DRX_CTRL_CONSTEL. +*/ +struct drx_complex { + s16 im; + /*< Imaginary part. */ + s16 re; + /*< Real part. */ +}; + +/*========================================*/ + +/* +* \struct struct drx_frequency_plan * Array element of a frequency plan. +* +* Used by DRX_CTRL_SCAN_INIT. +*/ +struct drx_frequency_plan { + s32 first; + /*< First centre frequency in this band */ + s32 last; + /*< Last centre frequency in this band */ + s32 step; + /*< Stepping frequency in this band */ + enum drx_bandwidth bandwidth; + /*< Bandwidth within this frequency band */ + u16 ch_number; + /*< First channel number in this band, or first + index in ch_names */ + char **ch_names; + /*< Optional list of channel names in this + band */ +}; + +/*========================================*/ + +/* +* \struct struct drx_scan_param * Parameters for channel scan. +* +* Used by DRX_CTRL_SCAN_INIT. +*/ +struct drx_scan_param { + struct drx_frequency_plan *frequency_plan; + /*< Frequency plan (array)*/ + u16 frequency_plan_size; /*< Number of bands */ + u32 num_tries; /*< Max channels tried */ + s32 skip; /*< Minimum frequency step to take + after a channel is found */ + void *ext_params; /*< Standard specific params */ +}; + +/*========================================*/ + +/* +* \brief Scan commands. +* Used by scanning algorithms. +*/ +enum drx_scan_command { + DRX_SCAN_COMMAND_INIT = 0,/*< Initialize scanning */ + DRX_SCAN_COMMAND_NEXT, /*< Next scan */ + DRX_SCAN_COMMAND_STOP /*< Stop scanning */ +}; + +/*========================================*/ + +/* +* \brief Inner scan function prototype. +*/ +typedef int(*drx_scan_func_t) (void *scan_context, + enum drx_scan_command scan_command, + struct drx_channel *scan_channel, + bool *get_next_channel); + +/*========================================*/ + +/* +* \struct struct drxtps_info * TPS information, DVB-T specific. +* +* Used by DRX_CTRL_TPS_INFO. +*/ + struct drxtps_info { + enum drx_fft_mode fftmode; /*< Fft mode */ + enum drx_guard guard; /*< Guard interval */ + enum drx_modulation constellation; + /*< Constellation */ + enum drx_hierarchy hierarchy; + /*< Hierarchy */ + enum drx_coderate high_coderate; + /*< High code rate */ + enum drx_coderate low_coderate; + /*< Low cod rate */ + enum drx_tps_frame frame; /*< Tps frame */ + u8 length; /*< Length */ + u16 cell_id; /*< Cell id */ + }; + +/*========================================*/ + +/* +* \brief Power mode of device. +* +* Used by DRX_CTRL_SET_POWER_MODE. +*/ + enum drx_power_mode { + DRX_POWER_UP = 0, + /*< Generic , Power Up Mode */ + DRX_POWER_MODE_1, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_2, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_3, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_4, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_5, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_6, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_7, + /*< Device specific , Power Up Mode */ + DRX_POWER_MODE_8, + /*< Device specific , Power Up Mode */ + + DRX_POWER_MODE_9, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_10, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_11, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_12, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_13, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_14, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_15, + /*< Device specific , Power Down Mode */ + DRX_POWER_MODE_16, + /*< Device specific , Power Down Mode */ + DRX_POWER_DOWN = 255 + /*< Generic , Power Down Mode */ + }; + +/*========================================*/ + +/* +* \enum enum drx_module * \brief Software module identification. +* +* Used by DRX_CTRL_VERSION. +*/ + enum drx_module { + DRX_MODULE_DEVICE, + DRX_MODULE_MICROCODE, + DRX_MODULE_DRIVERCORE, + DRX_MODULE_DEVICEDRIVER, + DRX_MODULE_DAP, + DRX_MODULE_BSP_I2C, + DRX_MODULE_BSP_TUNER, + DRX_MODULE_BSP_HOST, + DRX_MODULE_UNKNOWN + }; + +/* +* \enum struct drx_version * \brief Version information of one software module. +* +* Used by DRX_CTRL_VERSION. +*/ + struct drx_version { + enum drx_module module_type; + /*< Type identifier of the module */ + char *module_name; + /*< Name or description of module */ + u16 v_major; /*< Major version number */ + u16 v_minor; /*< Minor version number */ + u16 v_patch; /*< Patch version number */ + char *v_string; /*< Version as text string */ + }; + +/* +* \enum struct drx_version_list * \brief List element of NULL terminated, linked list for version information. +* +* Used by DRX_CTRL_VERSION. +*/ +struct drx_version_list { + struct drx_version *version;/*< Version information */ + struct drx_version_list *next; + /*< Next list element */ +}; + +/*========================================*/ + +/* +* \brief Parameters needed to confiugure a UIO. +* +* Used by DRX_CTRL_UIO_CFG. +*/ + struct drxuio_cfg { + enum drx_uio uio; + /*< UIO identifier */ + enum drxuio_mode mode; + /*< UIO operational mode */ + }; + +/*========================================*/ + +/* +* \brief Parameters needed to read from or write to a UIO. +* +* Used by DRX_CTRL_UIO_READ and DRX_CTRL_UIO_WRITE. +*/ + struct drxuio_data { + enum drx_uio uio; + /*< UIO identifier */ + bool value; + /*< UIO value (true=1, false=0) */ + }; + +/*========================================*/ + +/* +* \brief Parameters needed to configure OOB. +* +* Used by DRX_CTRL_SET_OOB. +*/ + struct drxoob { + s32 frequency; /*< Frequency in kHz */ + enum drxoob_downstream_standard standard; + /*< OOB standard */ + bool spectrum_inverted; /*< If true, then spectrum + is inverted */ + }; + +/*========================================*/ + +/* +* \brief Metrics from OOB. +* +* Used by DRX_CTRL_GET_OOB. +*/ + struct drxoob_status { + s32 frequency; /*< Frequency in Khz */ + enum drx_lock_status lock; /*< Lock status */ + u32 mer; /*< MER */ + s32 symbol_rate_offset; /*< Symbolrate offset in ppm */ + }; + +/*========================================*/ + +/* +* \brief Device dependent configuration data. +* +* Used by DRX_CTRL_SET_CFG and DRX_CTRL_GET_CFG. +* A sort of nested drx_ctrl() functionality for device specific controls. +*/ + struct drx_cfg { + u32 cfg_type; + /*< Function identifier */ + void *cfg_data; + /*< Function data */ + }; + +/*========================================*/ + +/* +* /struct DRXMpegStartWidth_t +* MStart width [nr MCLK cycles] for serial MPEG output. +*/ + + enum drxmpeg_str_width { + DRX_MPEG_STR_WIDTH_1, + DRX_MPEG_STR_WIDTH_8 + }; + +/* CTRL CFG MPEG output */ +/* +* \struct struct drx_cfg_mpeg_output * \brief Configuration parameters for MPEG output control. +* +* Used by DRX_CFG_MPEG_OUTPUT, in combination with DRX_CTRL_SET_CFG and +* DRX_CTRL_GET_CFG. +*/ + + struct drx_cfg_mpeg_output { + bool enable_mpeg_output;/*< If true, enable MPEG output */ + bool insert_rs_byte; /*< If true, insert RS byte */ + bool enable_parallel; /*< If true, parallel out otherwise + serial */ + bool invert_data; /*< If true, invert DATA signals */ + bool invert_err; /*< If true, invert ERR signal */ + bool invert_str; /*< If true, invert STR signals */ + bool invert_val; /*< If true, invert VAL signals */ + bool invert_clk; /*< If true, invert CLK signals */ + bool static_clk; /*< If true, static MPEG clockrate + will be used, otherwise clockrate + will adapt to the bitrate of the + TS */ + u32 bitrate; /*< Maximum bitrate in b/s in case + static clockrate is selected */ + enum drxmpeg_str_width width_str; + /*< MPEG start width */ + }; + + +/*========================================*/ + +/* +* \struct struct drxi2c_data * \brief Data for I2C via 2nd or 3rd or etc I2C port. +* +* Used by DRX_CTRL_I2C_READWRITE. +* If port_nr is equal to primairy port_nr BSPI2C will be used. +* +*/ + struct drxi2c_data { + u16 port_nr; /*< I2C port number */ + struct i2c_device_addr *w_dev_addr; + /*< Write device address */ + u16 w_count; /*< Size of write data in bytes */ + u8 *wData; /*< Pointer to write data */ + struct i2c_device_addr *r_dev_addr; + /*< Read device address */ + u16 r_count; /*< Size of data to read in bytes */ + u8 *r_data; /*< Pointer to read buffer */ + }; + +/*========================================*/ + +/* +* \enum enum drx_aud_standard * \brief Audio standard identifier. +* +* Used by DRX_CTRL_SET_AUD. +*/ + enum drx_aud_standard { + DRX_AUD_STANDARD_BTSC, /*< set BTSC standard (USA) */ + DRX_AUD_STANDARD_A2, /*< set A2-Korea FM Stereo */ + DRX_AUD_STANDARD_EIAJ, /*< set to Japanese FM Stereo */ + DRX_AUD_STANDARD_FM_STEREO,/*< set to FM-Stereo Radio */ + DRX_AUD_STANDARD_M_MONO, /*< for 4.5 MHz mono detected */ + DRX_AUD_STANDARD_D_K_MONO, /*< for 6.5 MHz mono detected */ + DRX_AUD_STANDARD_BG_FM, /*< set BG_FM standard */ + DRX_AUD_STANDARD_D_K1, /*< set D_K1 standard */ + DRX_AUD_STANDARD_D_K2, /*< set D_K2 standard */ + DRX_AUD_STANDARD_D_K3, /*< set D_K3 standard */ + DRX_AUD_STANDARD_BG_NICAM_FM, + /*< set BG_NICAM_FM standard */ + DRX_AUD_STANDARD_L_NICAM_AM, + /*< set L_NICAM_AM standard */ + DRX_AUD_STANDARD_I_NICAM_FM, + /*< set I_NICAM_FM standard */ + DRX_AUD_STANDARD_D_K_NICAM_FM, + /*< set D_K_NICAM_FM standard */ + DRX_AUD_STANDARD_NOT_READY,/*< used to detect audio standard */ + DRX_AUD_STANDARD_AUTO = DRX_AUTO, + /*< Automatic Standard Detection */ + DRX_AUD_STANDARD_UNKNOWN = DRX_UNKNOWN + /*< used as auto and for readback */ + }; + +/* CTRL_AUD_GET_STATUS - struct drx_aud_status */ +/* +* \enum enum drx_aud_nicam_status * \brief Status of NICAM carrier. +*/ + enum drx_aud_nicam_status { + DRX_AUD_NICAM_DETECTED = 0, + /*< NICAM carrier detected */ + DRX_AUD_NICAM_NOT_DETECTED, + /*< NICAM carrier not detected */ + DRX_AUD_NICAM_BAD /*< NICAM carrier bad quality */ + }; + +/* +* \struct struct drx_aud_status * \brief Audio status characteristics. +*/ + struct drx_aud_status { + bool stereo; /*< stereo detection */ + bool carrier_a; /*< carrier A detected */ + bool carrier_b; /*< carrier B detected */ + bool sap; /*< sap / bilingual detection */ + bool rds; /*< RDS data array present */ + enum drx_aud_nicam_status nicam_status; + /*< status of NICAM carrier */ + s8 fm_ident; /*< FM Identification value */ + }; + +/* CTRL_AUD_READ_RDS - DRXRDSdata_t */ + +/* +* \struct DRXRDSdata_t +* \brief Raw RDS data array. +*/ + struct drx_cfg_aud_rds { + bool valid; /*< RDS data validation */ + u16 data[18]; /*< data from one RDS data array */ + }; + +/* DRX_CFG_AUD_VOLUME - struct drx_cfg_aud_volume - set/get */ +/* +* \enum DRXAudAVCDecayTime_t +* \brief Automatic volume control configuration. +*/ + enum drx_aud_avc_mode { + DRX_AUD_AVC_OFF, /*< Automatic volume control off */ + DRX_AUD_AVC_DECAYTIME_8S, /*< level volume in 8 seconds */ + DRX_AUD_AVC_DECAYTIME_4S, /*< level volume in 4 seconds */ + DRX_AUD_AVC_DECAYTIME_2S, /*< level volume in 2 seconds */ + DRX_AUD_AVC_DECAYTIME_20MS/*< level volume in 20 millisec */ + }; + +/* +* /enum DRXAudMaxAVCGain_t +* /brief Automatic volume control max gain in audio baseband. +*/ + enum drx_aud_avc_max_gain { + DRX_AUD_AVC_MAX_GAIN_0DB, /*< maximum AVC gain 0 dB */ + DRX_AUD_AVC_MAX_GAIN_6DB, /*< maximum AVC gain 6 dB */ + DRX_AUD_AVC_MAX_GAIN_12DB /*< maximum AVC gain 12 dB */ + }; + +/* +* /enum DRXAudMaxAVCAtten_t +* /brief Automatic volume control max attenuation in audio baseband. +*/ + enum drx_aud_avc_max_atten { + DRX_AUD_AVC_MAX_ATTEN_12DB, + /*< maximum AVC attenuation 12 dB */ + DRX_AUD_AVC_MAX_ATTEN_18DB, + /*< maximum AVC attenuation 18 dB */ + DRX_AUD_AVC_MAX_ATTEN_24DB/*< maximum AVC attenuation 24 dB */ + }; +/* +* \struct struct drx_cfg_aud_volume * \brief Audio volume configuration. +*/ + struct drx_cfg_aud_volume { + bool mute; /*< mute overrides volume setting */ + s16 volume; /*< volume, range -114 to 12 dB */ + enum drx_aud_avc_mode avc_mode; /*< AVC auto volume control mode */ + u16 avc_ref_level; /*< AVC reference level */ + enum drx_aud_avc_max_gain avc_max_gain; + /*< AVC max gain selection */ + enum drx_aud_avc_max_atten avc_max_atten; + /*< AVC max attenuation selection */ + s16 strength_left; /*< quasi-peak, left speaker */ + s16 strength_right; /*< quasi-peak, right speaker */ + }; + +/* DRX_CFG_I2S_OUTPUT - struct drx_cfg_i2s_output - set/get */ +/* +* \enum enum drxi2s_mode * \brief I2S output mode. +*/ + enum drxi2s_mode { + DRX_I2S_MODE_MASTER, /*< I2S is in master mode */ + DRX_I2S_MODE_SLAVE /*< I2S is in slave mode */ + }; + +/* +* \enum enum drxi2s_word_length * \brief Width of I2S data. +*/ + enum drxi2s_word_length { + DRX_I2S_WORDLENGTH_32 = 0,/*< I2S data is 32 bit wide */ + DRX_I2S_WORDLENGTH_16 = 1 /*< I2S data is 16 bit wide */ + }; + +/* +* \enum enum drxi2s_format * \brief Data wordstrobe alignment for I2S. +*/ + enum drxi2s_format { + DRX_I2S_FORMAT_WS_WITH_DATA, + /*< I2S data and wordstrobe are aligned */ + DRX_I2S_FORMAT_WS_ADVANCED + /*< I2S data one cycle after wordstrobe */ + }; + +/* +* \enum enum drxi2s_polarity * \brief Polarity of I2S data. +*/ + enum drxi2s_polarity { + DRX_I2S_POLARITY_RIGHT,/*< wordstrobe - right high, left low */ + DRX_I2S_POLARITY_LEFT /*< wordstrobe - right low, left high */ + }; + +/* +* \struct struct drx_cfg_i2s_output * \brief I2S output configuration. +*/ + struct drx_cfg_i2s_output { + bool output_enable; /*< I2S output enable */ + u32 frequency; /*< range from 8000-48000 Hz */ + enum drxi2s_mode mode; /*< I2S mode, master or slave */ + enum drxi2s_word_length word_length; + /*< I2S wordlength, 16 or 32 bits */ + enum drxi2s_polarity polarity;/*< I2S wordstrobe polarity */ + enum drxi2s_format format; /*< I2S wordstrobe delay to data */ + }; + +/* ------------------------------expert interface-----------------------------*/ +/* +* /enum enum drx_aud_fm_deemphasis * setting for FM-Deemphasis in audio demodulator. +* +*/ + enum drx_aud_fm_deemphasis { + DRX_AUD_FM_DEEMPH_50US, + DRX_AUD_FM_DEEMPH_75US, + DRX_AUD_FM_DEEMPH_OFF + }; + +/* +* /enum DRXAudDeviation_t +* setting for deviation mode in audio demodulator. +* +*/ + enum drx_cfg_aud_deviation { + DRX_AUD_DEVIATION_NORMAL, + DRX_AUD_DEVIATION_HIGH + }; + +/* +* /enum enum drx_no_carrier_option * setting for carrier, mute/noise. +* +*/ + enum drx_no_carrier_option { + DRX_NO_CARRIER_MUTE, + DRX_NO_CARRIER_NOISE + }; + +/* +* \enum DRXAudAutoSound_t +* \brief Automatic Sound +*/ + enum drx_cfg_aud_auto_sound { + DRX_AUD_AUTO_SOUND_OFF = 0, + DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_ON, + DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_OFF + }; + +/* +* \enum DRXAudASSThres_t +* \brief Automatic Sound Select Thresholds +*/ + struct drx_cfg_aud_ass_thres { + u16 a2; /* A2 Threshold for ASS configuration */ + u16 btsc; /* BTSC Threshold for ASS configuration */ + u16 nicam; /* Nicam Threshold for ASS configuration */ + }; + +/* +* \struct struct drx_aud_carrier * \brief Carrier detection related parameters +*/ + struct drx_aud_carrier { + u16 thres; /* carrier detetcion threshold for primary carrier (A) */ + enum drx_no_carrier_option opt; /* Mute or noise at no carrier detection (A) */ + s32 shift; /* DC level of incoming signal (A) */ + s32 dco; /* frequency adjustment (A) */ + }; + +/* +* \struct struct drx_cfg_aud_carriers * \brief combining carrier A & B to one struct +*/ + struct drx_cfg_aud_carriers { + struct drx_aud_carrier a; + struct drx_aud_carrier b; + }; + +/* +* /enum enum drx_aud_i2s_src * Selection of audio source +*/ + enum drx_aud_i2s_src { + DRX_AUD_SRC_MONO, + DRX_AUD_SRC_STEREO_OR_AB, + DRX_AUD_SRC_STEREO_OR_A, + DRX_AUD_SRC_STEREO_OR_B}; + +/* +* \enum enum drx_aud_i2s_matrix * \brief Used for selecting I2S output. +*/ + enum drx_aud_i2s_matrix { + DRX_AUD_I2S_MATRIX_A_MONO, + /*< A sound only, stereo or mono */ + DRX_AUD_I2S_MATRIX_B_MONO, + /*< B sound only, stereo or mono */ + DRX_AUD_I2S_MATRIX_STEREO, + /*< A+B sound, transparent */ + DRX_AUD_I2S_MATRIX_MONO /*< A+B mixed to mono sum, (L+R)/2 */}; + +/* +* /enum enum drx_aud_fm_matrix * setting for FM-Matrix in audio demodulator. +* +*/ + enum drx_aud_fm_matrix { + DRX_AUD_FM_MATRIX_NO_MATRIX, + DRX_AUD_FM_MATRIX_GERMAN, + DRX_AUD_FM_MATRIX_KOREAN, + DRX_AUD_FM_MATRIX_SOUND_A, + DRX_AUD_FM_MATRIX_SOUND_B}; + +/* +* \struct DRXAudMatrices_t +* \brief Mixer settings +*/ +struct drx_cfg_aud_mixer { + enum drx_aud_i2s_src source_i2s; + enum drx_aud_i2s_matrix matrix_i2s; + enum drx_aud_fm_matrix matrix_fm; +}; + +/* +* \enum DRXI2SVidSync_t +* \brief Audio/video synchronization, interacts with I2S mode. +* AUTO_1 and AUTO_2 are for automatic video standard detection with preference +* for NTSC or Monochrome, because the frequencies are too close (59.94 & 60 Hz) +*/ + enum drx_cfg_aud_av_sync { + DRX_AUD_AVSYNC_OFF,/*< audio/video synchronization is off */ + DRX_AUD_AVSYNC_NTSC, + /*< it is an NTSC system */ + DRX_AUD_AVSYNC_MONOCHROME, + /*< it is a MONOCHROME system */ + DRX_AUD_AVSYNC_PAL_SECAM + /*< it is a PAL/SECAM system */}; + +/* +* \struct struct drx_cfg_aud_prescale * \brief Prescalers +*/ +struct drx_cfg_aud_prescale { + u16 fm_deviation; + s16 nicam_gain; +}; + +/* +* \struct struct drx_aud_beep * \brief Beep +*/ +struct drx_aud_beep { + s16 volume; /* dB */ + u16 frequency; /* Hz */ + bool mute; +}; + +/* +* \enum enum drx_aud_btsc_detect * \brief BTSC detetcion mode +*/ + enum drx_aud_btsc_detect { + DRX_BTSC_STEREO, + DRX_BTSC_MONO_AND_SAP}; + +/* +* \struct struct drx_aud_data * \brief Audio data structure +*/ +struct drx_aud_data { + /* audio storage */ + bool audio_is_active; + enum drx_aud_standard audio_standard; + struct drx_cfg_i2s_output i2sdata; + struct drx_cfg_aud_volume volume; + enum drx_cfg_aud_auto_sound auto_sound; + struct drx_cfg_aud_ass_thres ass_thresholds; + struct drx_cfg_aud_carriers carriers; + struct drx_cfg_aud_mixer mixer; + enum drx_cfg_aud_deviation deviation; + enum drx_cfg_aud_av_sync av_sync; + struct drx_cfg_aud_prescale prescale; + enum drx_aud_fm_deemphasis deemph; + enum drx_aud_btsc_detect btsc_detect; + /* rds */ + u16 rds_data_counter; + bool rds_data_present; +}; + +/* +* \enum enum drx_qam_lock_range * \brief QAM lock range mode +*/ + enum drx_qam_lock_range { + DRX_QAM_LOCKRANGE_NORMAL, + DRX_QAM_LOCKRANGE_EXTENDED}; + +/*============================================================================*/ +/*============================================================================*/ +/*== Data access structures ==================================================*/ +/*============================================================================*/ +/*============================================================================*/ + +/* Address on device */ + typedef u32 dr_xaddr_t, *pdr_xaddr_t; + +/* Protocol specific flags */ + typedef u32 dr_xflags_t, *pdr_xflags_t; + +/* Write block of data to device */ + typedef int(*drx_write_block_func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u16 datasize, /* size of data in bytes */ + u8 *data, /* data to send */ + u32 flags); + +/* Read block of data from device */ + typedef int(*drx_read_block_func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u16 datasize, /* size of data in bytes */ + u8 *data, /* receive buffer */ + u32 flags); + +/* Write 8-bits value to device */ + typedef int(*drx_write_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u8 data, /* data to send */ + u32 flags); + +/* Read 8-bits value to device */ + typedef int(*drx_read_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u8 *data, /* receive buffer */ + u32 flags); + +/* Read modify write 8-bits value to device */ + typedef int(*drx_read_modify_write_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 waddr, /* write address of register */ + u32 raddr, /* read address of register */ + u8 wdata, /* data to write */ + u8 *rdata); /* data to read */ + +/* Write 16-bits value to device */ + typedef int(*drx_write_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u16 data, /* data to send */ + u32 flags); + +/* Read 16-bits value to device */ + typedef int(*drx_read_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u16 *data, /* receive buffer */ + u32 flags); + +/* Read modify write 16-bits value to device */ + typedef int(*drx_read_modify_write_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 waddr, /* write address of register */ + u32 raddr, /* read address of register */ + u16 wdata, /* data to write */ + u16 *rdata); /* data to read */ + +/* Write 32-bits value to device */ + typedef int(*drx_write_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u32 data, /* data to send */ + u32 flags); + +/* Read 32-bits value to device */ + typedef int(*drx_read_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register/memory */ + u32 *data, /* receive buffer */ + u32 flags); + +/* Read modify write 32-bits value to device */ + typedef int(*drx_read_modify_write_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 waddr, /* write address of register */ + u32 raddr, /* read address of register */ + u32 wdata, /* data to write */ + u32 *rdata); /* data to read */ + +/* +* \struct struct drx_access_func * \brief Interface to an access protocol. +*/ +struct drx_access_func { + drx_write_block_func_t write_block_func; + drx_read_block_func_t read_block_func; + drx_write_reg8func_t write_reg8func; + drx_read_reg8func_t read_reg8func; + drx_read_modify_write_reg8func_t read_modify_write_reg8func; + drx_write_reg16func_t write_reg16func; + drx_read_reg16func_t read_reg16func; + drx_read_modify_write_reg16func_t read_modify_write_reg16func; + drx_write_reg32func_t write_reg32func; + drx_read_reg32func_t read_reg32func; + drx_read_modify_write_reg32func_t read_modify_write_reg32func; +}; + +/* Register address and data for register dump function */ +struct drx_reg_dump { + u32 address; + u32 data; +}; + +/*============================================================================*/ +/*============================================================================*/ +/*== Demod instance data structures ==========================================*/ +/*============================================================================*/ +/*============================================================================*/ + +/* +* \struct struct drx_common_attr * \brief Set of common attributes, shared by all DRX devices. +*/ + struct drx_common_attr { + /* Microcode (firmware) attributes */ + char *microcode_file; /*< microcode filename */ + bool verify_microcode; + /*< Use microcode verify or not. */ + struct drx_mc_version_rec mcversion; + /*< Version record of microcode from file */ + + /* Clocks and tuner attributes */ + s32 intermediate_freq; + /*< IF,if tuner instance not used. (kHz)*/ + s32 sys_clock_freq; + /*< Systemclock frequency. (kHz) */ + s32 osc_clock_freq; + /*< Oscillator clock frequency. (kHz) */ + s16 osc_clock_deviation; + /*< Oscillator clock deviation. (ppm) */ + bool mirror_freq_spect; + /*< Mirror IF frequency spectrum or not.*/ + + /* Initial MPEG output attributes */ + struct drx_cfg_mpeg_output mpeg_cfg; + /*< MPEG configuration */ + + bool is_opened; /*< if true instance is already opened. */ + + /* Channel scan */ + struct drx_scan_param *scan_param; + /*< scan parameters */ + u16 scan_freq_plan_index; + /*< next index in freq plan */ + s32 scan_next_frequency; + /*< next freq to scan */ + bool scan_ready; /*< scan ready flag */ + u32 scan_max_channels;/*< number of channels in freqplan */ + u32 scan_channels_scanned; + /*< number of channels scanned */ + /* Channel scan - inner loop: demod related */ + drx_scan_func_t scan_function; + /*< function to check channel */ + /* Channel scan - inner loop: SYSObj related */ + void *scan_context; /*< Context Pointer of SYSObj */ + /* Channel scan - parameters for default DTV scan function in core driver */ + u16 scan_demod_lock_timeout; + /*< millisecs to wait for lock */ + enum drx_lock_status scan_desired_lock; + /*< lock requirement for channel found */ + /* scan_active can be used by SetChannel to decide how to program the tuner, + fast or slow (but stable). Usually fast during scan. */ + bool scan_active; /*< true when scan routines are active */ + + /* Power management */ + enum drx_power_mode current_power_mode; + /*< current power management mode */ + + /* Tuner */ + u8 tuner_port_nr; /*< nr of I2C port to which tuner is */ + s32 tuner_min_freq_rf; + /*< minimum RF input frequency, in kHz */ + s32 tuner_max_freq_rf; + /*< maximum RF input frequency, in kHz */ + bool tuner_rf_agc_pol; /*< if true invert RF AGC polarity */ + bool tuner_if_agc_pol; /*< if true invert IF AGC polarity */ + bool tuner_slow_mode; /*< if true invert IF AGC polarity */ + + struct drx_channel current_channel; + /*< current channel parameters */ + enum drx_standard current_standard; + /*< current standard selection */ + enum drx_standard prev_standard; + /*< previous standard selection */ + enum drx_standard di_cache_standard; + /*< standard in DI cache if available */ + bool use_bootloader; /*< use bootloader in open */ + u32 capabilities; /*< capabilities flags */ + u32 product_id; /*< product ID inc. metal fix number */}; + +/* +* Generic functions for DRX devices. +*/ + +struct drx_demod_instance; + +/* +* \struct struct drx_demod_instance * \brief Top structure of demodulator instance. +*/ +struct drx_demod_instance { + /*< data access protocol functions */ + struct i2c_device_addr *my_i2c_dev_addr; + /*< i2c address and device identifier */ + struct drx_common_attr *my_common_attr; + /*< common DRX attributes */ + void *my_ext_attr; /*< device specific attributes */ + /* generic demodulator data */ + + struct i2c_adapter *i2c; + const struct firmware *firmware; +}; + +/*------------------------------------------------------------------------- +MACROS +Conversion from enum values to human readable form. +-------------------------------------------------------------------------*/ + +/* standard */ + +#define DRX_STR_STANDARD(x) ( \ + (x == DRX_STANDARD_DVBT) ? "DVB-T" : \ + (x == DRX_STANDARD_8VSB) ? "8VSB" : \ + (x == DRX_STANDARD_NTSC) ? "NTSC" : \ + (x == DRX_STANDARD_PAL_SECAM_BG) ? "PAL/SECAM B/G" : \ + (x == DRX_STANDARD_PAL_SECAM_DK) ? "PAL/SECAM D/K" : \ + (x == DRX_STANDARD_PAL_SECAM_I) ? "PAL/SECAM I" : \ + (x == DRX_STANDARD_PAL_SECAM_L) ? "PAL/SECAM L" : \ + (x == DRX_STANDARD_PAL_SECAM_LP) ? "PAL/SECAM LP" : \ + (x == DRX_STANDARD_ITU_A) ? "ITU-A" : \ + (x == DRX_STANDARD_ITU_B) ? "ITU-B" : \ + (x == DRX_STANDARD_ITU_C) ? "ITU-C" : \ + (x == DRX_STANDARD_ITU_D) ? "ITU-D" : \ + (x == DRX_STANDARD_FM) ? "FM" : \ + (x == DRX_STANDARD_DTMB) ? "DTMB" : \ + (x == DRX_STANDARD_AUTO) ? "Auto" : \ + (x == DRX_STANDARD_UNKNOWN) ? "Unknown" : \ + "(Invalid)") + +/* channel */ + +#define DRX_STR_BANDWIDTH(x) ( \ + (x == DRX_BANDWIDTH_8MHZ) ? "8 MHz" : \ + (x == DRX_BANDWIDTH_7MHZ) ? "7 MHz" : \ + (x == DRX_BANDWIDTH_6MHZ) ? "6 MHz" : \ + (x == DRX_BANDWIDTH_AUTO) ? "Auto" : \ + (x == DRX_BANDWIDTH_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_FFTMODE(x) ( \ + (x == DRX_FFTMODE_2K) ? "2k" : \ + (x == DRX_FFTMODE_4K) ? "4k" : \ + (x == DRX_FFTMODE_8K) ? "8k" : \ + (x == DRX_FFTMODE_AUTO) ? "Auto" : \ + (x == DRX_FFTMODE_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_GUARD(x) ( \ + (x == DRX_GUARD_1DIV32) ? "1/32nd" : \ + (x == DRX_GUARD_1DIV16) ? "1/16th" : \ + (x == DRX_GUARD_1DIV8) ? "1/8th" : \ + (x == DRX_GUARD_1DIV4) ? "1/4th" : \ + (x == DRX_GUARD_AUTO) ? "Auto" : \ + (x == DRX_GUARD_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_CONSTELLATION(x) ( \ + (x == DRX_CONSTELLATION_BPSK) ? "BPSK" : \ + (x == DRX_CONSTELLATION_QPSK) ? "QPSK" : \ + (x == DRX_CONSTELLATION_PSK8) ? "PSK8" : \ + (x == DRX_CONSTELLATION_QAM16) ? "QAM16" : \ + (x == DRX_CONSTELLATION_QAM32) ? "QAM32" : \ + (x == DRX_CONSTELLATION_QAM64) ? "QAM64" : \ + (x == DRX_CONSTELLATION_QAM128) ? "QAM128" : \ + (x == DRX_CONSTELLATION_QAM256) ? "QAM256" : \ + (x == DRX_CONSTELLATION_QAM512) ? "QAM512" : \ + (x == DRX_CONSTELLATION_QAM1024) ? "QAM1024" : \ + (x == DRX_CONSTELLATION_QPSK_NR) ? "QPSK_NR" : \ + (x == DRX_CONSTELLATION_AUTO) ? "Auto" : \ + (x == DRX_CONSTELLATION_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_CODERATE(x) ( \ + (x == DRX_CODERATE_1DIV2) ? "1/2nd" : \ + (x == DRX_CODERATE_2DIV3) ? "2/3rd" : \ + (x == DRX_CODERATE_3DIV4) ? "3/4th" : \ + (x == DRX_CODERATE_5DIV6) ? "5/6th" : \ + (x == DRX_CODERATE_7DIV8) ? "7/8th" : \ + (x == DRX_CODERATE_AUTO) ? "Auto" : \ + (x == DRX_CODERATE_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_HIERARCHY(x) ( \ + (x == DRX_HIERARCHY_NONE) ? "None" : \ + (x == DRX_HIERARCHY_ALPHA1) ? "Alpha=1" : \ + (x == DRX_HIERARCHY_ALPHA2) ? "Alpha=2" : \ + (x == DRX_HIERARCHY_ALPHA4) ? "Alpha=4" : \ + (x == DRX_HIERARCHY_AUTO) ? "Auto" : \ + (x == DRX_HIERARCHY_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_PRIORITY(x) ( \ + (x == DRX_PRIORITY_LOW) ? "Low" : \ + (x == DRX_PRIORITY_HIGH) ? "High" : \ + (x == DRX_PRIORITY_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_MIRROR(x) ( \ + (x == DRX_MIRROR_NO) ? "Normal" : \ + (x == DRX_MIRROR_YES) ? "Mirrored" : \ + (x == DRX_MIRROR_AUTO) ? "Auto" : \ + (x == DRX_MIRROR_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_CLASSIFICATION(x) ( \ + (x == DRX_CLASSIFICATION_GAUSS) ? "Gaussion" : \ + (x == DRX_CLASSIFICATION_HVY_GAUSS) ? "Heavy Gaussion" : \ + (x == DRX_CLASSIFICATION_COCHANNEL) ? "Co-channel" : \ + (x == DRX_CLASSIFICATION_STATIC) ? "Static echo" : \ + (x == DRX_CLASSIFICATION_MOVING) ? "Moving echo" : \ + (x == DRX_CLASSIFICATION_ZERODB) ? "Zero dB echo" : \ + (x == DRX_CLASSIFICATION_UNKNOWN) ? "Unknown" : \ + (x == DRX_CLASSIFICATION_AUTO) ? "Auto" : \ + "(Invalid)") + +#define DRX_STR_INTERLEAVEMODE(x) ( \ + (x == DRX_INTERLEAVEMODE_I128_J1) ? "I128_J1" : \ + (x == DRX_INTERLEAVEMODE_I128_J1_V2) ? "I128_J1_V2" : \ + (x == DRX_INTERLEAVEMODE_I128_J2) ? "I128_J2" : \ + (x == DRX_INTERLEAVEMODE_I64_J2) ? "I64_J2" : \ + (x == DRX_INTERLEAVEMODE_I128_J3) ? "I128_J3" : \ + (x == DRX_INTERLEAVEMODE_I32_J4) ? "I32_J4" : \ + (x == DRX_INTERLEAVEMODE_I128_J4) ? "I128_J4" : \ + (x == DRX_INTERLEAVEMODE_I16_J8) ? "I16_J8" : \ + (x == DRX_INTERLEAVEMODE_I128_J5) ? "I128_J5" : \ + (x == DRX_INTERLEAVEMODE_I8_J16) ? "I8_J16" : \ + (x == DRX_INTERLEAVEMODE_I128_J6) ? "I128_J6" : \ + (x == DRX_INTERLEAVEMODE_RESERVED_11) ? "Reserved 11" : \ + (x == DRX_INTERLEAVEMODE_I128_J7) ? "I128_J7" : \ + (x == DRX_INTERLEAVEMODE_RESERVED_13) ? "Reserved 13" : \ + (x == DRX_INTERLEAVEMODE_I128_J8) ? "I128_J8" : \ + (x == DRX_INTERLEAVEMODE_RESERVED_15) ? "Reserved 15" : \ + (x == DRX_INTERLEAVEMODE_I12_J17) ? "I12_J17" : \ + (x == DRX_INTERLEAVEMODE_I5_J4) ? "I5_J4" : \ + (x == DRX_INTERLEAVEMODE_B52_M240) ? "B52_M240" : \ + (x == DRX_INTERLEAVEMODE_B52_M720) ? "B52_M720" : \ + (x == DRX_INTERLEAVEMODE_B52_M48) ? "B52_M48" : \ + (x == DRX_INTERLEAVEMODE_B52_M0) ? "B52_M0" : \ + (x == DRX_INTERLEAVEMODE_UNKNOWN) ? "Unknown" : \ + (x == DRX_INTERLEAVEMODE_AUTO) ? "Auto" : \ + "(Invalid)") + +#define DRX_STR_LDPC(x) ( \ + (x == DRX_LDPC_0_4) ? "0.4" : \ + (x == DRX_LDPC_0_6) ? "0.6" : \ + (x == DRX_LDPC_0_8) ? "0.8" : \ + (x == DRX_LDPC_AUTO) ? "Auto" : \ + (x == DRX_LDPC_UNKNOWN) ? "Unknown" : \ + "(Invalid)") + +#define DRX_STR_CARRIER(x) ( \ + (x == DRX_CARRIER_MULTI) ? "Multi" : \ + (x == DRX_CARRIER_SINGLE) ? "Single" : \ + (x == DRX_CARRIER_AUTO) ? "Auto" : \ + (x == DRX_CARRIER_UNKNOWN) ? "Unknown" : \ + "(Invalid)") + +#define DRX_STR_FRAMEMODE(x) ( \ + (x == DRX_FRAMEMODE_420) ? "420" : \ + (x == DRX_FRAMEMODE_595) ? "595" : \ + (x == DRX_FRAMEMODE_945) ? "945" : \ + (x == DRX_FRAMEMODE_420_FIXED_PN) ? "420 with fixed PN" : \ + (x == DRX_FRAMEMODE_945_FIXED_PN) ? "945 with fixed PN" : \ + (x == DRX_FRAMEMODE_AUTO) ? "Auto" : \ + (x == DRX_FRAMEMODE_UNKNOWN) ? "Unknown" : \ + "(Invalid)") + +#define DRX_STR_PILOT(x) ( \ + (x == DRX_PILOT_ON) ? "On" : \ + (x == DRX_PILOT_OFF) ? "Off" : \ + (x == DRX_PILOT_AUTO) ? "Auto" : \ + (x == DRX_PILOT_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +/* TPS */ + +#define DRX_STR_TPS_FRAME(x) ( \ + (x == DRX_TPS_FRAME1) ? "Frame1" : \ + (x == DRX_TPS_FRAME2) ? "Frame2" : \ + (x == DRX_TPS_FRAME3) ? "Frame3" : \ + (x == DRX_TPS_FRAME4) ? "Frame4" : \ + (x == DRX_TPS_FRAME_UNKNOWN) ? "Unknown" : \ + "(Invalid)") + +/* lock status */ + +#define DRX_STR_LOCKSTATUS(x) ( \ + (x == DRX_NEVER_LOCK) ? "Never" : \ + (x == DRX_NOT_LOCKED) ? "No" : \ + (x == DRX_LOCKED) ? "Locked" : \ + (x == DRX_LOCK_STATE_1) ? "Lock state 1" : \ + (x == DRX_LOCK_STATE_2) ? "Lock state 2" : \ + (x == DRX_LOCK_STATE_3) ? "Lock state 3" : \ + (x == DRX_LOCK_STATE_4) ? "Lock state 4" : \ + (x == DRX_LOCK_STATE_5) ? "Lock state 5" : \ + (x == DRX_LOCK_STATE_6) ? "Lock state 6" : \ + (x == DRX_LOCK_STATE_7) ? "Lock state 7" : \ + (x == DRX_LOCK_STATE_8) ? "Lock state 8" : \ + (x == DRX_LOCK_STATE_9) ? "Lock state 9" : \ + "(Invalid)") + +/* version information , modules */ +#define DRX_STR_MODULE(x) ( \ + (x == DRX_MODULE_DEVICE) ? "Device" : \ + (x == DRX_MODULE_MICROCODE) ? "Microcode" : \ + (x == DRX_MODULE_DRIVERCORE) ? "CoreDriver" : \ + (x == DRX_MODULE_DEVICEDRIVER) ? "DeviceDriver" : \ + (x == DRX_MODULE_BSP_I2C) ? "BSP I2C" : \ + (x == DRX_MODULE_BSP_TUNER) ? "BSP Tuner" : \ + (x == DRX_MODULE_BSP_HOST) ? "BSP Host" : \ + (x == DRX_MODULE_DAP) ? "Data Access Protocol" : \ + (x == DRX_MODULE_UNKNOWN) ? "Unknown" : \ + "(Invalid)") + +#define DRX_STR_POWER_MODE(x) ( \ + (x == DRX_POWER_UP) ? "DRX_POWER_UP " : \ + (x == DRX_POWER_MODE_1) ? "DRX_POWER_MODE_1" : \ + (x == DRX_POWER_MODE_2) ? "DRX_POWER_MODE_2" : \ + (x == DRX_POWER_MODE_3) ? "DRX_POWER_MODE_3" : \ + (x == DRX_POWER_MODE_4) ? "DRX_POWER_MODE_4" : \ + (x == DRX_POWER_MODE_5) ? "DRX_POWER_MODE_5" : \ + (x == DRX_POWER_MODE_6) ? "DRX_POWER_MODE_6" : \ + (x == DRX_POWER_MODE_7) ? "DRX_POWER_MODE_7" : \ + (x == DRX_POWER_MODE_8) ? "DRX_POWER_MODE_8" : \ + (x == DRX_POWER_MODE_9) ? "DRX_POWER_MODE_9" : \ + (x == DRX_POWER_MODE_10) ? "DRX_POWER_MODE_10" : \ + (x == DRX_POWER_MODE_11) ? "DRX_POWER_MODE_11" : \ + (x == DRX_POWER_MODE_12) ? "DRX_POWER_MODE_12" : \ + (x == DRX_POWER_MODE_13) ? "DRX_POWER_MODE_13" : \ + (x == DRX_POWER_MODE_14) ? "DRX_POWER_MODE_14" : \ + (x == DRX_POWER_MODE_15) ? "DRX_POWER_MODE_15" : \ + (x == DRX_POWER_MODE_16) ? "DRX_POWER_MODE_16" : \ + (x == DRX_POWER_DOWN) ? "DRX_POWER_DOWN " : \ + "(Invalid)") + +#define DRX_STR_OOB_STANDARD(x) ( \ + (x == DRX_OOB_MODE_A) ? "ANSI 55-1 " : \ + (x == DRX_OOB_MODE_B_GRADE_A) ? "ANSI 55-2 A" : \ + (x == DRX_OOB_MODE_B_GRADE_B) ? "ANSI 55-2 B" : \ + "(Invalid)") + +#define DRX_STR_AUD_STANDARD(x) ( \ + (x == DRX_AUD_STANDARD_BTSC) ? "BTSC" : \ + (x == DRX_AUD_STANDARD_A2) ? "A2" : \ + (x == DRX_AUD_STANDARD_EIAJ) ? "EIAJ" : \ + (x == DRX_AUD_STANDARD_FM_STEREO) ? "FM Stereo" : \ + (x == DRX_AUD_STANDARD_AUTO) ? "Auto" : \ + (x == DRX_AUD_STANDARD_M_MONO) ? "M-Standard Mono" : \ + (x == DRX_AUD_STANDARD_D_K_MONO) ? "D/K Mono FM" : \ + (x == DRX_AUD_STANDARD_BG_FM) ? "B/G-Dual Carrier FM (A2)" : \ + (x == DRX_AUD_STANDARD_D_K1) ? "D/K1-Dual Carrier FM" : \ + (x == DRX_AUD_STANDARD_D_K2) ? "D/K2-Dual Carrier FM" : \ + (x == DRX_AUD_STANDARD_D_K3) ? "D/K3-Dual Carrier FM" : \ + (x == DRX_AUD_STANDARD_BG_NICAM_FM) ? "B/G-NICAM-FM" : \ + (x == DRX_AUD_STANDARD_L_NICAM_AM) ? "L-NICAM-AM" : \ + (x == DRX_AUD_STANDARD_I_NICAM_FM) ? "I-NICAM-FM" : \ + (x == DRX_AUD_STANDARD_D_K_NICAM_FM) ? "D/K-NICAM-FM" : \ + (x == DRX_AUD_STANDARD_UNKNOWN) ? "Unknown" : \ + "(Invalid)") +#define DRX_STR_AUD_STEREO(x) ( \ + (x == true) ? "Stereo" : \ + (x == false) ? "Mono" : \ + "(Invalid)") + +#define DRX_STR_AUD_SAP(x) ( \ + (x == true) ? "Present" : \ + (x == false) ? "Not present" : \ + "(Invalid)") + +#define DRX_STR_AUD_CARRIER(x) ( \ + (x == true) ? "Present" : \ + (x == false) ? "Not present" : \ + "(Invalid)") + +#define DRX_STR_AUD_RDS(x) ( \ + (x == true) ? "Available" : \ + (x == false) ? "Not Available" : \ + "(Invalid)") + +#define DRX_STR_AUD_NICAM_STATUS(x) ( \ + (x == DRX_AUD_NICAM_DETECTED) ? "Detected" : \ + (x == DRX_AUD_NICAM_NOT_DETECTED) ? "Not detected" : \ + (x == DRX_AUD_NICAM_BAD) ? "Bad" : \ + "(Invalid)") + +#define DRX_STR_RDS_VALID(x) ( \ + (x == true) ? "Valid" : \ + (x == false) ? "Not Valid" : \ + "(Invalid)") + +/*------------------------------------------------------------------------- +Access macros +-------------------------------------------------------------------------*/ + +/* +* \brief Create a compilable reference to the microcode attribute +* \param d pointer to demod instance +* +* Used as main reference to an attribute field. +* Used by both macro implementation and function implementation. +* These macros are defined to avoid duplication of code in macro and function +* definitions that handle access of demod common or extended attributes. +* +*/ + +#define DRX_ATTR_MCRECORD(d) ((d)->my_common_attr->mcversion) +#define DRX_ATTR_MIRRORFREQSPECT(d) ((d)->my_common_attr->mirror_freq_spect) +#define DRX_ATTR_CURRENTPOWERMODE(d)((d)->my_common_attr->current_power_mode) +#define DRX_ATTR_ISOPENED(d) ((d)->my_common_attr->is_opened) +#define DRX_ATTR_USEBOOTLOADER(d) ((d)->my_common_attr->use_bootloader) +#define DRX_ATTR_CURRENTSTANDARD(d) ((d)->my_common_attr->current_standard) +#define DRX_ATTR_PREVSTANDARD(d) ((d)->my_common_attr->prev_standard) +#define DRX_ATTR_CACHESTANDARD(d) ((d)->my_common_attr->di_cache_standard) +#define DRX_ATTR_CURRENTCHANNEL(d) ((d)->my_common_attr->current_channel) +#define DRX_ATTR_MICROCODE(d) ((d)->my_common_attr->microcode) +#define DRX_ATTR_VERIFYMICROCODE(d) ((d)->my_common_attr->verify_microcode) +#define DRX_ATTR_CAPABILITIES(d) ((d)->my_common_attr->capabilities) +#define DRX_ATTR_PRODUCTID(d) ((d)->my_common_attr->product_id) +#define DRX_ATTR_INTERMEDIATEFREQ(d) ((d)->my_common_attr->intermediate_freq) +#define DRX_ATTR_SYSCLOCKFREQ(d) ((d)->my_common_attr->sys_clock_freq) +#define DRX_ATTR_TUNERRFAGCPOL(d) ((d)->my_common_attr->tuner_rf_agc_pol) +#define DRX_ATTR_TUNERIFAGCPOL(d) ((d)->my_common_attr->tuner_if_agc_pol) +#define DRX_ATTR_TUNERSLOWMODE(d) ((d)->my_common_attr->tuner_slow_mode) +#define DRX_ATTR_TUNERSPORTNR(d) ((d)->my_common_attr->tuner_port_nr) +#define DRX_ATTR_I2CADDR(d) ((d)->my_i2c_dev_addr->i2c_addr) +#define DRX_ATTR_I2CDEVID(d) ((d)->my_i2c_dev_addr->i2c_dev_id) +#define DRX_ISMCVERTYPE(x) ((x) == AUX_VER_RECORD) + +/*************************/ + +/* Macros with device-specific handling are converted to CFG functions */ + +#define DRX_ACCESSMACRO_SET(demod, value, cfg_name, data_type) \ + do { \ + struct drx_cfg config; \ + data_type cfg_data; \ + config.cfg_type = cfg_name; \ + config.cfg_data = &cfg_data; \ + cfg_data = value; \ + drx_ctrl(demod, DRX_CTRL_SET_CFG, &config); \ + } while (0) + +#define DRX_ACCESSMACRO_GET(demod, value, cfg_name, data_type, error_value) \ + do { \ + int cfg_status; \ + struct drx_cfg config; \ + data_type cfg_data; \ + config.cfg_type = cfg_name; \ + config.cfg_data = &cfg_data; \ + cfg_status = drx_ctrl(demod, DRX_CTRL_GET_CFG, &config); \ + if (cfg_status == 0) { \ + value = cfg_data; \ + } else { \ + value = (data_type)error_value; \ + } \ + } while (0) + +/* Configuration functions for usage by Access (XS) Macros */ + +#ifndef DRX_XS_CFG_BASE +#define DRX_XS_CFG_BASE (500) +#endif + +#define DRX_XS_CFG_PRESET (DRX_XS_CFG_BASE + 0) +#define DRX_XS_CFG_AUD_BTSC_DETECT (DRX_XS_CFG_BASE + 1) +#define DRX_XS_CFG_QAM_LOCKRANGE (DRX_XS_CFG_BASE + 2) + +/* Access Macros with device-specific handling */ + +#define DRX_SET_PRESET(d, x) \ + DRX_ACCESSMACRO_SET((d), (x), DRX_XS_CFG_PRESET, char*) +#define DRX_GET_PRESET(d, x) \ + DRX_ACCESSMACRO_GET((d), (x), DRX_XS_CFG_PRESET, char*, "ERROR") + +#define DRX_SET_AUD_BTSC_DETECT(d, x) DRX_ACCESSMACRO_SET((d), (x), \ + DRX_XS_CFG_AUD_BTSC_DETECT, enum drx_aud_btsc_detect) +#define DRX_GET_AUD_BTSC_DETECT(d, x) DRX_ACCESSMACRO_GET((d), (x), \ + DRX_XS_CFG_AUD_BTSC_DETECT, enum drx_aud_btsc_detect, DRX_UNKNOWN) + +#define DRX_SET_QAM_LOCKRANGE(d, x) DRX_ACCESSMACRO_SET((d), (x), \ + DRX_XS_CFG_QAM_LOCKRANGE, enum drx_qam_lock_range) +#define DRX_GET_QAM_LOCKRANGE(d, x) DRX_ACCESSMACRO_GET((d), (x), \ + DRX_XS_CFG_QAM_LOCKRANGE, enum drx_qam_lock_range, DRX_UNKNOWN) + +/* +* \brief Macro to check if std is an ATV standard +* \retval true std is an ATV standard +* \retval false std is an ATV standard +*/ +#define DRX_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)) + +/* +* \brief Macro to check if std is an QAM standard +* \retval true std is an QAM standards +* \retval false std is an QAM standards +*/ +#define DRX_ISQAMSTD(std) (((std) == DRX_STANDARD_ITU_A) || \ + ((std) == DRX_STANDARD_ITU_B) || \ + ((std) == DRX_STANDARD_ITU_C) || \ + ((std) == DRX_STANDARD_ITU_D)) + +/* +* \brief Macro to check if std is VSB standard +* \retval true std is VSB standard +* \retval false std is not VSB standard +*/ +#define DRX_ISVSBSTD(std) ((std) == DRX_STANDARD_8VSB) + +/* +* \brief Macro to check if std is DVBT standard +* \retval true std is DVBT standard +* \retval false std is not DVBT standard +*/ +#define DRX_ISDVBTSTD(std) ((std) == DRX_STANDARD_DVBT) + +/*------------------------------------------------------------------------- +THE END +-------------------------------------------------------------------------*/ +#endif /* __DRXDRIVER_H__ */ diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h new file mode 100644 index 0000000000..ff05a4ffb1 --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h @@ -0,0 +1,72 @@ +/* + ******************************************************************************* + * WARNING - THIS FILE HAS BEEN GENERATED - DO NOT CHANGE + * + * Filename: drx_driver_version.h + * Generated on: Mon Jan 18 12:09:23 2010 + * Generated by: IDF:x 1.3.0 + * Generated from: ../../../device/drxj/version + * Output start: [entry point] + * + * filename last modified re-use + * + 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. +*/ + +/* ----------------------------------------------------- + * version.idf Mon Jan 18 11:56:10 2010 - + * + */ + +#ifndef __DRX_DRIVER_VERSION__H__ +#define __DRX_DRIVER_VERSION__H__ INCLUDED + +#ifdef _REGISTERTABLE_ +#include <registertable.h> + extern register_table_t drx_driver_version[]; + extern register_table_info_t drx_driver_version_info[]; +#endif /* _REGISTERTABLE_ */ + +/* + *============================================================================== + * VERSION + * version@/var/cvs/projects/drxj.cvsroot/hostcode/drxdriver/device/drxj + *============================================================================== + */ + +#define VERSION__A 0x0 +#define VERSION_MAJOR 1 +#define VERSION_MINOR 0 +#define VERSION_PATCH 56 + +#endif /* __DRX_DRIVER_VERSION__H__ */ +/* + * End of file (drx_driver_version.h) + ******************************************************************************* + */ diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c new file mode 100644 index 0000000000..a738573c8c --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -0,0 +1,12404 @@ +/* + 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 <media/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 address 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 Endianness 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 addresses directly. This must be READ ONLY to avoid problems. */ +/* Writing to the interface addresses are 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 configuration */ + 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 configuration */ + 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 output enable */ + false, /* flag SIF output 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 output */ + 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 omitted, they require no user input and + are initially 0, NULL or false. The compiler will initialize them to these + values when omitted. */ + 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 which 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 division: 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). +* +*/ +#if 0 +/* Currently, unused as we lack support for analog TV */ +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 +}; +#endif + +/*============================================================================*/ +/*== 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... + Address must be rewritten 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 Success +* \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 Success +* \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 Success +* \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 Success +* \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++) { + rc = drxj_dap_read_reg16(dev_addr, + (DRXJ_HI_ATOMIC_BUF_START + i), + &word, 0); + if (rc) { + pr_err("error %d\n", rc); + goto rw_error; + } + 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; + 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; + } + + /* 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 omit 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) { + rc = -ETIMEDOUT; + 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 in case 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; + fallthrough; /* as 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; + } /* switch (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 parallel -> 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 parallel -> 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) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + /* Enable Monitor Bus output over MPEG pads and ctl input */ + rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MON_CFG__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Write nomagic word to enable pdr reg write */ + rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else { + /* 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 inputmode */ + rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MSTRT_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_MERR_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_MCLK_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_MVAL_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_MD0_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_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) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Enable Monitor Bus output over MPEG pads and ctl input */ + rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MON_CFG__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Write nomagic word to enable pdr reg write */ + rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* save values for restore after re-acquire */ + common_attr->mpeg_cfg.enable_mpeg_output = cfg_data->enable_mpeg_output; + + return 0; +rw_error: + return rc; +} + +/*----------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------*/ +/* MPEG Output Configuration Functions - end */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* miscellaneous configurations - begin */ +/*----------------------------------------------------------------------------*/ + +/* +* \fn int set_mpegtei_handling() +* \brief Activate MPEG TEI handling settings. +* \param devmod Pointer to demodulator instance. +* \return int. +* +* This routine should be called during a set channel of QAM/VSB +* +*/ +static int set_mpegtei_handling(struct drx_demod_instance *demod) +{ + struct drxj_data *ext_attr = (struct drxj_data *) (NULL); + struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); + int rc; + u16 fec_oc_dpr_mode = 0; + u16 fec_oc_snc_mode = 0; + u16 fec_oc_ems_mode = 0; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_DPR_MODE__A, &fec_oc_dpr_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_EMS_MODE__A, &fec_oc_ems_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* reset to default, allow TEI bit to be changed */ + fec_oc_dpr_mode &= (~FEC_OC_DPR_MODE_ERR_DISABLE__M); + fec_oc_snc_mode &= (~(FEC_OC_SNC_MODE_ERROR_CTL__M | + FEC_OC_SNC_MODE_CORR_DISABLE__M)); + fec_oc_ems_mode &= (~FEC_OC_EMS_MODE_MODE__M); + + if (ext_attr->disable_te_ihandling) { + /* do not change TEI bit */ + fec_oc_dpr_mode |= FEC_OC_DPR_MODE_ERR_DISABLE__M; + fec_oc_snc_mode |= FEC_OC_SNC_MODE_CORR_DISABLE__M | + ((0x2) << (FEC_OC_SNC_MODE_ERROR_CTL__B)); + fec_oc_ems_mode |= ((0x01) << (FEC_OC_EMS_MODE_MODE__B)); + } + + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DPR_MODE__A, fec_oc_dpr_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_MODE__A, fec_oc_snc_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_EMS_MODE__A, fec_oc_ems_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*----------------------------------------------------------------------------*/ +/* +* \fn int bit_reverse_mpeg_output() +* \brief Set MPEG output bit-endian settings. +* \param devmod Pointer to demodulator instance. +* \return int. +* +* This routine should be called during a set channel of QAM/VSB +* +*/ +static int bit_reverse_mpeg_output(struct drx_demod_instance *demod) +{ + struct drxj_data *ext_attr = (struct drxj_data *) (NULL); + struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); + int rc; + u16 fec_oc_ipr_mode = 0; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_IPR_MODE__A, &fec_oc_ipr_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* reset to default (normal bit order) */ + fec_oc_ipr_mode &= (~FEC_OC_IPR_MODE_REVERSE_ORDER__M); + + if (ext_attr->bit_reverse_mpeg_outout) + fec_oc_ipr_mode |= FEC_OC_IPR_MODE_REVERSE_ORDER__M; + + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_MODE__A, fec_oc_ipr_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*----------------------------------------------------------------------------*/ +/* +* \fn int set_mpeg_start_width() +* \brief Set MPEG start width. +* \param devmod Pointer to demodulator instance. +* \return int. +* +* This routine should be called during a set channel of QAM/VSB +* +*/ +static int set_mpeg_start_width(struct drx_demod_instance *demod) +{ + struct drxj_data *ext_attr = (struct drxj_data *) (NULL); + struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); + struct drx_common_attr *common_attr = (struct drx_common_attr *) NULL; + int rc; + u16 fec_oc_comm_mb = 0; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + common_attr = demod->my_common_attr; + + if ((common_attr->mpeg_cfg.static_clk == true) + && (common_attr->mpeg_cfg.enable_parallel == false)) { + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_COMM_MB__A, &fec_oc_comm_mb, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fec_oc_comm_mb &= ~FEC_OC_COMM_MB_CTL_ON; + if (ext_attr->mpeg_start_width == DRXJ_MPEG_START_WIDTH_8CLKCYC) + fec_oc_comm_mb |= FEC_OC_COMM_MB_CTL_ON; + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_COMM_MB__A, fec_oc_comm_mb, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + return 0; +rw_error: + return rc; +} + +/*----------------------------------------------------------------------------*/ +/* miscellaneous configurations - end */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* UIO Configuration Functions - begin */ +/*----------------------------------------------------------------------------*/ +/* +* \fn int ctrl_set_uio_cfg() +* \brief Configure modus oprandi UIO. +* \param demod Pointer to demodulator instance. +* \param uio_cfg Pointer to a configuration setting for a certain UIO. +* \return int. +*/ +static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg *uio_cfg) +{ + struct drxj_data *ext_attr = (struct drxj_data *) (NULL); + int rc; + + if ((uio_cfg == NULL) || (demod == NULL)) + return -EINVAL; + + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + /* Write magic word to enable pdr reg write */ + rc = drxj_dap_write_reg16(demod->my_i2c_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; + } + switch (uio_cfg->uio) { + /*====================================================================*/ + case DRX_UIO1: + /* DRX_UIO1: SMA_TX UIO-1 */ + if (!ext_attr->has_smatx) + return -EIO; + switch (uio_cfg->mode) { + case DRX_UIO_MODE_FIRMWARE_SMA: + case DRX_UIO_MODE_FIRMWARE_SAW: + case DRX_UIO_MODE_READWRITE: + ext_attr->uio_sma_tx_mode = uio_cfg->mode; + break; + case DRX_UIO_MODE_DISABLE: + ext_attr->uio_sma_tx_mode = uio_cfg->mode; + /* pad configuration register is set 0 - input mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EINVAL; + } /* switch ( uio_cfg->mode ) */ + break; + /*====================================================================*/ + case DRX_UIO2: + /* DRX_UIO2: SMA_RX UIO-2 */ + if (!ext_attr->has_smarx) + return -EIO; + switch (uio_cfg->mode) { + case DRX_UIO_MODE_FIRMWARE0: + case DRX_UIO_MODE_READWRITE: + ext_attr->uio_sma_rx_mode = uio_cfg->mode; + break; + case DRX_UIO_MODE_DISABLE: + ext_attr->uio_sma_rx_mode = uio_cfg->mode; + /* pad configuration register is set 0 - input mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_RX_CFG__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EINVAL; + } /* switch ( uio_cfg->mode ) */ + break; + /*====================================================================*/ + case DRX_UIO3: + /* DRX_UIO3: GPIO UIO-3 */ + if (!ext_attr->has_gpio) + return -EIO; + switch (uio_cfg->mode) { + case DRX_UIO_MODE_FIRMWARE0: + case DRX_UIO_MODE_READWRITE: + ext_attr->uio_gpio_mode = uio_cfg->mode; + break; + case DRX_UIO_MODE_DISABLE: + ext_attr->uio_gpio_mode = uio_cfg->mode; + /* pad configuration register is set 0 - input mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_GPIO_CFG__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EINVAL; + } /* switch ( uio_cfg->mode ) */ + break; + /*====================================================================*/ + case DRX_UIO4: + /* DRX_UIO4: IRQN UIO-4 */ + if (!ext_attr->has_irqn) + return -EIO; + switch (uio_cfg->mode) { + case DRX_UIO_MODE_READWRITE: + ext_attr->uio_irqn_mode = uio_cfg->mode; + break; + case DRX_UIO_MODE_DISABLE: + /* pad configuration register is set 0 - input mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_IRQN_CFG__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->uio_irqn_mode = uio_cfg->mode; + break; + case DRX_UIO_MODE_FIRMWARE0: + default: + return -EINVAL; + } /* switch ( uio_cfg->mode ) */ + break; + /*====================================================================*/ + default: + return -EINVAL; + } /* switch ( uio_cfg->uio ) */ + + /* Write magic word to disable pdr reg write */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int ctrl_uio_write() +* \brief Write to a UIO. +* \param demod Pointer to demodulator instance. +* \param uio_data Pointer to data container for a certain UIO. +* \return int. +*/ +static int +ctrl_uio_write(struct drx_demod_instance *demod, struct drxuio_data *uio_data) +{ + struct drxj_data *ext_attr = (struct drxj_data *) (NULL); + int rc; + u16 pin_cfg_value = 0; + u16 value = 0; + + if ((uio_data == NULL) || (demod == NULL)) + return -EINVAL; + + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + /* Write magic word to enable pdr reg write */ + rc = drxj_dap_write_reg16(demod->my_i2c_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; + } + switch (uio_data->uio) { + /*====================================================================*/ + case DRX_UIO1: + /* DRX_UIO1: SMA_TX UIO-1 */ + if (!ext_attr->has_smatx) + return -EIO; + if ((ext_attr->uio_sma_tx_mode != DRX_UIO_MODE_READWRITE) + && (ext_attr->uio_sma_tx_mode != DRX_UIO_MODE_FIRMWARE_SAW)) { + return -EIO; + } + pin_cfg_value = 0; + /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ + pin_cfg_value |= 0x0113; + /* io_pad_cfg_mode output mode is drive always */ + /* io_pad_cfg_drive is set to power 2 (23 mA) */ + + /* write to io pad configuration register - output mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, pin_cfg_value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* use corresponding bit in io data output registar */ + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (!uio_data->value) + value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */ + else + value |= 0x8000; /* write one to 15th bit - 1st UIO */ + + /* write back to io data output register */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + /*======================================================================*/ + case DRX_UIO2: + /* DRX_UIO2: SMA_RX UIO-2 */ + if (!ext_attr->has_smarx) + return -EIO; + if (ext_attr->uio_sma_rx_mode != DRX_UIO_MODE_READWRITE) + return -EIO; + + pin_cfg_value = 0; + /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ + pin_cfg_value |= 0x0113; + /* io_pad_cfg_mode output mode is drive always */ + /* io_pad_cfg_drive is set to power 2 (23 mA) */ + + /* write to io pad configuration register - output mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_RX_CFG__A, pin_cfg_value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* use corresponding bit in io data output registar */ + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (!uio_data->value) + value &= 0xBFFF; /* write zero to 14th bit - 2nd UIO */ + else + value |= 0x4000; /* write one to 14th bit - 2nd UIO */ + + /* write back to io data output register */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + /*====================================================================*/ + case DRX_UIO3: + /* DRX_UIO3: ASEL UIO-3 */ + if (!ext_attr->has_gpio) + return -EIO; + if (ext_attr->uio_gpio_mode != DRX_UIO_MODE_READWRITE) + return -EIO; + + pin_cfg_value = 0; + /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ + pin_cfg_value |= 0x0113; + /* io_pad_cfg_mode output mode is drive always */ + /* io_pad_cfg_drive is set to power 2 (23 mA) */ + + /* write to io pad configuration register - output mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_GPIO_CFG__A, pin_cfg_value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* use corresponding bit in io data output registar */ + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_HI__A, &value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (!uio_data->value) + value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */ + else + value |= 0x0004; /* write one to 2nd bit - 3rd UIO */ + + /* write back to io data output register */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_HI__A, value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + /*=====================================================================*/ + case DRX_UIO4: + /* DRX_UIO4: IRQN UIO-4 */ + if (!ext_attr->has_irqn) + return -EIO; + + if (ext_attr->uio_irqn_mode != DRX_UIO_MODE_READWRITE) + return -EIO; + + pin_cfg_value = 0; + /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ + pin_cfg_value |= 0x0113; + /* io_pad_cfg_mode output mode is drive always */ + /* io_pad_cfg_drive is set to power 2 (23 mA) */ + + /* write to io pad configuration register - output mode */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_IRQN_CFG__A, pin_cfg_value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* use corresponding bit in io data output registar */ + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (uio_data->value == false) + value &= 0xEFFF; /* write zero to 12th bit - 4th UIO */ + else + value |= 0x1000; /* write one to 12th bit - 4th UIO */ + + /* write back to io data output register */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + /*=====================================================================*/ + default: + return -EINVAL; + } /* switch ( uio_data->uio ) */ + + /* Write magic word to disable pdr reg write */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*---------------------------------------------------------------------------*/ +/* UIO Configuration Functions - end */ +/*---------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* I2C Bridge Functions - begin */ +/*----------------------------------------------------------------------------*/ +/* +* \fn int ctrl_i2c_bridge() +* \brief Open or close the I2C switch to tuner. +* \param demod Pointer to demodulator instance. +* \param bridge_closed Pointer to bool indication if bridge is closed not. +* \return int. + +*/ +static int +ctrl_i2c_bridge(struct drx_demod_instance *demod, bool *bridge_closed) +{ + struct drxj_hi_cmd hi_cmd; + u16 result = 0; + + /* check arguments */ + if (bridge_closed == NULL) + return -EINVAL; + + hi_cmd.cmd = SIO_HI_RA_RAM_CMD_BRDCTRL; + hi_cmd.param1 = SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY; + if (*bridge_closed) + hi_cmd.param2 = SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED; + else + hi_cmd.param2 = SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN; + + return hi_command(demod->my_i2c_dev_addr, &hi_cmd, &result); +} + +/*----------------------------------------------------------------------------*/ +/* I2C Bridge Functions - end */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Smart antenna Functions - begin */ +/*----------------------------------------------------------------------------*/ +/* +* \fn int smart_ant_init() +* \brief Initialize Smart Antenna. +* \param pointer to struct drx_demod_instance. +* \return int. +* +*/ +static int smart_ant_init(struct drx_demod_instance *demod) +{ + struct drxj_data *ext_attr = NULL; + struct i2c_device_addr *dev_addr = NULL; + struct drxuio_cfg uio_cfg = { DRX_UIO1, DRX_UIO_MODE_FIRMWARE_SMA }; + int rc; + u16 data = 0; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + /* Write magic word to enable pdr reg write */ + rc = drxj_dap_write_reg16(demod->my_i2c_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; + } + /* init smart antenna */ + rc = drxj_dap_read_reg16(dev_addr, SIO_SA_TX_COMMAND__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (ext_attr->smart_ant_inverted) { + rc = drxj_dap_write_reg16(dev_addr, SIO_SA_TX_COMMAND__A, (data | SIO_SA_TX_COMMAND_TX_INVERT__M) | SIO_SA_TX_COMMAND_TX_ENABLE__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else { + rc = drxj_dap_write_reg16(dev_addr, SIO_SA_TX_COMMAND__A, (data & (~SIO_SA_TX_COMMAND_TX_INVERT__M)) | SIO_SA_TX_COMMAND_TX_ENABLE__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* config SMA_TX pin to smart antenna mode */ + rc = ctrl_set_uio_cfg(demod, &uio_cfg); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, 0x13, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_GPIO_FNC__A, 0x03, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Write magic word to disable pdr reg write */ + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +static int scu_command(struct i2c_device_addr *dev_addr, struct drxjscu_cmd *cmd) +{ + int rc; + u16 cur_cmd = 0; + unsigned long timeout; + + /* Check param */ + if (cmd == NULL) + return -EINVAL; + + /* Wait until SCU command interface is ready to receive command */ + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_COMMAND__A, &cur_cmd, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (cur_cmd != DRX_SCU_READY) + return -EIO; + + switch (cmd->parameter_len) { + case 5: + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_4__A, *(cmd->parameter + 4), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 4: + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_3__A, *(cmd->parameter + 3), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 3: + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_2__A, *(cmd->parameter + 2), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 2: + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_1__A, *(cmd->parameter + 1), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 1: + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_0__A, *(cmd->parameter + 0), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 0: + /* do nothing */ + break; + default: + /* this number of parameters is not supported */ + return -EIO; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_COMMAND__A, cmd->command, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Wait until SCU has processed command */ + timeout = jiffies + msecs_to_jiffies(DRXJ_MAX_WAITTIME); + while (time_is_after_jiffies(timeout)) { + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_COMMAND__A, &cur_cmd, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (cur_cmd == DRX_SCU_READY) + break; + usleep_range(1000, 2000); + } + + if (cur_cmd != DRX_SCU_READY) + return -EIO; + + /* read results */ + if ((cmd->result_len > 0) && (cmd->result != NULL)) { + s16 err; + + switch (cmd->result_len) { + case 4: + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_3__A, cmd->result + 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 3: + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_2__A, cmd->result + 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 2: + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_1__A, cmd->result + 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 1: + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_0__A, cmd->result + 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + fallthrough; + case 0: + /* do nothing */ + break; + default: + /* this number of parameters is not supported */ + return -EIO; + } + + /* Check if an error was reported by SCU */ + err = cmd->result[0]; + + /* check a few fixed error codes */ + if ((err == (s16) SCU_RAM_PARAM_0_RESULT_UNKSTD) + || (err == (s16) SCU_RAM_PARAM_0_RESULT_UNKCMD) + || (err == (s16) SCU_RAM_PARAM_0_RESULT_INVPAR) + || (err == (s16) SCU_RAM_PARAM_0_RESULT_SIZE) + ) { + return -EINVAL; + } + /* here it is assumed that negative means error, and positive no error */ + else if (err < 0) + return -EIO; + else + return 0; + } + + return 0; + +rw_error: + return rc; +} + +/* +* \fn int DRXJ_DAP_SCUAtomicReadWriteBlock() +* \brief Basic access routine for SCU 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 Success +* \retval -EIO Timeout, I2C error, illegal bank +* +*/ +#define ADDR_AT_SCU_SPACE(x) ((x - 0x82E000) * 2) +static +int drxj_dap_scu_atomic_read_write_block(struct i2c_device_addr *dev_addr, u32 addr, u16 datasize, /* max 30 bytes because the limit of SCU parameter */ + u8 *data, bool read_flag) +{ + struct drxjscu_cmd scu_cmd; + int rc; + u16 set_param_parameters[18]; + u16 cmd_result[15]; + + /* Parameter check */ + if (!data || !dev_addr || (datasize % 2) || ((datasize / 2) > 16)) + return -EINVAL; + + set_param_parameters[1] = (u16) ADDR_AT_SCU_SPACE(addr); + if (read_flag) { /* read */ + set_param_parameters[0] = ((~(0x0080)) & datasize); + scu_cmd.parameter_len = 2; + scu_cmd.result_len = datasize / 2 + 2; + } else { + int i = 0; + + set_param_parameters[0] = 0x0080 | datasize; + for (i = 0; i < (datasize / 2); i++) { + set_param_parameters[i + 2] = + (data[2 * i] | (data[(2 * i) + 1] << 8)); + } + scu_cmd.parameter_len = datasize / 2 + 2; + scu_cmd.result_len = 1; + } + + scu_cmd.command = + SCU_RAM_COMMAND_STANDARD_TOP | + SCU_RAM_COMMAND_CMD_AUX_SCU_ATOMIC_ACCESS; + scu_cmd.result = cmd_result; + scu_cmd.parameter = set_param_parameters; + rc = scu_command(dev_addr, &scu_cmd); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (read_flag) { + int i = 0; + /* read data from buffer */ + for (i = 0; i < (datasize / 2); i++) { + data[2 * i] = (u8) (scu_cmd.result[i + 2] & 0xFF); + data[(2 * i) + 1] = (u8) (scu_cmd.result[i + 2] >> 8); + } + } + + return 0; + +rw_error: + return rc; + +} + +/*============================================================================*/ + +/* +* \fn int DRXJ_DAP_AtomicReadReg16() +* \brief Atomic read of 16 bits words +*/ +static +int drxj_dap_scu_atomic_read_reg16(struct i2c_device_addr *dev_addr, + u32 addr, + u16 *data, u32 flags) +{ + u8 buf[2] = { 0 }; + int rc; + u16 word = 0; + + if (!data) + return -EINVAL; + + rc = drxj_dap_scu_atomic_read_write_block(dev_addr, addr, 2, buf, true); + if (rc < 0) + return rc; + + word = (u16) (buf[0] + (buf[1] << 8)); + + *data = word; + + return rc; +} + +/*============================================================================*/ +/* +* \fn int drxj_dap_scu_atomic_write_reg16() +* \brief Atomic read of 16 bits words +*/ +static +int drxj_dap_scu_atomic_write_reg16(struct i2c_device_addr *dev_addr, + u32 addr, + u16 data, u32 flags) +{ + u8 buf[2]; + int rc; + + buf[0] = (u8) (data & 0xff); + buf[1] = (u8) ((data >> 8) & 0xff); + + rc = drxj_dap_scu_atomic_read_write_block(dev_addr, addr, 2, buf, false); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* +* \brief Measure result of ADC synchronisation +* \param demod demod instance +* \param count (returned) count +* \return int. +* \retval 0 Success +* \retval -EIO Failure: I2C error +* +*/ +static int adc_sync_measurement(struct drx_demod_instance *demod, u16 *count) +{ + struct i2c_device_addr *dev_addr = NULL; + int rc; + u16 data = 0; + + dev_addr = demod->my_i2c_dev_addr; + + /* Start measurement */ + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_START_LOCK__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Wait at least 3*128*(1/sysclk) <<< 1 millisec */ + msleep(1); + + *count = 0; + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE0__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (data == 127) + *count = *count + 1; + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE1__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (data == 127) + *count = *count + 1; + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE2__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (data == 127) + *count = *count + 1; + + return 0; +rw_error: + return rc; +} + +/* +* \brief Synchronize analog and digital clock domains +* \param demod demod instance +* \return int. +* \retval 0 Success +* \retval -EIO Failure: I2C error or failure to synchronize +* +* An IQM reset will also reset the results of this synchronization. +* After an IQM reset this routine needs to be called again. +* +*/ + +static int adc_synchronization(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = NULL; + int rc; + u16 count = 0; + + dev_addr = demod->my_i2c_dev_addr; + + rc = adc_sync_measurement(demod, &count); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (count == 1) { + /* Try sampling on a different edge */ + u16 clk_neg = 0; + + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_CLKNEG__A, &clk_neg, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + clk_neg ^= IQM_AF_CLKNEG_CLKNEGDATA__M; + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLKNEG__A, clk_neg, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = adc_sync_measurement(demod, &count); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* TODO: implement fallback scenarios */ + if (count < 2) + return -EIO; + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ +/*== END AUXILIARY FUNCTIONS ==*/ +/*============================================================================*/ + +/*============================================================================*/ +/*============================================================================*/ +/*== 8VSB & QAM COMMON DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ +/*============================================================================*/ +/* +* \fn int init_agc () +* \brief Initialize AGC for all standards. +* \param demod instance of demodulator. +* \param channel pointer to channel data. +* \return int. +*/ +static int init_agc(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drx_common_attr *common_attr = NULL; + struct drxj_data *ext_attr = NULL; + struct drxj_cfg_agc *p_agc_rf_settings = NULL; + struct drxj_cfg_agc *p_agc_if_settings = NULL; + int rc; + u16 ingain_tgt_max = 0; + u16 clp_dir_to = 0; + u16 sns_sum_max = 0; + u16 clp_sum_max = 0; + u16 sns_dir_to = 0; + u16 ki_innergain_min = 0; + u16 agc_ki = 0; + u16 ki_max = 0; + u16 if_iaccu_hi_tgt_min = 0; + u16 data = 0; + u16 agc_ki_dgain = 0; + u16 ki_min = 0; + u16 clp_ctrl_mode = 0; + u16 agc_rf = 0; + u16 agc_if = 0; + + dev_addr = demod->my_i2c_dev_addr; + common_attr = (struct drx_common_attr *) demod->my_common_attr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + switch (ext_attr->standard) { + case DRX_STANDARD_8VSB: + clp_sum_max = 1023; + clp_dir_to = (u16) (-9); + sns_sum_max = 1023; + sns_dir_to = (u16) (-9); + ki_innergain_min = (u16) (-32768); + ki_max = 0x032C; + agc_ki_dgain = 0xC; + if_iaccu_hi_tgt_min = 2047; + ki_min = 0x0117; + ingain_tgt_max = 16383; + clp_ctrl_mode = 0; + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCCNT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_WD__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_STP__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCCNT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_WD__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_STP__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN__A, 1024, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_VSB_AGC_POW_TGT__A, 22600, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT__A, 13200, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + p_agc_if_settings = &(ext_attr->vsb_if_agc_cfg); + p_agc_rf_settings = &(ext_attr->vsb_rf_agc_cfg); + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_C: + case DRX_STANDARD_ITU_B: + ingain_tgt_max = 5119; + clp_sum_max = 1023; + clp_dir_to = (u16) (-5); + sns_sum_max = 127; + sns_dir_to = (u16) (-3); + ki_innergain_min = 0; + ki_max = 0x0657; + if_iaccu_hi_tgt_min = 2047; + agc_ki_dgain = 0x7; + ki_min = 0x0117; + clp_ctrl_mode = 0; + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCCNT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_WD__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_STP__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCCNT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_WD__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_STP__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + p_agc_if_settings = &(ext_attr->qam_if_agc_cfg); + p_agc_rf_settings = &(ext_attr->qam_rf_agc_cfg); + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT__A, p_agc_if_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_AGC_KI__A, &agc_ki, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + agc_ki &= 0xf000; + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI__A, agc_ki, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; +#endif + default: + return -EINVAL; + } + + /* for new AGC interface */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT_MIN__A, p_agc_if_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN__A, p_agc_if_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* Gain fed from inner to outer AGC */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingain_tgt_max, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, if_iaccu_hi_tgt_min, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_HI__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* set to p_agc_settings->top before */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_LO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_IACCU_HI__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_IACCU_LO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_MAX__A, 32767, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM_MAX__A, clp_sum_max, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM_MAX__A, sns_sum_max, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, ki_innergain_min, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_CYCLEN__A, 500, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCLEN__A, 500, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MIN__A, ki_min, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAX__A, ki_max, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_RED__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM_MIN__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCLEN__A, 500, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_TO__A, clp_dir_to, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM_MIN__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_TO__A, sns_dir_to, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CTRL_MODE__A, clp_ctrl_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + agc_rf = 0x800 + p_agc_rf_settings->cut_off_current; + if (common_attr->tuner_rf_agc_pol == true) + agc_rf = 0x87ff - agc_rf; + + agc_if = 0x800; + if (common_attr->tuner_if_agc_pol == true) + agc_rf = 0x87ff - agc_rf; + + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AGC_RF__A, agc_rf, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AGC_IF__A, agc_if, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Set/restore Ki DGAIN factor */ + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_DGAIN__M; + data |= (agc_ki_dgain << SCU_RAM_AGC_KI_DGAIN__B); + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_frequency () +* \brief Set frequency shift. +* \param demod instance of demodulator. +* \param channel pointer to channel data. +* \param tuner_freq_offset residual frequency from tuner. +* \return int. +*/ +static int +set_frequency(struct drx_demod_instance *demod, + struct drx_channel *channel, s32 tuner_freq_offset) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drxj_data *ext_attr = demod->my_ext_attr; + int rc; + s32 sampling_frequency = 0; + s32 frequency_shift = 0; + s32 if_freq_actual = 0; + s32 rf_freq_residual = -1 * tuner_freq_offset; + s32 adc_freq = 0; + s32 intermediate_freq = 0; + u32 iqm_fs_rate_ofs = 0; + bool adc_flip = true; + bool select_pos_image = false; + bool rf_mirror; + bool tuner_mirror; + bool image_to_select; + s32 fm_frequency_shift = 0; + + rf_mirror = (ext_attr->mirror == DRX_MIRROR_YES) ? true : false; + tuner_mirror = demod->my_common_attr->mirror_freq_spect ? false : true; + /* + Program frequency shifter + No need to account for mirroring on RF + */ + switch (ext_attr->standard) { + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_C: + case DRX_STANDARD_PAL_SECAM_LP: + case DRX_STANDARD_8VSB: + select_pos_image = true; + break; + case DRX_STANDARD_FM: + /* After IQM FS sound carrier must appear at 4 Mhz in spect. + Sound carrier is already 3Mhz above centre frequency due + to tuner setting so now add an extra shift of 1MHz... */ + fm_frequency_shift = 1000; + fallthrough; + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_NTSC: + case DRX_STANDARD_PAL_SECAM_BG: + case DRX_STANDARD_PAL_SECAM_DK: + case DRX_STANDARD_PAL_SECAM_I: + case DRX_STANDARD_PAL_SECAM_L: + select_pos_image = false; + break; + default: + return -EINVAL; + } + intermediate_freq = demod->my_common_attr->intermediate_freq; + sampling_frequency = demod->my_common_attr->sys_clock_freq / 3; + if (tuner_mirror) + if_freq_actual = intermediate_freq + rf_freq_residual + fm_frequency_shift; + else + if_freq_actual = intermediate_freq - rf_freq_residual - fm_frequency_shift; + if (if_freq_actual > sampling_frequency / 2) { + /* adc mirrors */ + adc_freq = sampling_frequency - if_freq_actual; + adc_flip = true; + } else { + /* adc doesn't mirror */ + adc_freq = if_freq_actual; + adc_flip = false; + } + + frequency_shift = adc_freq; + image_to_select = + (bool) (rf_mirror ^ tuner_mirror ^ adc_flip ^ select_pos_image); + iqm_fs_rate_ofs = frac28(frequency_shift, sampling_frequency); + + if (image_to_select) + iqm_fs_rate_ofs = ~iqm_fs_rate_ofs + 1; + + /* Program frequency shifter with tuner offset compensation */ + /* frequency_shift += tuner_freq_offset; TODO */ + rc = drxdap_fasi_write_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, iqm_fs_rate_ofs, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->iqm_fs_rate_ofs = iqm_fs_rate_ofs; + ext_attr->pos_image = (bool) (rf_mirror ^ tuner_mirror ^ select_pos_image); + + return 0; +rw_error: + return rc; +} + +/* +* \fn int get_acc_pkt_err() +* \brief Retrieve signal strength for VSB and QAM. +* \param demod Pointer to demod instance +* \param packet_err Pointer to packet error +* \return int. +* \retval 0 sig_strength contains valid data. +* \retval -EINVAL sig_strength is NULL. +* \retval -EIO Erroneous data, sig_strength contains invalid data. +*/ +#ifdef DRXJ_SIGNAL_ACCUM_ERR +static int get_acc_pkt_err(struct drx_demod_instance *demod, u16 *packet_err) +{ + int rc; + static u16 pkt_err; + static u16 last_pkt_err; + u16 data = 0; + struct drxj_data *ext_attr = NULL; + struct i2c_device_addr *dev_addr = NULL; + + ext_attr = (struct drxj_data *) demod->my_ext_attr; + dev_addr = demod->my_i2c_dev_addr; + + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (ext_attr->reset_pkt_err_acc) { + last_pkt_err = data; + pkt_err = 0; + ext_attr->reset_pkt_err_acc = false; + } + + if (data < last_pkt_err) { + pkt_err += 0xffff - last_pkt_err; + pkt_err += data; + } else { + pkt_err += (data - last_pkt_err); + } + *packet_err = pkt_err; + last_pkt_err = data; + + return 0; +rw_error: + return rc; +} +#endif + + +/*============================================================================*/ + +/* +* \fn int set_agc_rf () +* \brief Configure RF AGC +* \param demod instance of demodulator. +* \param agc_settings AGC configuration structure +* \return int. +*/ +static int +set_agc_rf(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, bool atomic) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + struct drxj_cfg_agc *p_agc_settings = NULL; + struct drx_common_attr *common_attr = NULL; + int rc; + drx_write_reg16func_t scu_wr16 = NULL; + drx_read_reg16func_t scu_rr16 = NULL; + + common_attr = (struct drx_common_attr *) demod->my_common_attr; + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + if (atomic) { + scu_rr16 = drxj_dap_scu_atomic_read_reg16; + scu_wr16 = drxj_dap_scu_atomic_write_reg16; + } else { + scu_rr16 = drxj_dap_read_reg16; + scu_wr16 = drxj_dap_write_reg16; + } + + /* Configure AGC only if standard is currently active */ + if ((ext_attr->standard == agc_settings->standard) || + (DRXJ_ISQAMSTD(ext_attr->standard) && + DRXJ_ISQAMSTD(agc_settings->standard)) || + (DRXJ_ISATVSTD(ext_attr->standard) && + DRXJ_ISATVSTD(agc_settings->standard))) { + u16 data = 0; + + switch (agc_settings->ctrl_mode) { + case DRX_AGC_CTRL_AUTO: + + /* Enable RF AGC DAC */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data |= IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE; + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Enable SCU RF AGC loop */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_RF__M; + if (ext_attr->standard == DRX_STANDARD_8VSB) + data |= (2 << SCU_RAM_AGC_KI_RF__B); + else if (DRXJ_ISQAMSTD(ext_attr->standard)) + data |= (5 << SCU_RAM_AGC_KI_RF__B); + else + data |= (4 << SCU_RAM_AGC_KI_RF__B); + + if (common_attr->tuner_rf_agc_pol) + data |= SCU_RAM_AGC_KI_INV_RF_POL__M; + else + data &= ~SCU_RAM_AGC_KI_INV_RF_POL__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Set speed ( using complementary reduction value ) */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, (~(agc_settings->speed << SCU_RAM_AGC_KI_RED_RAGC_RED__B) & SCU_RAM_AGC_KI_RED_RAGC_RED__M) | data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (agc_settings->standard == DRX_STANDARD_8VSB) + p_agc_settings = &(ext_attr->vsb_if_agc_cfg); + else if (DRXJ_ISQAMSTD(agc_settings->standard)) + p_agc_settings = &(ext_attr->qam_if_agc_cfg); + else if (DRXJ_ISATVSTD(agc_settings->standard)) + p_agc_settings = &(ext_attr->atv_if_agc_cfg); + else + return -EINVAL; + + /* Set TOP, only if IF-AGC is in AUTO mode */ + if (p_agc_settings->ctrl_mode == DRX_AGC_CTRL_AUTO) { + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, agc_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, agc_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* Cut-Off current */ + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_RF_IACCU_HI_CO__A, agc_settings->cut_off_current, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_AGC_CTRL_USER: + + /* Enable RF AGC DAC */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data |= IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE; + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Disable SCU RF AGC loop */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_RF__M; + if (common_attr->tuner_rf_agc_pol) + data |= SCU_RAM_AGC_KI_INV_RF_POL__M; + else + data &= ~SCU_RAM_AGC_KI_INV_RF_POL__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Write value to output pin */ + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_RF_IACCU_HI__A, agc_settings->output_level, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_AGC_CTRL_OFF: + + /* Disable RF AGC DAC */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= (~IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE); + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Disable SCU RF AGC loop */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_RF__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EINVAL; + } /* switch ( agcsettings->ctrl_mode ) */ + } + + /* Store rf agc settings */ + switch (agc_settings->standard) { + case DRX_STANDARD_8VSB: + ext_attr->vsb_rf_agc_cfg = *agc_settings; + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + ext_attr->qam_rf_agc_cfg = *agc_settings; + break; +#endif + default: + return -EIO; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_agc_if () +* \brief Configure If AGC +* \param demod instance of demodulator. +* \param agc_settings AGC configuration structure +* \return int. +*/ +static int +set_agc_if(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, bool atomic) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + struct drxj_cfg_agc *p_agc_settings = NULL; + struct drx_common_attr *common_attr = NULL; + drx_write_reg16func_t scu_wr16 = NULL; + drx_read_reg16func_t scu_rr16 = NULL; + int rc; + + common_attr = (struct drx_common_attr *) demod->my_common_attr; + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + if (atomic) { + scu_rr16 = drxj_dap_scu_atomic_read_reg16; + scu_wr16 = drxj_dap_scu_atomic_write_reg16; + } else { + scu_rr16 = drxj_dap_read_reg16; + scu_wr16 = drxj_dap_write_reg16; + } + + /* Configure AGC only if standard is currently active */ + if ((ext_attr->standard == agc_settings->standard) || + (DRXJ_ISQAMSTD(ext_attr->standard) && + DRXJ_ISQAMSTD(agc_settings->standard)) || + (DRXJ_ISATVSTD(ext_attr->standard) && + DRXJ_ISATVSTD(agc_settings->standard))) { + u16 data = 0; + + switch (agc_settings->ctrl_mode) { + case DRX_AGC_CTRL_AUTO: + /* Enable IF AGC DAC */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data |= IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE; + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Enable SCU IF AGC loop */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M; + data &= ~SCU_RAM_AGC_KI_IF__M; + if (ext_attr->standard == DRX_STANDARD_8VSB) + data |= (3 << SCU_RAM_AGC_KI_IF__B); + else if (DRXJ_ISQAMSTD(ext_attr->standard)) + data |= (6 << SCU_RAM_AGC_KI_IF__B); + else + data |= (5 << SCU_RAM_AGC_KI_IF__B); + + if (common_attr->tuner_if_agc_pol) + data |= SCU_RAM_AGC_KI_INV_IF_POL__M; + else + data &= ~SCU_RAM_AGC_KI_INV_IF_POL__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Set speed (using complementary reduction value) */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M; + rc = (*scu_wr16) (dev_addr, SCU_RAM_AGC_KI_RED__A, (~(agc_settings->speed << SCU_RAM_AGC_KI_RED_IAGC_RED__B) & SCU_RAM_AGC_KI_RED_IAGC_RED__M) | data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (agc_settings->standard == DRX_STANDARD_8VSB) + p_agc_settings = &(ext_attr->vsb_rf_agc_cfg); + else if (DRXJ_ISQAMSTD(agc_settings->standard)) + p_agc_settings = &(ext_attr->qam_rf_agc_cfg); + else if (DRXJ_ISATVSTD(agc_settings->standard)) + p_agc_settings = &(ext_attr->atv_rf_agc_cfg); + else + return -EINVAL; + + /* Restore TOP */ + if (p_agc_settings->ctrl_mode == DRX_AGC_CTRL_AUTO) { + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, p_agc_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, p_agc_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else { + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + break; + + case DRX_AGC_CTRL_USER: + + /* Enable IF AGC DAC */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data |= IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE; + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Disable SCU IF AGC loop */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M; + data |= SCU_RAM_AGC_KI_IF_AGC_DISABLE__M; + if (common_attr->tuner_if_agc_pol) + data |= SCU_RAM_AGC_KI_INV_IF_POL__M; + else + data &= ~SCU_RAM_AGC_KI_INV_IF_POL__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Write value to output pin */ + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, agc_settings->output_level, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + + case DRX_AGC_CTRL_OFF: + + /* Disable If AGC DAC */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= (~IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE); + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Disable SCU IF AGC loop */ + rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M; + data |= SCU_RAM_AGC_KI_IF_AGC_DISABLE__M; + rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EINVAL; + } /* switch ( agcsettings->ctrl_mode ) */ + + /* always set the top to support configurations without if-loop */ + rc = (*scu_wr16) (dev_addr, SCU_RAM_AGC_INGAIN_TGT_MIN__A, agc_settings->top, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* Store if agc settings */ + switch (agc_settings->standard) { + case DRX_STANDARD_8VSB: + ext_attr->vsb_if_agc_cfg = *agc_settings; + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + ext_attr->qam_if_agc_cfg = *agc_settings; + break; +#endif + default: + return -EIO; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_iqm_af () +* \brief Configure IQM AF registers +* \param demod instance of demodulator. +* \param active +* \return int. +*/ +static int set_iqm_af(struct drx_demod_instance *demod, bool active) +{ + u16 data = 0; + struct i2c_device_addr *dev_addr = NULL; + int rc; + + dev_addr = demod->my_i2c_dev_addr; + + /* Configure IQM */ + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (!active) + data &= ((~IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_PD_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE)); + else + data |= (IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE | IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE | IQM_AF_STDBY_STDBY_PD_A2_ACTIVE | IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE | IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE); + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ +/*== END 8VSB & QAM COMMON DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ + +/*============================================================================*/ +/*============================================================================*/ +/*== 8VSB DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ +/*============================================================================*/ + +/* +* \fn int power_down_vsb () +* \brief Powr down QAM related blocks. +* \param demod instance of demodulator. +* \param channel pointer to channel data. +* \return int. +*/ +static int power_down_vsb(struct drx_demod_instance *demod, bool primary) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drxjscu_cmd cmd_scu = { /* command */ 0, + /* parameter_len */ 0, + /* result_len */ 0, + /* *parameter */ NULL, + /* *result */ NULL + }; + struct drx_cfg_mpeg_output cfg_mpeg_output; + int rc; + u16 cmd_result = 0; + + /* + STOP demodulator + reset of FEC and VSB HW + */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB | + SCU_RAM_COMMAND_CMD_DEMOD_STOP; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* stop all comm_exec */ + rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (primary) { + rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_iqm_af(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else { + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + cfg_mpeg_output.enable_mpeg_output = false; + rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_vsb_leak_n_gain () +* \brief Set ATSC demod. +* \param demod instance of demodulator. +* \return int. +*/ +static int set_vsb_leak_n_gain(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = NULL; + int rc; + + static const u8 vsb_ffe_leak_gain_ram0[] = { + DRXJ_16TO8(0x8), /* FFETRAINLKRATIO1 */ + DRXJ_16TO8(0x8), /* FFETRAINLKRATIO2 */ + DRXJ_16TO8(0x8), /* FFETRAINLKRATIO3 */ + DRXJ_16TO8(0xf), /* FFETRAINLKRATIO4 */ + DRXJ_16TO8(0xf), /* FFETRAINLKRATIO5 */ + DRXJ_16TO8(0xf), /* FFETRAINLKRATIO6 */ + DRXJ_16TO8(0xf), /* FFETRAINLKRATIO7 */ + DRXJ_16TO8(0xf), /* FFETRAINLKRATIO8 */ + DRXJ_16TO8(0xf), /* FFETRAINLKRATIO9 */ + DRXJ_16TO8(0x8), /* FFETRAINLKRATIO10 */ + DRXJ_16TO8(0x8), /* FFETRAINLKRATIO11 */ + DRXJ_16TO8(0x8), /* FFETRAINLKRATIO12 */ + DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO1 */ + DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO2 */ + DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO3 */ + DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO4 */ + DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO5 */ + DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO6 */ + DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO7 */ + DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO8 */ + DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO9 */ + DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO10 */ + DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO11 */ + DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO12 */ + DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO1 */ + DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO2 */ + DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO3 */ + DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO4 */ + DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO5 */ + DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO6 */ + DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO7 */ + DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO8 */ + DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO9 */ + DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO10 */ + DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO11 */ + DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO12 */ + DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO1 */ + DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO2 */ + DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO3 */ + DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO4 */ + DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO5 */ + DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO6 */ + DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO7 */ + DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO8 */ + DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO9 */ + DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO10 */ + DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO11 */ + DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO12 */ + DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO1 */ + DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO2 */ + DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO3 */ + DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO4 */ + DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO5 */ + DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO6 */ + DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO7 */ + DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO8 */ + DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO9 */ + DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO10 */ + DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO11 */ + DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO12 */ + DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO1 */ + DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO2 */ + DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO3 */ + DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO4 */ + DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO5 */ + DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO6 */ + DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO7 */ + DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO8 */ + DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO9 */ + DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO10 */ + DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO11 */ + DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO12 */ + DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO1 */ + DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO2 */ + DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO3 */ + DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO4 */ + DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO5 */ + DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO6 */ + DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO7 */ + DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO8 */ + DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO9 */ + DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO10 */ + DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO11 */ + DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO12 */ + DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO1 */ + DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO2 */ + DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO3 */ + DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO4 */ + DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO5 */ + DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO6 */ + DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO7 */ + DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO8 */ + DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO9 */ + DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO10 */ + DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO11 */ + DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO12 */ + DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO1 */ + DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO2 */ + DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO3 */ + DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO4 */ + DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO5 */ + DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO6 */ + DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO7 */ + DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO8 */ + DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO9 */ + DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO10 */ + DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO11 */ + DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO12 */ + DRXJ_16TO8(0x2020), /* FIRTRAINGAIN1 */ + DRXJ_16TO8(0x2020), /* FIRTRAINGAIN2 */ + DRXJ_16TO8(0x2020), /* FIRTRAINGAIN3 */ + DRXJ_16TO8(0x4040), /* FIRTRAINGAIN4 */ + DRXJ_16TO8(0x4040), /* FIRTRAINGAIN5 */ + DRXJ_16TO8(0x4040), /* FIRTRAINGAIN6 */ + DRXJ_16TO8(0x4040), /* FIRTRAINGAIN7 */ + DRXJ_16TO8(0x4040), /* FIRTRAINGAIN8 */ + DRXJ_16TO8(0x4040), /* FIRTRAINGAIN9 */ + DRXJ_16TO8(0x2020), /* FIRTRAINGAIN10 */ + DRXJ_16TO8(0x2020), /* FIRTRAINGAIN11 */ + DRXJ_16TO8(0x2020), /* FIRTRAINGAIN12 */ + DRXJ_16TO8(0x0808), /* FIRRCA1GAIN1 */ + DRXJ_16TO8(0x0808), /* FIRRCA1GAIN2 */ + DRXJ_16TO8(0x0808), /* FIRRCA1GAIN3 */ + DRXJ_16TO8(0x1010), /* FIRRCA1GAIN4 */ + DRXJ_16TO8(0x1010), /* FIRRCA1GAIN5 */ + DRXJ_16TO8(0x1010), /* FIRRCA1GAIN6 */ + DRXJ_16TO8(0x1010), /* FIRRCA1GAIN7 */ + DRXJ_16TO8(0x1010) /* FIRRCA1GAIN8 */ + }; + + static const u8 vsb_ffe_leak_gain_ram1[] = { + DRXJ_16TO8(0x1010), /* FIRRCA1GAIN9 */ + DRXJ_16TO8(0x0808), /* FIRRCA1GAIN10 */ + DRXJ_16TO8(0x0808), /* FIRRCA1GAIN11 */ + DRXJ_16TO8(0x0808), /* FIRRCA1GAIN12 */ + DRXJ_16TO8(0x0808), /* FIRRCA2GAIN1 */ + DRXJ_16TO8(0x0808), /* FIRRCA2GAIN2 */ + DRXJ_16TO8(0x0808), /* FIRRCA2GAIN3 */ + DRXJ_16TO8(0x1010), /* FIRRCA2GAIN4 */ + DRXJ_16TO8(0x1010), /* FIRRCA2GAIN5 */ + DRXJ_16TO8(0x1010), /* FIRRCA2GAIN6 */ + DRXJ_16TO8(0x1010), /* FIRRCA2GAIN7 */ + DRXJ_16TO8(0x1010), /* FIRRCA2GAIN8 */ + DRXJ_16TO8(0x1010), /* FIRRCA2GAIN9 */ + DRXJ_16TO8(0x0808), /* FIRRCA2GAIN10 */ + DRXJ_16TO8(0x0808), /* FIRRCA2GAIN11 */ + DRXJ_16TO8(0x0808), /* FIRRCA2GAIN12 */ + DRXJ_16TO8(0x0303), /* FIRDDM1GAIN1 */ + DRXJ_16TO8(0x0303), /* FIRDDM1GAIN2 */ + DRXJ_16TO8(0x0303), /* FIRDDM1GAIN3 */ + DRXJ_16TO8(0x0606), /* FIRDDM1GAIN4 */ + DRXJ_16TO8(0x0606), /* FIRDDM1GAIN5 */ + DRXJ_16TO8(0x0606), /* FIRDDM1GAIN6 */ + DRXJ_16TO8(0x0606), /* FIRDDM1GAIN7 */ + DRXJ_16TO8(0x0606), /* FIRDDM1GAIN8 */ + DRXJ_16TO8(0x0606), /* FIRDDM1GAIN9 */ + DRXJ_16TO8(0x0303), /* FIRDDM1GAIN10 */ + DRXJ_16TO8(0x0303), /* FIRDDM1GAIN11 */ + DRXJ_16TO8(0x0303), /* FIRDDM1GAIN12 */ + DRXJ_16TO8(0x0303), /* FIRDDM2GAIN1 */ + DRXJ_16TO8(0x0303), /* FIRDDM2GAIN2 */ + DRXJ_16TO8(0x0303), /* FIRDDM2GAIN3 */ + DRXJ_16TO8(0x0505), /* FIRDDM2GAIN4 */ + DRXJ_16TO8(0x0505), /* FIRDDM2GAIN5 */ + DRXJ_16TO8(0x0505), /* FIRDDM2GAIN6 */ + DRXJ_16TO8(0x0505), /* FIRDDM2GAIN7 */ + DRXJ_16TO8(0x0505), /* FIRDDM2GAIN8 */ + DRXJ_16TO8(0x0505), /* FIRDDM2GAIN9 */ + DRXJ_16TO8(0x0303), /* FIRDDM2GAIN10 */ + DRXJ_16TO8(0x0303), /* FIRDDM2GAIN11 */ + DRXJ_16TO8(0x0303), /* FIRDDM2GAIN12 */ + DRXJ_16TO8(0x001f), /* DFETRAINLKRATIO */ + DRXJ_16TO8(0x01ff), /* DFERCA1TRAINLKRATIO */ + DRXJ_16TO8(0x01ff), /* DFERCA1DATALKRATIO */ + DRXJ_16TO8(0x004f), /* DFERCA2TRAINLKRATIO */ + DRXJ_16TO8(0x004f), /* DFERCA2DATALKRATIO */ + DRXJ_16TO8(0x01ff), /* DFEDDM1TRAINLKRATIO */ + DRXJ_16TO8(0x01ff), /* DFEDDM1DATALKRATIO */ + DRXJ_16TO8(0x0352), /* DFEDDM2TRAINLKRATIO */ + DRXJ_16TO8(0x0352), /* DFEDDM2DATALKRATIO */ + DRXJ_16TO8(0x0000), /* DFETRAINGAIN */ + DRXJ_16TO8(0x2020), /* DFERCA1GAIN */ + DRXJ_16TO8(0x1010), /* DFERCA2GAIN */ + DRXJ_16TO8(0x1818), /* DFEDDM1GAIN */ + DRXJ_16TO8(0x1212) /* DFEDDM2GAIN */ + }; + + dev_addr = demod->my_i2c_dev_addr; + rc = drxdap_fasi_write_block(dev_addr, VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__A, sizeof(vsb_ffe_leak_gain_ram0), ((u8 *)vsb_ffe_leak_gain_ram0), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__A, sizeof(vsb_ffe_leak_gain_ram1), ((u8 *)vsb_ffe_leak_gain_ram1), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_vsb() +* \brief Set 8VSB demod. +* \param demod instance of demodulator. +* \return int. +* +*/ +static int set_vsb(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = NULL; + int rc; + struct drx_common_attr *common_attr = NULL; + struct drxjscu_cmd cmd_scu; + struct drxj_data *ext_attr = NULL; + u16 cmd_result = 0; + u16 cmd_param = 0; + static const u8 vsb_taps_re[] = { + DRXJ_16TO8(-2), /* re0 */ + DRXJ_16TO8(4), /* re1 */ + DRXJ_16TO8(1), /* re2 */ + DRXJ_16TO8(-4), /* re3 */ + DRXJ_16TO8(1), /* re4 */ + DRXJ_16TO8(4), /* re5 */ + DRXJ_16TO8(-3), /* re6 */ + DRXJ_16TO8(-3), /* re7 */ + DRXJ_16TO8(6), /* re8 */ + DRXJ_16TO8(1), /* re9 */ + DRXJ_16TO8(-9), /* re10 */ + DRXJ_16TO8(3), /* re11 */ + DRXJ_16TO8(12), /* re12 */ + DRXJ_16TO8(-9), /* re13 */ + DRXJ_16TO8(-15), /* re14 */ + DRXJ_16TO8(17), /* re15 */ + DRXJ_16TO8(19), /* re16 */ + DRXJ_16TO8(-29), /* re17 */ + DRXJ_16TO8(-22), /* re18 */ + DRXJ_16TO8(45), /* re19 */ + DRXJ_16TO8(25), /* re20 */ + DRXJ_16TO8(-70), /* re21 */ + DRXJ_16TO8(-28), /* re22 */ + DRXJ_16TO8(111), /* re23 */ + DRXJ_16TO8(30), /* re24 */ + DRXJ_16TO8(-201), /* re25 */ + DRXJ_16TO8(-31), /* re26 */ + DRXJ_16TO8(629) /* re27 */ + }; + + dev_addr = demod->my_i2c_dev_addr; + common_attr = (struct drx_common_attr *) demod->my_common_attr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + /* stop all comm_exec */ + rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* reset demodulator */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB + | SCU_RAM_COMMAND_CMD_DEMOD_RESET; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_DCF_BYPASS__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_ADJ_SEL__A, IQM_FS_ADJ_SEL_B_VSB, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_ADJ_SEL__A, IQM_RC_ADJ_SEL_B_VSB, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->iqm_rc_rate_ofs = 0x00AD0D79; + rc = drxdap_fasi_write_reg32(dev_addr, IQM_RC_RATE_OFS_LO__A, ext_attr->iqm_rc_rate_ofs, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CFAGC_GAINSHIFT__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1TRK__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_CROUT_ENA__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_STRETCH__A, 28, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RT_ACTIVE__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SYMMETRIC__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_MIDTAP__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_OUT_ENA__A, IQM_CF_OUT_ENA_VSB__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE__A, 1393, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(vsb_taps_re), ((u8 *)vsb_taps_re), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(vsb_taps_re), ((u8 *)vsb_taps_re), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BNTHRESH__A, 330, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* set higher threshold */ + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CLPLASTNUM__A, 90, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* burst detection on */ + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_RCA1__A, 0x0042, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* drop thresholds by 1 dB */ + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_RCA2__A, 0x0053, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* drop thresholds by 2 dB */ + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_EQCTRL__A, 0x1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* cma on */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_GPIO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* GPIO */ + + /* Initialize the FEC Subsystem */ + rc = drxj_dap_write_reg16(dev_addr, FEC_TOP_ANNEX__A, FEC_TOP_ANNEX_D, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + { + u16 fec_oc_snc_mode = 0; + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* output data even when not locked */ + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_MODE__A, fec_oc_snc_mode | FEC_OC_SNC_MODE_UNLOCK_ENABLE__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* set clip */ + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_LEN__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_TH__A, 470, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SNS_LEN__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_PT__A, 0xD4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* no transparent, no A&C framing; parity is set in mpegoutput */ + { + u16 fec_oc_reg_mode = 0; + 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_write_reg16(dev_addr, FEC_OC_MODE__A, fec_oc_reg_mode & (~(FEC_OC_MODE_TRANSPARENT__M | FEC_OC_MODE_CLEAR__M | FEC_OC_MODE_RETAIN_FRAMING__M)), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + rc = drxj_dap_write_reg16(dev_addr, FEC_DI_TIMEOUT_LO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* timeout counter for restarting */ + rc = drxj_dap_write_reg16(dev_addr, FEC_DI_TIMEOUT_HI__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MODE__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* bypass disabled */ + /* initialize RS packet error measurement parameters */ + rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PERIOD__A, FEC_RS_MEASUREMENT_PERIOD, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PRESCALE__A, FEC_RS_MEASUREMENT_PRESCALE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* init measurement period of MER/SER */ + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_MEASUREMENT_PERIOD__A, VSB_TOP_MEASUREMENT_PERIOD, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_reg32(dev_addr, SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_MEAS_COUNT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CKGN1TRK__A, 128, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* B-Input to ADC, PGA+filter in standby */ + if (!ext_attr->has_lna) { + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AMUX__A, 0x02, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* turn on IQMAF. It has to be in front of setAgc**() */ + rc = set_iqm_af(demod, true); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = adc_synchronization(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = init_agc(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_agc_if(demod, &(ext_attr->vsb_if_agc_cfg), false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_agc_rf(demod, &(ext_attr->vsb_rf_agc_cfg), false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + { + /* TODO fix this, store a struct drxj_cfg_afe_gain structure in struct drxj_data instead + of only the gain */ + struct drxj_cfg_afe_gain vsb_pga_cfg = { DRX_STANDARD_8VSB, 0 }; + + vsb_pga_cfg.gain = ext_attr->vsb_pga_cfg; + rc = ctrl_set_cfg_afe_gain(demod, &vsb_pga_cfg); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + rc = ctrl_set_cfg_pre_saw(demod, &(ext_attr->vsb_pre_saw_cfg)); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Mpeg output has to be in front of FEC active */ + rc = set_mpegtei_handling(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = bit_reverse_mpeg_output(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_mpeg_start_width(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + { + /* TODO: move to set_standard after hardware reset value problem is solved */ + /* Configure initial MPEG output */ + struct drx_cfg_mpeg_output cfg_mpeg_output; + + memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output)); + cfg_mpeg_output.enable_mpeg_output = true; + + rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* TBD: what parameters should be set */ + cmd_param = 0x00; /* Default mode AGC on, etc */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB + | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM; + cmd_scu.parameter_len = 1; + cmd_scu.result_len = 1; + cmd_scu.parameter = &cmd_param; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BEAGC_GAINSHIFT__A, 0x0004, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_PT__A, 0x00D2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SYSSMTRNCTRL__A, VSB_TOP_SYSSMTRNCTRL__PRE | VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BEDETCTRL__A, 0x142, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_LBAGCREFLVL__A, 640, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1ACQ__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1TRK__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN2TRK__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* start demodulator */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB + | SCU_RAM_COMMAND_CMD_DEMOD_START; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn static short get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr, u16 *PckErrs) +* \brief Get the values of packet error in 8VSB mode +* \return Error code +*/ +static int get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr, + u32 *pck_errs, u32 *pck_count) +{ + int rc; + u16 data = 0; + u16 period = 0; + u16 prescale = 0; + u16 packet_errors_mant = 0; + u16 packet_errors_exp = 0; + + rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_FAILURES__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + packet_errors_mant = data & FEC_RS_NR_FAILURES_FIXED_MANT__M; + packet_errors_exp = (data & FEC_RS_NR_FAILURES_EXP__M) + >> FEC_RS_NR_FAILURES_EXP__B; + period = FEC_RS_MEASUREMENT_PERIOD; + prescale = FEC_RS_MEASUREMENT_PRESCALE; + /* packet error rate = (error packet number) per second */ + /* 77.3 us is time for per packet */ + if (period * prescale == 0) { + pr_err("error: period and/or prescale is zero!\n"); + return -EIO; + } + *pck_errs = packet_errors_mant * (1 << packet_errors_exp); + *pck_count = period * prescale * 77; + + return 0; +rw_error: + return rc; +} + +/* +* \fn static short GetVSBBer(struct i2c_device_addr *dev_addr, u32 *ber) +* \brief Get the values of ber in VSB mode +* \return Error code +*/ +static int get_vs_bpost_viterbi_ber(struct i2c_device_addr *dev_addr, + u32 *ber, u32 *cnt) +{ + int rc; + u16 data = 0; + u16 period = 0; + u16 prescale = 0; + u16 bit_errors_mant = 0; + u16 bit_errors_exp = 0; + + rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + period = FEC_RS_MEASUREMENT_PERIOD; + prescale = FEC_RS_MEASUREMENT_PRESCALE; + + bit_errors_mant = data & FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M; + bit_errors_exp = (data & FEC_RS_NR_BIT_ERRORS_EXP__M) + >> FEC_RS_NR_BIT_ERRORS_EXP__B; + + *cnt = period * prescale * 207 * ((bit_errors_exp > 2) ? 1 : 8); + + if (((bit_errors_mant << bit_errors_exp) >> 3) > 68700) + *ber = (*cnt) * 26570; + else { + if (period * prescale == 0) { + pr_err("error: period and/or prescale is zero!\n"); + return -EIO; + } + *ber = bit_errors_mant << ((bit_errors_exp > 2) ? + (bit_errors_exp - 3) : bit_errors_exp); + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn static short get_vs_bpre_viterbi_ber(struct i2c_device_addr *dev_addr, u32 *ber) +* \brief Get the values of ber in VSB mode +* \return Error code +*/ +static int get_vs_bpre_viterbi_ber(struct i2c_device_addr *dev_addr, + u32 *ber, u32 *cnt) +{ + u16 data = 0; + int rc; + + rc = drxj_dap_read_reg16(dev_addr, VSB_TOP_NR_SYM_ERRS__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + return -EIO; + } + *ber = data; + *cnt = VSB_TOP_MEASUREMENT_PERIOD * SYMBOLS_PER_SEGMENT; + + return 0; +} + +/* +* \fn static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer) +* \brief Get the values of MER +* \return Error code +*/ +static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer) +{ + int rc; + u16 data_hi = 0; + + rc = drxj_dap_read_reg16(dev_addr, VSB_TOP_ERR_ENERGY_H__A, &data_hi, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + *mer = + (u16) (log1_times100(21504) - log1_times100((data_hi << 6) / 52)); + + return 0; +rw_error: + return rc; +} + + +/*============================================================================*/ +/*== END 8VSB DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ + +/*============================================================================*/ +/*============================================================================*/ +/*== QAM DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ +/*============================================================================*/ + +/* +* \fn int power_down_qam () +* \brief Powr down QAM related blocks. +* \param demod instance of demodulator. +* \param channel pointer to channel data. +* \return int. +*/ +static int power_down_qam(struct drx_demod_instance *demod, bool primary) +{ + struct drxjscu_cmd cmd_scu = { /* command */ 0, + /* parameter_len */ 0, + /* result_len */ 0, + /* *parameter */ NULL, + /* *result */ NULL + }; + int rc; + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drx_cfg_mpeg_output cfg_mpeg_output; + struct drx_common_attr *common_attr = demod->my_common_attr; + u16 cmd_result = 0; + + /* + STOP demodulator + resets IQM, QAM and FEC HW blocks + */ + /* stop all comm_exec */ + rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_STOP; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (primary) { + rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_iqm_af(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else { + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output)); + cfg_mpeg_output.enable_mpeg_output = false; + + rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int set_qam_measurement () +* \brief Setup of the QAM Measuremnt intervals for signal quality +* \param demod instance of demod. +* \param constellation current constellation. +* \return int. +* +* NOTE: +* Take into account that for certain settings the errorcounters can overflow. +* The implementation does not check this. +* +* TODO: overriding the ext_attr->fec_bits_desired by constellation dependent +* constants to get a measurement period of approx. 1 sec. Remove fec_bits_desired +* field ? +* +*/ +#ifndef DRXJ_VSB_ONLY +static int +set_qam_measurement(struct drx_demod_instance *demod, + enum drx_modulation constellation, u32 symbol_rate) +{ + struct i2c_device_addr *dev_addr = NULL; /* device address for I2C writes */ + struct drxj_data *ext_attr = NULL; /* Global data container for DRXJ specific data */ + int rc; + u32 fec_bits_desired = 0; /* BER accounting period */ + u16 fec_rs_plen = 0; /* defines RS BER measurement period */ + u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */ + u32 fec_rs_period = 0; /* Value for corresponding I2C register */ + u32 fec_rs_bit_cnt = 0; /* Actual precise amount of bits */ + u32 fec_oc_snc_fail_period = 0; /* Value for corresponding I2C register */ + u32 qam_vd_period = 0; /* Value for corresponding I2C register */ + u32 qam_vd_bit_cnt = 0; /* Actual precise amount of bits */ + u16 fec_vd_plen = 0; /* no of trellis symbols: VD SER measur period */ + u16 qam_vd_prescale = 0; /* Viterbi Measurement Prescale */ + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + fec_bits_desired = ext_attr->fec_bits_desired; + fec_rs_prescale = ext_attr->fec_rs_prescale; + + switch (constellation) { + case DRX_CONSTELLATION_QAM16: + fec_bits_desired = 4 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM32: + fec_bits_desired = 5 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM64: + fec_bits_desired = 6 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM128: + fec_bits_desired = 7 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM256: + fec_bits_desired = 8 * symbol_rate; + break; + default: + return -EINVAL; + } + + /* Parameters for Reed-Solomon Decoder */ + /* fecrs_period = (int)ceil(FEC_BITS_DESIRED/(fecrs_prescale*plen)) */ + /* rs_bit_cnt = fecrs_period*fecrs_prescale*plen */ + /* result is within 32 bit arithmetic -> */ + /* no need for mult or frac functions */ + + /* TODO: use constant instead of calculation and remove the fec_rs_plen in ext_attr */ + switch (ext_attr->standard) { + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_C: + fec_rs_plen = 204 * 8; + break; + case DRX_STANDARD_ITU_B: + fec_rs_plen = 128 * 7; + break; + default: + return -EINVAL; + } + + ext_attr->fec_rs_plen = fec_rs_plen; /* for getSigQual */ + fec_rs_bit_cnt = fec_rs_prescale * fec_rs_plen; /* temp storage */ + if (fec_rs_bit_cnt == 0) { + pr_err("error: fec_rs_bit_cnt is zero!\n"); + return -EIO; + } + fec_rs_period = fec_bits_desired / fec_rs_bit_cnt + 1; /* ceil */ + if (ext_attr->standard != DRX_STANDARD_ITU_B) + fec_oc_snc_fail_period = fec_rs_period; + + /* limit to max 16 bit value (I2C register width) if needed */ + if (fec_rs_period > 0xFFFF) + fec_rs_period = 0xFFFF; + + /* write corresponding registers */ + switch (ext_attr->standard) { + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_C: + break; + case DRX_STANDARD_ITU_B: + switch (constellation) { + case DRX_CONSTELLATION_QAM64: + fec_rs_period = 31581; + fec_oc_snc_fail_period = 17932; + break; + case DRX_CONSTELLATION_QAM256: + fec_rs_period = 45446; + fec_oc_snc_fail_period = 25805; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_FAIL_PERIOD__A, (u16)fec_oc_snc_fail_period, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PERIOD__A, (u16)fec_rs_period, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PRESCALE__A, fec_rs_prescale, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->fec_rs_period = (u16) fec_rs_period; + ext_attr->fec_rs_prescale = fec_rs_prescale; + rc = drxdap_fasi_write_reg32(dev_addr, SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_MEAS_COUNT__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (ext_attr->standard == DRX_STANDARD_ITU_B) { + /* Parameters for Viterbi Decoder */ + /* qamvd_period = (int)ceil(FEC_BITS_DESIRED/ */ + /* (qamvd_prescale*plen*(qam_constellation+1))) */ + /* vd_bit_cnt = qamvd_period*qamvd_prescale*plen */ + /* result is within 32 bit arithmetic -> */ + /* no need for mult or frac functions */ + + /* a(8 bit) * b(8 bit) = 16 bit result => mult32 not needed */ + fec_vd_plen = ext_attr->fec_vd_plen; + qam_vd_prescale = ext_attr->qam_vd_prescale; + qam_vd_bit_cnt = qam_vd_prescale * fec_vd_plen; /* temp storage */ + + switch (constellation) { + case DRX_CONSTELLATION_QAM64: + /* a(16 bit) * b(4 bit) = 20 bit result => mult32 not needed */ + qam_vd_period = + qam_vd_bit_cnt * (QAM_TOP_CONSTELLATION_QAM64 + 1) + * (QAM_TOP_CONSTELLATION_QAM64 + 1); + break; + case DRX_CONSTELLATION_QAM256: + /* a(16 bit) * b(5 bit) = 21 bit result => mult32 not needed */ + qam_vd_period = + qam_vd_bit_cnt * (QAM_TOP_CONSTELLATION_QAM256 + 1) + * (QAM_TOP_CONSTELLATION_QAM256 + 1); + break; + default: + return -EINVAL; + } + if (qam_vd_period == 0) { + pr_err("error: qam_vd_period is zero!\n"); + return -EIO; + } + qam_vd_period = fec_bits_desired / qam_vd_period; + /* limit to max 16 bit value (I2C register width) if needed */ + if (qam_vd_period > 0xFFFF) + qam_vd_period = 0xFFFF; + + /* a(16 bit) * b(16 bit) = 32 bit result => mult32 not needed */ + qam_vd_bit_cnt *= qam_vd_period; + + rc = drxj_dap_write_reg16(dev_addr, QAM_VD_MEASUREMENT_PERIOD__A, (u16)qam_vd_period, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_VD_MEASUREMENT_PRESCALE__A, qam_vd_prescale, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->qam_vd_period = (u16) qam_vd_period; + ext_attr->qam_vd_prescale = qam_vd_prescale; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int set_qam16 () +* \brief QAM16 specific setup +* \param demod instance of demod. +* \return int. +*/ +static int set_qam16(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + static const u8 qam_dq_qual_fun[] = { + DRXJ_16TO8(2), /* fun0 */ + DRXJ_16TO8(2), /* fun1 */ + DRXJ_16TO8(2), /* fun2 */ + DRXJ_16TO8(2), /* fun3 */ + DRXJ_16TO8(3), /* fun4 */ + DRXJ_16TO8(3), /* fun5 */ + }; + static const u8 qam_eq_cma_rad[] = { + DRXJ_16TO8(13517), /* RAD0 */ + DRXJ_16TO8(13517), /* RAD1 */ + DRXJ_16TO8(13517), /* RAD2 */ + DRXJ_16TO8(13517), /* RAD3 */ + DRXJ_16TO8(13517), /* RAD4 */ + DRXJ_16TO8(13517), /* RAD5 */ + }; + + rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 140, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 120, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 230, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 95, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 105, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 56, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 220, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 25, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 6, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-24), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-65), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-127), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 10, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 240, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 40960, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int set_qam32 () +* \brief QAM32 specific setup +* \param demod instance of demod. +* \return int. +*/ +static int set_qam32(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + static const u8 qam_dq_qual_fun[] = { + DRXJ_16TO8(3), /* fun0 */ + DRXJ_16TO8(3), /* fun1 */ + DRXJ_16TO8(3), /* fun2 */ + DRXJ_16TO8(3), /* fun3 */ + DRXJ_16TO8(4), /* fun4 */ + DRXJ_16TO8(4), /* fun5 */ + }; + static const u8 qam_eq_cma_rad[] = { + DRXJ_16TO8(6707), /* RAD0 */ + DRXJ_16TO8(6707), /* RAD1 */ + DRXJ_16TO8(6707), /* RAD2 */ + DRXJ_16TO8(6707), /* RAD3 */ + DRXJ_16TO8(6707), /* RAD4 */ + DRXJ_16TO8(6707), /* RAD5 */ + }; + + rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 90, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 170, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 100, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 56, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 140, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16)(-16), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-26), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-56), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-86), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 10, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 176, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 20480, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int set_qam64 () +* \brief QAM64 specific setup +* \param demod instance of demod. +* \return int. +*/ +static int set_qam64(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + static const u8 qam_dq_qual_fun[] = { + /* this is hw reset value. no necessary to re-write */ + DRXJ_16TO8(4), /* fun0 */ + DRXJ_16TO8(4), /* fun1 */ + DRXJ_16TO8(4), /* fun2 */ + DRXJ_16TO8(4), /* fun3 */ + DRXJ_16TO8(6), /* fun4 */ + DRXJ_16TO8(6), /* fun5 */ + }; + static const u8 qam_eq_cma_rad[] = { + DRXJ_16TO8(13336), /* RAD0 */ + DRXJ_16TO8(12618), /* RAD1 */ + DRXJ_16TO8(11988), /* RAD2 */ + DRXJ_16TO8(13809), /* RAD3 */ + DRXJ_16TO8(13809), /* RAD4 */ + DRXJ_16TO8(15609), /* RAD5 */ + }; + + rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 105, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 195, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 84, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 141, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 7, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-15), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-45), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-80), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 48, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 160, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 43008, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int set_qam128 () +* \brief QAM128 specific setup +* \param demod: instance of demod. +* \return int. +*/ +static int set_qam128(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + static const u8 qam_dq_qual_fun[] = { + DRXJ_16TO8(6), /* fun0 */ + DRXJ_16TO8(6), /* fun1 */ + DRXJ_16TO8(6), /* fun2 */ + DRXJ_16TO8(6), /* fun3 */ + DRXJ_16TO8(9), /* fun4 */ + DRXJ_16TO8(9), /* fun5 */ + }; + static const u8 qam_eq_cma_rad[] = { + DRXJ_16TO8(6164), /* RAD0 */ + DRXJ_16TO8(6598), /* RAD1 */ + DRXJ_16TO8(6394), /* RAD2 */ + DRXJ_16TO8(6409), /* RAD3 */ + DRXJ_16TO8(6656), /* RAD4 */ + DRXJ_16TO8(7238), /* RAD5 */ + }; + + rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 140, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 100, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 65, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-1), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-23), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 144, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 20992, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int set_qam256 () +* \brief QAM256 specific setup +* \param demod: instance of demod. +* \return int. +*/ +static int set_qam256(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + static const u8 qam_dq_qual_fun[] = { + DRXJ_16TO8(8), /* fun0 */ + DRXJ_16TO8(8), /* fun1 */ + DRXJ_16TO8(8), /* fun2 */ + DRXJ_16TO8(8), /* fun3 */ + DRXJ_16TO8(12), /* fun4 */ + DRXJ_16TO8(12), /* fun5 */ + }; + static const u8 qam_eq_cma_rad[] = { + DRXJ_16TO8(12345), /* RAD0 */ + DRXJ_16TO8(12345), /* RAD1 */ + DRXJ_16TO8(13626), /* RAD2 */ + DRXJ_16TO8(12931), /* RAD3 */ + DRXJ_16TO8(14719), /* RAD4 */ + DRXJ_16TO8(15356), /* RAD5 */ + }; + + rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 150, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 110, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 74, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 18, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 13, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, 7, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 25, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 48, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 80, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 16, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 43520, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ +#define QAM_SET_OP_ALL 0x1 +#define QAM_SET_OP_CONSTELLATION 0x2 +#define QAM_SET_OP_SPECTRUM 0X4 + +/* +* \fn int set_qam () +* \brief Set QAM demod. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \return int. +*/ +static int +set_qam(struct drx_demod_instance *demod, + struct drx_channel *channel, s32 tuner_freq_offset, u32 op) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + struct drx_common_attr *common_attr = NULL; + int rc; + u32 adc_frequency = 0; + u32 iqm_rc_rate = 0; + u16 cmd_result = 0; + u16 lc_symbol_freq = 0; + u16 iqm_rc_stretch = 0; + u16 set_env_parameters = 0; + u16 set_param_parameters[2] = { 0 }; + struct drxjscu_cmd cmd_scu = { /* command */ 0, + /* parameter_len */ 0, + /* result_len */ 0, + /* parameter */ NULL, + /* result */ NULL + }; + static const u8 qam_a_taps[] = { + DRXJ_16TO8(-1), /* re0 */ + DRXJ_16TO8(1), /* re1 */ + DRXJ_16TO8(1), /* re2 */ + DRXJ_16TO8(-1), /* re3 */ + DRXJ_16TO8(-1), /* re4 */ + DRXJ_16TO8(2), /* re5 */ + DRXJ_16TO8(1), /* re6 */ + DRXJ_16TO8(-2), /* re7 */ + DRXJ_16TO8(0), /* re8 */ + DRXJ_16TO8(3), /* re9 */ + DRXJ_16TO8(-1), /* re10 */ + DRXJ_16TO8(-3), /* re11 */ + DRXJ_16TO8(4), /* re12 */ + DRXJ_16TO8(1), /* re13 */ + DRXJ_16TO8(-8), /* re14 */ + DRXJ_16TO8(4), /* re15 */ + DRXJ_16TO8(13), /* re16 */ + DRXJ_16TO8(-13), /* re17 */ + DRXJ_16TO8(-19), /* re18 */ + DRXJ_16TO8(28), /* re19 */ + DRXJ_16TO8(25), /* re20 */ + DRXJ_16TO8(-53), /* re21 */ + DRXJ_16TO8(-31), /* re22 */ + DRXJ_16TO8(96), /* re23 */ + DRXJ_16TO8(37), /* re24 */ + DRXJ_16TO8(-190), /* re25 */ + DRXJ_16TO8(-40), /* re26 */ + DRXJ_16TO8(619) /* re27 */ + }; + static const u8 qam_b64_taps[] = { + DRXJ_16TO8(0), /* re0 */ + DRXJ_16TO8(-2), /* re1 */ + DRXJ_16TO8(1), /* re2 */ + DRXJ_16TO8(2), /* re3 */ + DRXJ_16TO8(-2), /* re4 */ + DRXJ_16TO8(0), /* re5 */ + DRXJ_16TO8(4), /* re6 */ + DRXJ_16TO8(-2), /* re7 */ + DRXJ_16TO8(-4), /* re8 */ + DRXJ_16TO8(4), /* re9 */ + DRXJ_16TO8(3), /* re10 */ + DRXJ_16TO8(-6), /* re11 */ + DRXJ_16TO8(0), /* re12 */ + DRXJ_16TO8(6), /* re13 */ + DRXJ_16TO8(-5), /* re14 */ + DRXJ_16TO8(-3), /* re15 */ + DRXJ_16TO8(11), /* re16 */ + DRXJ_16TO8(-4), /* re17 */ + DRXJ_16TO8(-19), /* re18 */ + DRXJ_16TO8(19), /* re19 */ + DRXJ_16TO8(28), /* re20 */ + DRXJ_16TO8(-45), /* re21 */ + DRXJ_16TO8(-36), /* re22 */ + DRXJ_16TO8(90), /* re23 */ + DRXJ_16TO8(42), /* re24 */ + DRXJ_16TO8(-185), /* re25 */ + DRXJ_16TO8(-46), /* re26 */ + DRXJ_16TO8(614) /* re27 */ + }; + static const u8 qam_b256_taps[] = { + DRXJ_16TO8(-2), /* re0 */ + DRXJ_16TO8(4), /* re1 */ + DRXJ_16TO8(1), /* re2 */ + DRXJ_16TO8(-4), /* re3 */ + DRXJ_16TO8(0), /* re4 */ + DRXJ_16TO8(4), /* re5 */ + DRXJ_16TO8(-2), /* re6 */ + DRXJ_16TO8(-4), /* re7 */ + DRXJ_16TO8(5), /* re8 */ + DRXJ_16TO8(2), /* re9 */ + DRXJ_16TO8(-8), /* re10 */ + DRXJ_16TO8(2), /* re11 */ + DRXJ_16TO8(11), /* re12 */ + DRXJ_16TO8(-8), /* re13 */ + DRXJ_16TO8(-15), /* re14 */ + DRXJ_16TO8(16), /* re15 */ + DRXJ_16TO8(19), /* re16 */ + DRXJ_16TO8(-27), /* re17 */ + DRXJ_16TO8(-22), /* re18 */ + DRXJ_16TO8(44), /* re19 */ + DRXJ_16TO8(26), /* re20 */ + DRXJ_16TO8(-69), /* re21 */ + DRXJ_16TO8(-28), /* re22 */ + DRXJ_16TO8(110), /* re23 */ + DRXJ_16TO8(31), /* re24 */ + DRXJ_16TO8(-201), /* re25 */ + DRXJ_16TO8(-32), /* re26 */ + DRXJ_16TO8(628) /* re27 */ + }; + static const u8 qam_c_taps[] = { + DRXJ_16TO8(-3), /* re0 */ + DRXJ_16TO8(3), /* re1 */ + DRXJ_16TO8(2), /* re2 */ + DRXJ_16TO8(-4), /* re3 */ + DRXJ_16TO8(0), /* re4 */ + DRXJ_16TO8(4), /* re5 */ + DRXJ_16TO8(-1), /* re6 */ + DRXJ_16TO8(-4), /* re7 */ + DRXJ_16TO8(3), /* re8 */ + DRXJ_16TO8(3), /* re9 */ + DRXJ_16TO8(-5), /* re10 */ + DRXJ_16TO8(0), /* re11 */ + DRXJ_16TO8(9), /* re12 */ + DRXJ_16TO8(-4), /* re13 */ + DRXJ_16TO8(-12), /* re14 */ + DRXJ_16TO8(10), /* re15 */ + DRXJ_16TO8(16), /* re16 */ + DRXJ_16TO8(-21), /* re17 */ + DRXJ_16TO8(-20), /* re18 */ + DRXJ_16TO8(37), /* re19 */ + DRXJ_16TO8(25), /* re20 */ + DRXJ_16TO8(-62), /* re21 */ + DRXJ_16TO8(-28), /* re22 */ + DRXJ_16TO8(105), /* re23 */ + DRXJ_16TO8(31), /* re24 */ + DRXJ_16TO8(-197), /* re25 */ + DRXJ_16TO8(-33), /* re26 */ + DRXJ_16TO8(626) /* re27 */ + }; + + 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 ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) { + if (ext_attr->standard == DRX_STANDARD_ITU_B) { + switch (channel->constellation) { + case DRX_CONSTELLATION_QAM256: + iqm_rc_rate = 0x00AE3562; + lc_symbol_freq = + QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_256; + channel->symbolrate = 5360537; + iqm_rc_stretch = IQM_RC_STRETCH_QAM_B_256; + break; + case DRX_CONSTELLATION_QAM64: + iqm_rc_rate = 0x00C05A0E; + lc_symbol_freq = 409; + channel->symbolrate = 5056941; + iqm_rc_stretch = IQM_RC_STRETCH_QAM_B_64; + break; + default: + return -EINVAL; + } + } else { + adc_frequency = (common_attr->sys_clock_freq * 1000) / 3; + if (channel->symbolrate == 0) { + pr_err("error: channel symbolrate is zero!\n"); + return -EIO; + } + iqm_rc_rate = + (adc_frequency / channel->symbolrate) * (1 << 21) + + (frac28 + ((adc_frequency % channel->symbolrate), + channel->symbolrate) >> 7) - (1 << 23); + lc_symbol_freq = + (u16) (frac28 + (channel->symbolrate + + (adc_frequency >> 13), + adc_frequency) >> 16); + if (lc_symbol_freq > 511) + lc_symbol_freq = 511; + + iqm_rc_stretch = 21; + } + + if (ext_attr->standard == DRX_STANDARD_ITU_A) { + set_env_parameters = QAM_TOP_ANNEX_A; /* annex */ + set_param_parameters[0] = channel->constellation; /* constellation */ + set_param_parameters[1] = DRX_INTERLEAVEMODE_I12_J17; /* interleave mode */ + } else if (ext_attr->standard == DRX_STANDARD_ITU_B) { + set_env_parameters = QAM_TOP_ANNEX_B; /* annex */ + set_param_parameters[0] = channel->constellation; /* constellation */ + set_param_parameters[1] = channel->interleavemode; /* interleave mode */ + } else if (ext_attr->standard == DRX_STANDARD_ITU_C) { + set_env_parameters = QAM_TOP_ANNEX_C; /* annex */ + set_param_parameters[0] = channel->constellation; /* constellation */ + set_param_parameters[1] = DRX_INTERLEAVEMODE_I12_J17; /* interleave mode */ + } else { + return -EINVAL; + } + } + + if (op & QAM_SET_OP_ALL) { + /* + STEP 1: reset demodulator + resets IQM, QAM and FEC HW blocks + resets SCU variables + */ + /* stop all comm_exec */ + rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_RESET; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) { + /* + STEP 2: configure demodulator + -set env + -set params (resets IQM,QAM,FEC HW; initializes some SCU variables ) + */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV; + cmd_scu.parameter_len = 1; + cmd_scu.result_len = 1; + cmd_scu.parameter = &set_env_parameters; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM; + cmd_scu.parameter_len = 2; + cmd_scu.result_len = 1; + cmd_scu.parameter = set_param_parameters; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* set symbol rate */ + rc = drxdap_fasi_write_reg32(dev_addr, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->iqm_rc_rate_ofs = iqm_rc_rate; + rc = set_qam_measurement(demod, channel->constellation, channel->symbolrate); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + /* STEP 3: enable the system in a mode where the ADC provides valid signal + setup constellation independent registers */ + /* from qam_cmd.py script (qam_driver_b) */ + /* TODO: remove re-writes of HW reset values */ + if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_SPECTRUM)) { + rc = set_frequency(demod, channel, tuner_freq_offset); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) { + + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_SYMBOL_FREQ__A, lc_symbol_freq, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_STRETCH__A, iqm_rc_stretch, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + if (op & QAM_SET_OP_ALL) { + if (!ext_attr->has_lna) { + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AMUX__A, 0x02, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SYMMETRIC__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_MIDTAP__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_OUT_ENA__A, IQM_CF_OUT_ENA_QAM__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_WR_RSV_0__A, 0x5f, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* scu temporary shut down agc */ + + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SYNC_SEL__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_LEN__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_TH__A, 448, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SNS_LEN__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PDREF__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, 0x10, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PGA_GAIN__A, 11, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + if (ext_attr->standard == DRX_STANDARD_ITU_B) { + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, QAM_SY_SYNC_LWM__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, QAM_SY_SYNC_AWM__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, QAM_SY_SYNC_HWM__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + } else { + switch (channel->constellation) { + case DRX_CONSTELLATION_QAM16: + case DRX_CONSTELLATION_QAM64: + case DRX_CONSTELLATION_QAM256: + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, 0x03, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, 0x04, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, QAM_SY_SYNC_HWM__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + break; + case DRX_CONSTELLATION_QAM32: + case DRX_CONSTELLATION_QAM128: + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, 0x03, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, 0x05, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, 0x06, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EIO; + } /* switch */ + } + + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_MODE__A, QAM_LC_MODE__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /*! reset default val ! */ + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_RATE_LIMIT__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_LPF_FACTORP__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_LPF_FACTORI__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_MODE__A, 7, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB0__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB1__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB2__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB3__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB4__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB5__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB6__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB8__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB9__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB10__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB12__A, 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB15__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB16__A, 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB20__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB25__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_ADJ_SEL__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_ADJ_SEL__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_ADJ_SEL__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_GPIO__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* No more resets of the IQM, current standard correctly set => + now AGCs can be configured. */ + /* turn on IQMAF. It has to be in front of setAgc**() */ + rc = set_iqm_af(demod, true); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = adc_synchronization(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = init_agc(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_agc_if(demod, &(ext_attr->qam_if_agc_cfg), false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_agc_rf(demod, &(ext_attr->qam_rf_agc_cfg), false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + { + /* TODO fix this, store a struct drxj_cfg_afe_gain structure in struct drxj_data instead + of only the gain */ + struct drxj_cfg_afe_gain qam_pga_cfg = { DRX_STANDARD_ITU_B, 0 }; + + qam_pga_cfg.gain = ext_attr->qam_pga_cfg; + rc = ctrl_set_cfg_afe_gain(demod, &qam_pga_cfg); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + rc = ctrl_set_cfg_pre_saw(demod, &(ext_attr->qam_pre_saw_cfg)); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) { + if (ext_attr->standard == DRX_STANDARD_ITU_A) { + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_a_taps), ((u8 *)qam_a_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_a_taps), ((u8 *)qam_a_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else if (ext_attr->standard == DRX_STANDARD_ITU_B) { + switch (channel->constellation) { + case DRX_CONSTELLATION_QAM64: + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_b64_taps), ((u8 *)qam_b64_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_b64_taps), ((u8 *)qam_b64_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_CONSTELLATION_QAM256: + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_b256_taps), ((u8 *)qam_b256_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_b256_taps), ((u8 *)qam_b256_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EIO; + } + } else if (ext_attr->standard == DRX_STANDARD_ITU_C) { + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_c_taps), ((u8 *)qam_c_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_c_taps), ((u8 *)qam_c_taps), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* SETP 4: constellation specific setup */ + switch (channel->constellation) { + case DRX_CONSTELLATION_QAM16: + rc = set_qam16(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_CONSTELLATION_QAM32: + rc = set_qam32(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_CONSTELLATION_QAM64: + rc = set_qam64(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_CONSTELLATION_QAM128: + rc = set_qam128(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_CONSTELLATION_QAM256: + rc = set_qam256(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + return -EIO; + } /* switch */ + } + + if ((op & QAM_SET_OP_ALL)) { + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Mpeg output has to be in front of FEC active */ + rc = set_mpegtei_handling(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = bit_reverse_mpeg_output(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_mpeg_start_width(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + { + /* TODO: move to set_standard after hardware reset value problem is solved */ + /* Configure initial MPEG output */ + struct drx_cfg_mpeg_output cfg_mpeg_output; + + memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output)); + cfg_mpeg_output.enable_mpeg_output = true; + + rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + } + + if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) { + + /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_START; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ +static int ctrl_get_qam_sig_quality(struct drx_demod_instance *demod); + +static int qam_flip_spec(struct drx_demod_instance *demod, struct drx_channel *channel) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drxj_data *ext_attr = demod->my_ext_attr; + int rc; + u32 iqm_fs_rate_ofs = 0; + u32 iqm_fs_rate_lo = 0; + u16 qam_ctl_ena = 0; + u16 data = 0; + u16 equ_mode = 0; + u16 fsm_state = 0; + int i = 0; + int ofsofs = 0; + + /* Silence the controlling of lc, equ, and the acquisition state machine */ + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, &qam_ctl_ena, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, qam_ctl_ena & ~(SCU_RAM_QAM_CTL_ENA_ACQ__M | SCU_RAM_QAM_CTL_ENA_EQU__M | SCU_RAM_QAM_CTL_ENA_LC__M), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* freeze the frequency control loop */ + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CF__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CF1__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_atomic_read_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, &iqm_fs_rate_ofs, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_atomic_read_reg32(dev_addr, IQM_FS_RATE_LO__A, &iqm_fs_rate_lo, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ofsofs = iqm_fs_rate_lo - iqm_fs_rate_ofs; + iqm_fs_rate_ofs = ~iqm_fs_rate_ofs + 1; + iqm_fs_rate_ofs -= 2 * ofsofs; + + /* freeze dq/fq updating */ + rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_MODE__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + data = (data & 0xfff9); + rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* lc_cp / _ci / _ca */ + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CI__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_LC_EP__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_LA_FACTOR__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* flip the spec */ + rc = drxdap_fasi_write_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, iqm_fs_rate_ofs, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + ext_attr->iqm_fs_rate_ofs = iqm_fs_rate_ofs; + ext_attr->pos_image = (ext_attr->pos_image) ? false : true; + + /* freeze dq/fq updating */ + rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_MODE__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + equ_mode = data; + data = (data & 0xfff9); + rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + for (i = 0; i < 28; i++) { + rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_TAP_IM_EL0__A + (2 * i), &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_TAP_IM_EL0__A + (2 * i), -data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + for (i = 0; i < 24; i++) { + rc = drxj_dap_read_reg16(dev_addr, QAM_FQ_TAP_IM_EL0__A + (2 * i), &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_TAP_IM_EL0__A + (2 * i), -data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + data = equ_mode; + rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_STATE_TGT__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + i = 0; + while ((fsm_state != 4) && (i++ < 100)) { + rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_QAM_FSM_STATE__A, &fsm_state, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, (qam_ctl_ena | 0x0016), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; + +} + +#define NO_LOCK 0x0 +#define DEMOD_LOCKED 0x1 +#define SYNC_FLIPPED 0x2 +#define SPEC_MIRRORED 0x4 +/* +* \fn int qam64auto () +* \brief auto do sync pattern switching and mirroring. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \param tuner_freq_offset: tuner frequency offset. +* \param lock_status: pointer to lock status. +* \return int. +*/ +static int +qam64auto(struct drx_demod_instance *demod, + struct drx_channel *channel, + s32 tuner_freq_offset, enum drx_lock_status *lock_status) +{ + struct drxj_data *ext_attr = demod->my_ext_attr; + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drx39xxj_state *state = dev_addr->user_data; + struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache; + int rc; + u32 lck_state = NO_LOCK; + u32 start_time = 0; + u32 d_locked_time = 0; + u32 timeout_ofs = 0; + u16 data = 0; + + /* external attributes for storing acquired channel constellation */ + *lock_status = DRX_NOT_LOCKED; + start_time = jiffies_to_msecs(jiffies); + lck_state = NO_LOCK; + do { + rc = ctrl_lock_status(demod, lock_status); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + switch (lck_state) { + case NO_LOCK: + if (*lock_status == DRXJ_DEMOD_LOCK) { + rc = ctrl_get_qam_sig_quality(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (p->cnr.stat[0].svalue > 20800) { + lck_state = DEMOD_LOCKED; + /* some delay to see if fec_lock possible TODO find the right value */ + timeout_ofs += DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; /* see something, waiting longer */ + d_locked_time = jiffies_to_msecs(jiffies); + } + } + break; + case DEMOD_LOCKED: + if ((*lock_status == DRXJ_DEMOD_LOCK) && /* still demod_lock in 150ms */ + ((jiffies_to_msecs(jiffies) - d_locked_time) > + DRXJ_QAM_FEC_LOCK_WAITTIME)) { + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data | 0x1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + lck_state = SYNC_FLIPPED; + msleep(10); + } + break; + case SYNC_FLIPPED: + if (*lock_status == DRXJ_DEMOD_LOCK) { + if (channel->mirror == DRX_MIRROR_AUTO) { + /* flip sync pattern back */ + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data & 0xFFFE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* flip spectrum */ + ext_attr->mirror = DRX_MIRROR_YES; + rc = qam_flip_spec(demod, channel); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + lck_state = SPEC_MIRRORED; + /* reset timer TODO: still need 500ms? */ + start_time = d_locked_time = + jiffies_to_msecs(jiffies); + timeout_ofs = 0; + } else { /* no need to wait lock */ + + start_time = + jiffies_to_msecs(jiffies) - + DRXJ_QAM_MAX_WAITTIME - timeout_ofs; + } + } + break; + case SPEC_MIRRORED: + if ((*lock_status == DRXJ_DEMOD_LOCK) && /* still demod_lock in 150ms */ + ((jiffies_to_msecs(jiffies) - d_locked_time) > + DRXJ_QAM_FEC_LOCK_WAITTIME)) { + rc = ctrl_get_qam_sig_quality(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (p->cnr.stat[0].svalue > 20800) { + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data | 0x1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* no need to wait lock */ + start_time = + jiffies_to_msecs(jiffies) - + DRXJ_QAM_MAX_WAITTIME - timeout_ofs; + } + } + break; + default: + break; + } + msleep(10); + } while + ((*lock_status != DRX_LOCKED) && + (*lock_status != DRX_NEVER_LOCK) && + ((jiffies_to_msecs(jiffies) - start_time) < + (DRXJ_QAM_MAX_WAITTIME + timeout_ofs)) + ); + /* Returning control to application ... */ + + return 0; +rw_error: + return rc; +} + +/* +* \fn int qam256auto () +* \brief auto do sync pattern switching and mirroring. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \param tuner_freq_offset: tuner frequency offset. +* \param lock_status: pointer to lock status. +* \return int. +*/ +static int +qam256auto(struct drx_demod_instance *demod, + struct drx_channel *channel, + s32 tuner_freq_offset, enum drx_lock_status *lock_status) +{ + struct drxj_data *ext_attr = demod->my_ext_attr; + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drx39xxj_state *state = dev_addr->user_data; + struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache; + int rc; + u32 lck_state = NO_LOCK; + u32 start_time = 0; + u32 d_locked_time = 0; + u32 timeout_ofs = DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; + + /* external attributes for storing acquired channel constellation */ + *lock_status = DRX_NOT_LOCKED; + start_time = jiffies_to_msecs(jiffies); + lck_state = NO_LOCK; + do { + rc = ctrl_lock_status(demod, lock_status); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + switch (lck_state) { + case NO_LOCK: + if (*lock_status == DRXJ_DEMOD_LOCK) { + rc = ctrl_get_qam_sig_quality(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (p->cnr.stat[0].svalue > 26800) { + lck_state = DEMOD_LOCKED; + timeout_ofs += DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; /* see something, wait longer */ + d_locked_time = jiffies_to_msecs(jiffies); + } + } + break; + case DEMOD_LOCKED: + if (*lock_status == DRXJ_DEMOD_LOCK) { + if ((channel->mirror == DRX_MIRROR_AUTO) && + ((jiffies_to_msecs(jiffies) - d_locked_time) > + DRXJ_QAM_FEC_LOCK_WAITTIME)) { + ext_attr->mirror = DRX_MIRROR_YES; + rc = qam_flip_spec(demod, channel); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + lck_state = SPEC_MIRRORED; + /* reset timer TODO: still need 300ms? */ + start_time = jiffies_to_msecs(jiffies); + timeout_ofs = -DRXJ_QAM_MAX_WAITTIME / 2; + } + } + break; + case SPEC_MIRRORED: + break; + default: + break; + } + msleep(10); + } while + ((*lock_status < DRX_LOCKED) && + (*lock_status != DRX_NEVER_LOCK) && + ((jiffies_to_msecs(jiffies) - start_time) < + (DRXJ_QAM_MAX_WAITTIME + timeout_ofs))); + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_qam_channel () +* \brief Set QAM channel according to the requested constellation. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \return int. +*/ +static int +set_qam_channel(struct drx_demod_instance *demod, + struct drx_channel *channel, s32 tuner_freq_offset) +{ + struct drxj_data *ext_attr = NULL; + int rc; + enum drx_lock_status lock_status = DRX_NOT_LOCKED; + bool auto_flag = false; + + /* external attributes for storing acquired channel constellation */ + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + /* set QAM channel constellation */ + switch (channel->constellation) { + case DRX_CONSTELLATION_QAM16: + case DRX_CONSTELLATION_QAM32: + case DRX_CONSTELLATION_QAM128: + return -EINVAL; + case DRX_CONSTELLATION_QAM64: + case DRX_CONSTELLATION_QAM256: + if (ext_attr->standard != DRX_STANDARD_ITU_B) + return -EINVAL; + + ext_attr->constellation = channel->constellation; + if (channel->mirror == DRX_MIRROR_AUTO) + ext_attr->mirror = DRX_MIRROR_NO; + else + ext_attr->mirror = channel->mirror; + + rc = set_qam(demod, channel, tuner_freq_offset, QAM_SET_OP_ALL); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (channel->constellation == DRX_CONSTELLATION_QAM64) + rc = qam64auto(demod, channel, tuner_freq_offset, + &lock_status); + else + rc = qam256auto(demod, channel, tuner_freq_offset, + &lock_status); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_CONSTELLATION_AUTO: /* for channel scan */ + if (ext_attr->standard == DRX_STANDARD_ITU_B) { + u16 qam_ctl_ena = 0; + + auto_flag = true; + + /* try to lock default QAM constellation: QAM256 */ + channel->constellation = DRX_CONSTELLATION_QAM256; + ext_attr->constellation = DRX_CONSTELLATION_QAM256; + if (channel->mirror == DRX_MIRROR_AUTO) + ext_attr->mirror = DRX_MIRROR_NO; + else + ext_attr->mirror = channel->mirror; + rc = set_qam(demod, channel, tuner_freq_offset, + QAM_SET_OP_ALL); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = qam256auto(demod, channel, tuner_freq_offset, + &lock_status); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (lock_status >= DRX_LOCKED) { + channel->constellation = DRX_CONSTELLATION_AUTO; + break; + } + + /* QAM254 not locked. Try QAM64 constellation */ + channel->constellation = DRX_CONSTELLATION_QAM64; + ext_attr->constellation = DRX_CONSTELLATION_QAM64; + if (channel->mirror == DRX_MIRROR_AUTO) + ext_attr->mirror = DRX_MIRROR_NO; + else + ext_attr->mirror = channel->mirror; + + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_CTL_ENA__A, + &qam_ctl_ena, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_CTL_ENA__A, + qam_ctl_ena & ~SCU_RAM_QAM_CTL_ENA_ACQ__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_FSM_STATE_TGT__A, + 0x2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* force to rate hunting */ + + rc = set_qam(demod, channel, tuner_freq_offset, + QAM_SET_OP_CONSTELLATION); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_CTL_ENA__A, + qam_ctl_ena, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = qam64auto(demod, channel, tuner_freq_offset, + &lock_status); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + channel->constellation = DRX_CONSTELLATION_AUTO; + } else if (ext_attr->standard == DRX_STANDARD_ITU_C) { + u16 qam_ctl_ena = 0; + + channel->constellation = DRX_CONSTELLATION_QAM64; + ext_attr->constellation = DRX_CONSTELLATION_QAM64; + auto_flag = true; + + if (channel->mirror == DRX_MIRROR_AUTO) + ext_attr->mirror = DRX_MIRROR_NO; + else + ext_attr->mirror = channel->mirror; + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_CTL_ENA__A, + &qam_ctl_ena, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_CTL_ENA__A, + qam_ctl_ena & ~SCU_RAM_QAM_CTL_ENA_ACQ__M, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_FSM_STATE_TGT__A, + 0x2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* force to rate hunting */ + + rc = set_qam(demod, channel, tuner_freq_offset, + QAM_SET_OP_CONSTELLATION); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, + SCU_RAM_QAM_CTL_ENA__A, + qam_ctl_ena, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = qam64auto(demod, channel, tuner_freq_offset, + &lock_status); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + channel->constellation = DRX_CONSTELLATION_AUTO; + } else { + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +rw_error: + /* restore starting value */ + if (auto_flag) + channel->constellation = DRX_CONSTELLATION_AUTO; + return rc; +} + +/*============================================================================*/ + +/* +* \fn static short get_qamrs_err_count(struct i2c_device_addr *dev_addr) +* \brief Get RS error count in QAM mode (used for post RS BER calculation) +* \return Error code +* +* precondition: measurement period & measurement prescale must be set +* +*/ +static int +get_qamrs_err_count(struct i2c_device_addr *dev_addr, + struct drxjrs_errors *rs_errors) +{ + int rc; + u16 nr_bit_errors = 0, + nr_symbol_errors = 0, + nr_packet_errors = 0, nr_failures = 0, nr_snc_par_fail_count = 0; + + /* check arguments */ + if (dev_addr == NULL) + return -EINVAL; + + /* all reported errors are received in the */ + /* most recently finished measurement period */ + /* no of pre RS bit errors */ + rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &nr_bit_errors, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* no of symbol errors */ + rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_SYMBOL_ERRORS__A, &nr_symbol_errors, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* no of packet errors */ + rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_PACKET_ERRORS__A, &nr_packet_errors, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* no of failures to decode */ + rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_FAILURES__A, &nr_failures, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* no of post RS bit erros */ + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_FAIL_COUNT__A, &nr_snc_par_fail_count, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* TODO: NOTE */ + /* These register values are fetched in non-atomic fashion */ + /* It is possible that the read values contain unrelated information */ + + rs_errors->nr_bit_errors = nr_bit_errors & FEC_RS_NR_BIT_ERRORS__M; + rs_errors->nr_symbol_errors = nr_symbol_errors & FEC_RS_NR_SYMBOL_ERRORS__M; + rs_errors->nr_packet_errors = nr_packet_errors & FEC_RS_NR_PACKET_ERRORS__M; + rs_errors->nr_failures = nr_failures & FEC_RS_NR_FAILURES__M; + rs_errors->nr_snc_par_fail_count = + nr_snc_par_fail_count & FEC_OC_SNC_FAIL_COUNT__M; + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* + * \fn int get_sig_strength() + * \brief Retrieve signal strength for VSB and QAM. + * \param demod Pointer to demod instance + * \param u16-t Pointer to signal strength data; range 0, .. , 100. + * \return int. + * \retval 0 sig_strength contains valid data. + * \retval -EINVAL sig_strength is NULL. + * \retval -EIO Erroneous data, sig_strength contains invalid data. + */ +#define DRXJ_AGC_TOP 0x2800 +#define DRXJ_AGC_SNS 0x1600 +#define DRXJ_RFAGC_MAX 0x3fff +#define DRXJ_RFAGC_MIN 0x800 + +static int get_sig_strength(struct drx_demod_instance *demod, u16 *sig_strength) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + u16 rf_gain = 0; + u16 if_gain = 0; + u16 if_agc_sns = 0; + u16 if_agc_top = 0; + u16 rf_agc_max = 0; + u16 rf_agc_min = 0; + + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_AGC_IF__A, &if_gain, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if_gain &= IQM_AF_AGC_IF__M; + rc = drxj_dap_read_reg16(dev_addr, IQM_AF_AGC_RF__A, &rf_gain, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rf_gain &= IQM_AF_AGC_RF__M; + + if_agc_sns = DRXJ_AGC_SNS; + if_agc_top = DRXJ_AGC_TOP; + rf_agc_max = DRXJ_RFAGC_MAX; + rf_agc_min = DRXJ_RFAGC_MIN; + + if (if_gain > if_agc_top) { + if (rf_gain > rf_agc_max) + *sig_strength = 100; + else if (rf_gain > rf_agc_min) { + if (rf_agc_max == rf_agc_min) { + pr_err("error: rf_agc_max == rf_agc_min\n"); + return -EIO; + } + *sig_strength = + 75 + 25 * (rf_gain - rf_agc_min) / (rf_agc_max - + rf_agc_min); + } else + *sig_strength = 75; + } else if (if_gain > if_agc_sns) { + if (if_agc_top == if_agc_sns) { + pr_err("error: if_agc_top == if_agc_sns\n"); + return -EIO; + } + *sig_strength = + 20 + 55 * (if_gain - if_agc_sns) / (if_agc_top - if_agc_sns); + } else { + if (!if_agc_sns) { + pr_err("error: if_agc_sns is zero!\n"); + return -EIO; + } + *sig_strength = (20 * if_gain / if_agc_sns); + } + + if (*sig_strength <= 7) + *sig_strength = 0; + + return 0; +rw_error: + return rc; +} + +/* +* \fn int ctrl_get_qam_sig_quality() +* \brief Retrieve QAM signal quality from device. +* \param devmod Pointer to demodulator instance. +* \param sig_quality Pointer to signal quality data. +* \return int. +* \retval 0 sig_quality contains valid data. +* \retval -EINVAL sig_quality is NULL. +* \retval -EIO Erroneous data, sig_quality contains invalid data. + +* Pre-condition: Device must be started and in lock. +*/ +static int +ctrl_get_qam_sig_quality(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drxj_data *ext_attr = demod->my_ext_attr; + struct drx39xxj_state *state = dev_addr->user_data; + struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache; + struct drxjrs_errors measuredrs_errors = { 0, 0, 0, 0, 0 }; + enum drx_modulation constellation = ext_attr->constellation; + int rc; + + u32 pre_bit_err_rs = 0; /* pre RedSolomon Bit Error Rate */ + u32 post_bit_err_rs = 0; /* post RedSolomon Bit Error Rate */ + u32 pkt_errs = 0; /* no of packet errors in RS */ + u16 qam_sl_err_power = 0; /* accumulated error between raw and sliced symbols */ + u16 qsym_err_vd = 0; /* quadrature symbol errors in QAM_VD */ + u16 fec_oc_period = 0; /* SNC sync failure measurement period */ + u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */ + u16 fec_rs_period = 0; /* Value for corresponding I2C register */ + /* calculation constants */ + u32 rs_bit_cnt = 0; /* RedSolomon Bit Count */ + u32 qam_sl_sig_power = 0; /* used for MER, depends of QAM constellation */ + /* intermediate results */ + u32 e = 0; /* exponent value used for QAM BER/SER */ + u32 m = 0; /* mantisa value used for QAM BER/SER */ + u32 ber_cnt = 0; /* BER count */ + /* signal quality info */ + u32 qam_sl_mer = 0; /* QAM MER */ + u32 qam_pre_rs_ber = 0; /* Pre RedSolomon BER */ + u32 qam_post_rs_ber = 0; /* Post RedSolomon BER */ + u32 qam_vd_ser = 0; /* ViterbiDecoder SER */ + u16 qam_vd_prescale = 0; /* Viterbi Measurement Prescale */ + u16 qam_vd_period = 0; /* Viterbi Measurement period */ + u32 vd_bit_cnt = 0; /* ViterbiDecoder Bit Count */ + + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* read the physical registers */ + /* Get the RS error data */ + rc = get_qamrs_err_count(dev_addr, &measuredrs_errors); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* get the register value needed for MER */ + rc = drxj_dap_read_reg16(dev_addr, QAM_SL_ERR_POWER__A, &qam_sl_err_power, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* get the register value needed for post RS BER */ + rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_FAIL_PERIOD__A, &fec_oc_period, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* get constants needed for signal quality calculation */ + fec_rs_period = ext_attr->fec_rs_period; + fec_rs_prescale = ext_attr->fec_rs_prescale; + rs_bit_cnt = fec_rs_period * fec_rs_prescale * ext_attr->fec_rs_plen; + qam_vd_period = ext_attr->qam_vd_period; + qam_vd_prescale = ext_attr->qam_vd_prescale; + vd_bit_cnt = qam_vd_period * qam_vd_prescale * ext_attr->fec_vd_plen; + + /* DRXJ_QAM_SL_SIG_POWER_QAMxxx * 4 */ + switch (constellation) { + case DRX_CONSTELLATION_QAM16: + qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM16 << 2; + break; + case DRX_CONSTELLATION_QAM32: + qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM32 << 2; + break; + case DRX_CONSTELLATION_QAM64: + qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM64 << 2; + break; + case DRX_CONSTELLATION_QAM128: + qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM128 << 2; + break; + case DRX_CONSTELLATION_QAM256: + qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM256 << 2; + break; + default: + rc = -EIO; + goto rw_error; + } + + /* ------------------------------ */ + /* MER Calculation */ + /* ------------------------------ */ + /* MER is good if it is above 27.5 for QAM256 or 21.5 for QAM64 */ + + /* 10.0*log10(qam_sl_sig_power * 4.0 / qam_sl_err_power); */ + if (qam_sl_err_power == 0) + qam_sl_mer = 0; + else + qam_sl_mer = log1_times100(qam_sl_sig_power) - log1_times100((u32)qam_sl_err_power); + + /* ----------------------------------------- */ + /* Pre Viterbi Symbol Error Rate Calculation */ + /* ----------------------------------------- */ + /* pre viterbi SER is good if it is below 0.025 */ + + /* get the register value */ + /* no of quadrature symbol errors */ + rc = drxj_dap_read_reg16(dev_addr, QAM_VD_NR_QSYM_ERRORS__A, &qsym_err_vd, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Extract the Exponent and the Mantisa */ + /* of number of quadrature symbol errors */ + e = (qsym_err_vd & QAM_VD_NR_QSYM_ERRORS_EXP__M) >> + QAM_VD_NR_QSYM_ERRORS_EXP__B; + m = (qsym_err_vd & QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__M) >> + QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__B; + + if ((m << e) >> 3 > 549752) + qam_vd_ser = 500000 * vd_bit_cnt * ((e > 2) ? 1 : 8) / 8; + else + qam_vd_ser = m << ((e > 2) ? (e - 3) : e); + + /* --------------------------------------- */ + /* pre and post RedSolomon BER Calculation */ + /* --------------------------------------- */ + /* pre RS BER is good if it is below 3.5e-4 */ + + /* get the register values */ + pre_bit_err_rs = (u32) measuredrs_errors.nr_bit_errors; + pkt_errs = post_bit_err_rs = (u32) measuredrs_errors.nr_snc_par_fail_count; + + /* Extract the Exponent and the Mantisa of the */ + /* pre Reed-Solomon bit error count */ + e = (pre_bit_err_rs & FEC_RS_NR_BIT_ERRORS_EXP__M) >> + FEC_RS_NR_BIT_ERRORS_EXP__B; + m = (pre_bit_err_rs & FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M) >> + FEC_RS_NR_BIT_ERRORS_FIXED_MANT__B; + + ber_cnt = m << e; + + /*qam_pre_rs_ber = frac_times1e6( ber_cnt, rs_bit_cnt ); */ + if (m > (rs_bit_cnt >> (e + 1)) || (rs_bit_cnt >> e) == 0) + qam_pre_rs_ber = 500000 * rs_bit_cnt >> e; + else + qam_pre_rs_ber = ber_cnt; + + /* post RS BER = 1000000* (11.17 * FEC_OC_SNC_FAIL_COUNT__A) / */ + /* (1504.0 * FEC_OC_SNC_FAIL_PERIOD__A) */ + /* + => c = (1000000*100*11.17)/1504 = + post RS BER = (( c* FEC_OC_SNC_FAIL_COUNT__A) / + (100 * FEC_OC_SNC_FAIL_PERIOD__A) + *100 and /100 is for more precision. + => (20 bits * 12 bits) /(16 bits * 7 bits) => safe in 32 bits computation + + Precision errors still possible. + */ + if (!fec_oc_period) { + qam_post_rs_ber = 0xFFFFFFFF; + } else { + e = post_bit_err_rs * 742686; + m = fec_oc_period * 100; + qam_post_rs_ber = e / m; + } + + /* fill signal quality data structure */ + p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + + p->cnr.stat[0].svalue = ((u16) qam_sl_mer) * 100; + if (ext_attr->standard == DRX_STANDARD_ITU_B) { + p->pre_bit_error.stat[0].uvalue += qam_vd_ser; + p->pre_bit_count.stat[0].uvalue += vd_bit_cnt * ((e > 2) ? 1 : 8) / 8; + } else { + p->pre_bit_error.stat[0].uvalue += qam_pre_rs_ber; + p->pre_bit_count.stat[0].uvalue += rs_bit_cnt >> e; + } + + p->post_bit_error.stat[0].uvalue += qam_post_rs_ber; + p->post_bit_count.stat[0].uvalue += rs_bit_cnt >> e; + + p->block_error.stat[0].uvalue += pkt_errs; + +#ifdef DRXJ_SIGNAL_ACCUM_ERR + rc = get_acc_pkt_err(demod, &sig_quality->packet_error); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } +#endif + + return 0; +rw_error: + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return rc; +} + +#endif /* #ifndef DRXJ_VSB_ONLY */ + +/*============================================================================*/ +/*== END QAM DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ + +/*============================================================================*/ +/*============================================================================*/ +/*== ATV DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ +/*============================================================================*/ + +/* + Implementation notes. + + NTSC/FM AGCs + + Four AGCs are used for NTSC: + (1) RF (used to attenuate the input signal in case of to much power) + (2) IF (used to attenuate the input signal in case of to much power) + (3) Video AGC (used to amplify the output signal in case input to low) + (4) SIF AGC (used to amplify the output signal in case input to low) + + Video AGC is coupled to RF and IF. SIF AGC is not coupled. It is assumed + that the coupling between Video AGC and the RF and IF AGCs also works in + favor of the SIF AGC. + + Three AGCs are used for FM: + (1) RF (used to attenuate the input signal in case of to much power) + (2) IF (used to attenuate the input signal in case of to much power) + (3) SIF AGC (used to amplify the output signal in case input to low) + + The SIF AGC is now coupled to the RF/IF AGCs. + The SIF AGC is needed for both SIF output and the internal SIF signal to + the AUD block. + + RF and IF AGCs DACs are part of AFE, Video and SIF AGC DACs are part of + the ATV block. The AGC control algorithms are all implemented in + microcode. + + ATV SETTINGS + + (Shadow settings will not be used for now, they will be implemented + later on because of the schedule) + + Several HW/SCU "settings" can be used for ATV. The standard selection + will reset most of these settings. To avoid that the end user application + has to perform these settings each time the ATV or FM standards is + selected the driver will shadow these settings. This enables the end user + to perform the settings only once after a drx_open(). The driver must + write the shadow settings to HW/SCU in case: + ( setstandard FM/ATV) || + ( settings have changed && FM/ATV standard is active) + The shadow settings will be stored in the device specific data container. + A set of flags will be defined to flag changes in shadow settings. + A routine will be implemented to write all changed shadow settings to + HW/SCU. + + The "settings" will consist of: AGC settings, filter settings etc. + + Disadvantage of use of shadow settings: + Direct changes in HW/SCU registers will not be reflected in the + shadow settings and these changes will be overwritten during a next + update. This can happen during evaluation. This will not be a problem + for normal customer usage. +*/ +/* -------------------------------------------------------------------------- */ + +/* +* \fn int power_down_atv () +* \brief Power down ATV. +* \param demod instance of demodulator +* \param standard either NTSC or FM (sub strandard for ATV ) +* \return int. +* +* Stops and thus resets ATV and IQM block +* SIF and CVBS ADC are powered down +* Calls audio power down +*/ +static int +power_down_atv(struct drx_demod_instance *demod, enum drx_standard standard, bool primary) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drxjscu_cmd cmd_scu = { /* command */ 0, + /* parameter_len */ 0, + /* result_len */ 0, + /* *parameter */ NULL, + /* *result */ NULL + }; + int rc; + u16 cmd_result = 0; + + /* ATV NTSC */ + + /* Stop ATV SCU (will reset ATV and IQM hardware */ + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_ATV | + SCU_RAM_COMMAND_CMD_DEMOD_STOP; + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 1; + cmd_scu.parameter = NULL; + cmd_scu.result = &cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Disable ATV outputs (ATV reset enables CVBS, undo this) */ + rc = drxj_dap_write_reg16(dev_addr, ATV_TOP_STDBY__A, (ATV_TOP_STDBY_SIF_STDBY_STANDBY & (~ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE)), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, ATV_COMM_EXEC__A, ATV_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (primary) { + rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_iqm_af(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } else { + rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + rc = power_down_aud(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \brief Power up AUD. +* \param demod instance of demodulator +* \return int. +* +*/ +static int power_down_aud(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + int rc; + + dev_addr = (struct i2c_device_addr *)demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + rc = drxj_dap_write_reg16(dev_addr, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + ext_attr->aud_data.audio_is_active = false; + + return 0; +rw_error: + return rc; +} + +/* +* \fn int set_orx_nsu_aox() +* \brief Configure OrxNsuAox for OOB +* \param demod instance of demodulator. +* \param active +* \return int. +*/ +static int set_orx_nsu_aox(struct drx_demod_instance *demod, bool active) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + u16 data = 0; + + /* Configure NSU_AOX */ + rc = drxj_dap_read_reg16(dev_addr, ORX_NSU_AOX_STDBY_W__A, &data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (!active) + data &= ((~ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON)); + else + data |= (ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON); + rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_STDBY_W__A, data, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + return 0; +rw_error: + return rc; +} + +/* +* \fn int ctrl_set_oob() +* \brief Set OOB channel to be used. +* \param demod instance of demodulator +* \param oob_param OOB parameters for channel setting. +* \frequency should be in KHz +* \return int. +* +* Accepts only. Returns error otherwise. +* Demapper value is written after scu_command START +* because START command causes COMM_EXEC transition +* from 0 to 1 which causes all registers to be +* overwritten with initial value +* +*/ + +/* Nyquist filter impulse response */ +#define IMPULSE_COSINE_ALPHA_0_3 {-3, -4, -1, 6, 10, 7, -5, -20, -25, -10, 29, 79, 123, 140} /*sqrt raised-cosine filter with alpha=0.3 */ +#define IMPULSE_COSINE_ALPHA_0_5 { 2, 0, -2, -2, 2, 5, 2, -10, -20, -14, 20, 74, 125, 145} /*sqrt raised-cosine filter with alpha=0.5 */ +#define IMPULSE_COSINE_ALPHA_RO_0_5 { 0, 0, 1, 2, 3, 0, -7, -15, -16, 0, 34, 77, 114, 128} /*full raised-cosine filter with alpha=0.5 (receiver only) */ + +/* Coefficients for the nyquist filter (total: 27 taps) */ +#define NYQFILTERLEN 27 + +static int ctrl_set_oob(struct drx_demod_instance *demod, struct drxoob *oob_param) +{ + int rc; + s32 freq = 0; /* KHz */ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + u16 i = 0; + bool mirror_freq_spect_oob = false; + u16 trk_filter_value = 0; + struct drxjscu_cmd scu_cmd; + u16 set_param_parameters[3]; + u16 cmd_result[2] = { 0, 0 }; + s16 nyquist_coeffs[4][(NYQFILTERLEN + 1) / 2] = { + IMPULSE_COSINE_ALPHA_0_3, /* Target Mode 0 */ + IMPULSE_COSINE_ALPHA_0_3, /* Target Mode 1 */ + IMPULSE_COSINE_ALPHA_0_5, /* Target Mode 2 */ + IMPULSE_COSINE_ALPHA_RO_0_5 /* Target Mode 3 */ + }; + u8 mode_val[4] = { 2, 2, 0, 1 }; + u8 pfi_coeffs[4][6] = { + {DRXJ_16TO8(-92), DRXJ_16TO8(-108), DRXJ_16TO8(100)}, /* TARGET_MODE = 0: PFI_A = -23/32; PFI_B = -54/32; PFI_C = 25/32; fg = 0.5 MHz (Att=26dB) */ + {DRXJ_16TO8(-64), DRXJ_16TO8(-80), DRXJ_16TO8(80)}, /* TARGET_MODE = 1: PFI_A = -16/32; PFI_B = -40/32; PFI_C = 20/32; fg = 1.0 MHz (Att=28dB) */ + {DRXJ_16TO8(-80), DRXJ_16TO8(-98), DRXJ_16TO8(92)}, /* TARGET_MODE = 2, 3: PFI_A = -20/32; PFI_B = -49/32; PFI_C = 23/32; fg = 0.8 MHz (Att=25dB) */ + {DRXJ_16TO8(-80), DRXJ_16TO8(-98), DRXJ_16TO8(92)} /* TARGET_MODE = 2, 3: PFI_A = -20/32; PFI_B = -49/32; PFI_C = 23/32; fg = 0.8 MHz (Att=25dB) */ + }; + u16 mode_index; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + mirror_freq_spect_oob = ext_attr->mirror_freq_spect_oob; + + /* Check parameters */ + if (oob_param == NULL) { + /* power off oob module */ + scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB + | SCU_RAM_COMMAND_CMD_DEMOD_STOP; + scu_cmd.parameter_len = 0; + scu_cmd.result_len = 1; + scu_cmd.result = cmd_result; + rc = scu_command(dev_addr, &scu_cmd); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_orx_nsu_aox(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + ext_attr->oob_power_on = false; + return 0; + } + + freq = oob_param->frequency; + if ((freq < 70000) || (freq > 130000)) + return -EIO; + freq = (freq - 50000) / 50; + + { + u16 index = 0; + u16 remainder = 0; + u16 *trk_filtercfg = ext_attr->oob_trk_filter_cfg; + + index = (u16) ((freq - 400) / 200); + remainder = (u16) ((freq - 400) % 200); + trk_filter_value = + trk_filtercfg[index] - (trk_filtercfg[index] - + trk_filtercfg[index + + 1]) / 10 * remainder / + 20; + } + + /********/ + /* Stop */ + /********/ + rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB + | SCU_RAM_COMMAND_CMD_DEMOD_STOP; + scu_cmd.parameter_len = 0; + scu_cmd.result_len = 1; + scu_cmd.result = cmd_result; + rc = scu_command(dev_addr, &scu_cmd); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /********/ + /* Reset */ + /********/ + scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB + | SCU_RAM_COMMAND_CMD_DEMOD_RESET; + scu_cmd.parameter_len = 0; + scu_cmd.result_len = 1; + scu_cmd.result = cmd_result; + rc = scu_command(dev_addr, &scu_cmd); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /**********/ + /* SET_ENV */ + /**********/ + /* set frequency, spectrum inversion and data rate */ + scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB + | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV; + scu_cmd.parameter_len = 3; + /* 1-data rate;2-frequency */ + switch (oob_param->standard) { + case DRX_OOB_MODE_A: + if ( + /* signal is transmitted inverted */ + ((oob_param->spectrum_inverted == true) && + /* and tuner is not mirroring the signal */ + (!mirror_freq_spect_oob)) | + /* or */ + /* signal is transmitted noninverted */ + ((oob_param->spectrum_inverted == false) && + /* and tuner is mirroring the signal */ + (mirror_freq_spect_oob)) + ) + set_param_parameters[0] = + SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC; + else + set_param_parameters[0] = + SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC; + break; + case DRX_OOB_MODE_B_GRADE_A: + if ( + /* signal is transmitted inverted */ + ((oob_param->spectrum_inverted == true) && + /* and tuner is not mirroring the signal */ + (!mirror_freq_spect_oob)) | + /* or */ + /* signal is transmitted noninverted */ + ((oob_param->spectrum_inverted == false) && + /* and tuner is mirroring the signal */ + (mirror_freq_spect_oob)) + ) + set_param_parameters[0] = + SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_INVSPEC; + else + set_param_parameters[0] = + SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_REGSPEC; + break; + case DRX_OOB_MODE_B_GRADE_B: + default: + if ( + /* signal is transmitted inverted */ + ((oob_param->spectrum_inverted == true) && + /* and tuner is not mirroring the signal */ + (!mirror_freq_spect_oob)) | + /* or */ + /* signal is transmitted noninverted */ + ((oob_param->spectrum_inverted == false) && + /* and tuner is mirroring the signal */ + (mirror_freq_spect_oob)) + ) + set_param_parameters[0] = + SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_INVSPEC; + else + set_param_parameters[0] = + SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_REGSPEC; + break; + } + set_param_parameters[1] = (u16) (freq & 0xFFFF); + set_param_parameters[2] = trk_filter_value; + scu_cmd.parameter = set_param_parameters; + scu_cmd.result_len = 1; + scu_cmd.result = cmd_result; + mode_index = mode_val[(set_param_parameters[0] & 0xC0) >> 6]; + rc = scu_command(dev_addr, &scu_cmd); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + 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; + } /* Write magic word to enable pdr reg write */ + rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_OOB_CRX_CFG__A, OOB_CRX_DRIVE_STRENGTH << SIO_PDR_OOB_CRX_CFG_DRIVE__B | 0x03 << SIO_PDR_OOB_CRX_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_OOB_DRX_CFG__A, OOB_DRX_DRIVE_STRENGTH << SIO_PDR_OOB_DRX_CFG_DRIVE__B | 0x03 << SIO_PDR_OOB_DRX_CFG_MODE__B, 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, 0x0000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } /* Write magic word to disable pdr reg write */ + + rc = drxj_dap_write_reg16(dev_addr, ORX_TOP_COMM_KEY__A, 0, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_AAG_LEN_W__A, 16000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_AAG_THR_W__A, 40, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* ddc */ + rc = drxj_dap_write_reg16(dev_addr, ORX_DDC_OFO_SET_W__A, ORX_DDC_OFO_SET_W__PRE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* nsu */ + rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_LOPOW_W__A, ext_attr->oob_lo_pow, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* initialization for target mode */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TARGET_MODE__A, SCU_RAM_ORX_TARGET_MODE_2048KBPS_SQRT, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FREQ_GAIN_CORR__A, SCU_RAM_ORX_FREQ_GAIN_CORR_2048KBPS, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Reset bits for timing and freq. recovery */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_CPH__A, 0x0001, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_CTI__A, 0x0002, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_KRN__A, 0x0004, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_KRP__A, 0x0008, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* AGN_LOCK = {2048>>3, -2048, 8, -8, 0, 1}; */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_TH__A, 2048 >> 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_TOTH__A, (u16)(-2048), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_ONLOCK_TTH__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_UNLOCK_TTH__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_MASK__A, 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* DGN_LOCK = {10, -2048, 8, -8, 0, 1<<1}; */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_TH__A, 10, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_TOTH__A, (u16)(-2048), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_ONLOCK_TTH__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_UNLOCK_TTH__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_MASK__A, 1 << 1, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* FRQ_LOCK = {15,-2048, 8, -8, 0, 1<<2}; */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_TH__A, 17, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_TOTH__A, (u16)(-2048), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_ONLOCK_TTH__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_UNLOCK_TTH__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_MASK__A, 1 << 2, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* PHA_LOCK = {5000, -2048, 8, -8, 0, 1<<3}; */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_TH__A, 3000, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_TOTH__A, (u16)(-2048), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_ONLOCK_TTH__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_UNLOCK_TTH__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_MASK__A, 1 << 3, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* TIM_LOCK = {300, -2048, 8, -8, 0, 1<<4}; */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_TH__A, 400, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_TOTH__A, (u16)(-2048), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_ONLOCK_TTH__A, 8, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_UNLOCK_TTH__A, (u16)(-8), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_MASK__A, 1 << 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* EQU_LOCK = {20, -2048, 8, -8, 0, 1<<5}; */ + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_TH__A, 20, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_TOTH__A, (u16)(-2048), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_ONLOCK_TTH__A, 4, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_UNLOCK_TTH__A, (u16)(-4), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_MASK__A, 1 << 5, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* PRE-Filter coefficients (PFI) */ + rc = drxdap_fasi_write_block(dev_addr, ORX_FWP_PFI_A_W__A, sizeof(pfi_coeffs[mode_index]), ((u8 *)pfi_coeffs[mode_index]), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_TOP_MDE_W__A, mode_index, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* NYQUIST-Filter coefficients (NYQ) */ + for (i = 0; i < (NYQFILTERLEN + 1) / 2; i++) { + rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_ADR_W__A, i, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_COF_RW__A, nyquist_coeffs[mode_index][i], 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_ADR_W__A, 31, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /********/ + /* Start */ + /********/ + scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB + | SCU_RAM_COMMAND_CMD_DEMOD_START; + scu_cmd.parameter_len = 0; + scu_cmd.result_len = 1; + scu_cmd.result = cmd_result; + rc = scu_command(dev_addr, &scu_cmd); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = set_orx_nsu_aox(demod, true); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_STHR_W__A, ext_attr->oob_pre_saw, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + ext_attr->oob_power_on = true; + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ +/*== END OOB DATAPATH FUNCTIONS ==*/ +/*============================================================================*/ + +/*============================================================================= + ===== MC command related functions ========================================== + ===========================================================================*/ + +/*============================================================================= + ===== ctrl_set_channel() ========================================================== + ===========================================================================*/ +/* +* \fn int ctrl_set_channel() +* \brief Select a new transmission channel. +* \param demod instance of demod. +* \param channel Pointer to channel data. +* \return int. +* +* In case the tuner module is not used and in case of NTSC/FM the pogrammer +* must tune the tuner to the centre frequency of the NTSC/FM channel. +* +*/ +static int +ctrl_set_channel(struct drx_demod_instance *demod, struct drx_channel *channel) +{ + int rc; + s32 tuner_freq_offset = 0; + struct drxj_data *ext_attr = NULL; + struct i2c_device_addr *dev_addr = NULL; + enum drx_standard standard = DRX_STANDARD_UNKNOWN; +#ifndef DRXJ_VSB_ONLY + u32 min_symbol_rate = 0; + u32 max_symbol_rate = 0; + int bandwidth_temp = 0; + int bandwidth = 0; +#endif + /*== check arguments ======================================================*/ + if ((demod == NULL) || (channel == NULL)) + return -EINVAL; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + standard = ext_attr->standard; + + /* check valid standards */ + switch (standard) { + case DRX_STANDARD_8VSB: +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: +#endif /* DRXJ_VSB_ONLY */ + break; + case DRX_STANDARD_UNKNOWN: + default: + return -EINVAL; + } + + /* check bandwidth QAM annex B, NTSC and 8VSB */ + if ((standard == DRX_STANDARD_ITU_B) || + (standard == DRX_STANDARD_8VSB) || + (standard == DRX_STANDARD_NTSC)) { + switch (channel->bandwidth) { + case DRX_BANDWIDTH_6MHZ: + case DRX_BANDWIDTH_UNKNOWN: + channel->bandwidth = DRX_BANDWIDTH_6MHZ; + break; + case DRX_BANDWIDTH_8MHZ: + case DRX_BANDWIDTH_7MHZ: + default: + return -EINVAL; + } + } + + /* For QAM annex A and annex C: + -check symbolrate and constellation + -derive bandwidth from symbolrate (input bandwidth is ignored) + */ +#ifndef DRXJ_VSB_ONLY + if ((standard == DRX_STANDARD_ITU_A) || + (standard == DRX_STANDARD_ITU_C)) { + struct drxuio_cfg uio_cfg = { DRX_UIO1, DRX_UIO_MODE_FIRMWARE_SAW }; + int bw_rolloff_factor = 0; + + bw_rolloff_factor = (standard == DRX_STANDARD_ITU_A) ? 115 : 113; + min_symbol_rate = DRXJ_QAM_SYMBOLRATE_MIN; + max_symbol_rate = DRXJ_QAM_SYMBOLRATE_MAX; + /* config SMA_TX pin to SAW switch mode */ + rc = ctrl_set_uio_cfg(demod, &uio_cfg); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if (channel->symbolrate < min_symbol_rate || + channel->symbolrate > max_symbol_rate) { + return -EINVAL; + } + + switch (channel->constellation) { + case DRX_CONSTELLATION_QAM16: + case DRX_CONSTELLATION_QAM32: + case DRX_CONSTELLATION_QAM64: + case DRX_CONSTELLATION_QAM128: + case DRX_CONSTELLATION_QAM256: + bandwidth_temp = channel->symbolrate * bw_rolloff_factor; + bandwidth = bandwidth_temp / 100; + + if ((bandwidth_temp % 100) >= 50) + bandwidth++; + + if (bandwidth <= 6100000) { + channel->bandwidth = DRX_BANDWIDTH_6MHZ; + } else if ((bandwidth > 6100000) + && (bandwidth <= 7100000)) { + channel->bandwidth = DRX_BANDWIDTH_7MHZ; + } else if (bandwidth > 7100000) { + channel->bandwidth = DRX_BANDWIDTH_8MHZ; + } + break; + default: + return -EINVAL; + } + } + + /* For QAM annex B: + -check constellation + */ + if (standard == DRX_STANDARD_ITU_B) { + switch (channel->constellation) { + case DRX_CONSTELLATION_AUTO: + case DRX_CONSTELLATION_QAM256: + case DRX_CONSTELLATION_QAM64: + break; + default: + return -EINVAL; + } + + switch (channel->interleavemode) { + case DRX_INTERLEAVEMODE_I128_J1: + case DRX_INTERLEAVEMODE_I128_J1_V2: + case DRX_INTERLEAVEMODE_I128_J2: + case DRX_INTERLEAVEMODE_I64_J2: + case DRX_INTERLEAVEMODE_I128_J3: + case DRX_INTERLEAVEMODE_I32_J4: + case DRX_INTERLEAVEMODE_I128_J4: + case DRX_INTERLEAVEMODE_I16_J8: + case DRX_INTERLEAVEMODE_I128_J5: + case DRX_INTERLEAVEMODE_I8_J16: + case DRX_INTERLEAVEMODE_I128_J6: + case DRX_INTERLEAVEMODE_I128_J7: + case DRX_INTERLEAVEMODE_I128_J8: + case DRX_INTERLEAVEMODE_I12_J17: + case DRX_INTERLEAVEMODE_I5_J4: + case DRX_INTERLEAVEMODE_B52_M240: + case DRX_INTERLEAVEMODE_B52_M720: + case DRX_INTERLEAVEMODE_UNKNOWN: + case DRX_INTERLEAVEMODE_AUTO: + break; + default: + return -EINVAL; + } + } + + if ((ext_attr->uio_sma_tx_mode) == DRX_UIO_MODE_FIRMWARE_SAW) { + /* SAW SW, user UIO is used for switchable SAW */ + struct drxuio_data uio1 = { DRX_UIO1, false }; + + switch (channel->bandwidth) { + case DRX_BANDWIDTH_8MHZ: + uio1.value = true; + break; + case DRX_BANDWIDTH_7MHZ: + uio1.value = false; + break; + case DRX_BANDWIDTH_6MHZ: + uio1.value = false; + break; + case DRX_BANDWIDTH_UNKNOWN: + default: + return -EINVAL; + } + + rc = ctrl_uio_write(demod, &uio1); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } +#endif /* DRXJ_VSB_ONLY */ + rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + tuner_freq_offset = 0; + + /*== Setup demod for specific standard ====================================*/ + switch (standard) { + case DRX_STANDARD_8VSB: + if (channel->mirror == DRX_MIRROR_AUTO) + ext_attr->mirror = DRX_MIRROR_NO; + else + ext_attr->mirror = channel->mirror; + rc = set_vsb(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_frequency(demod, channel, tuner_freq_offset); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + rc = set_qam_channel(demod, channel, tuner_freq_offset); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; +#endif + case DRX_STANDARD_UNKNOWN: + default: + return -EIO; + } + + /* flag the packet error counter reset */ + ext_attr->reset_pkt_err_acc = true; + + return 0; +rw_error: + return rc; +} + +/*============================================================================= + ===== SigQuality() ========================================================== + ===========================================================================*/ + +/* +* \fn int ctrl_sig_quality() +* \brief Retrieve signal quality form device. +* \param devmod Pointer to demodulator instance. +* \param sig_quality Pointer to signal quality data. +* \return int. +* \retval 0 sig_quality contains valid data. +* \retval -EINVAL sig_quality is NULL. +* \retval -EIO Erroneous data, sig_quality contains invalid data. + +*/ +static int +ctrl_sig_quality(struct drx_demod_instance *demod, + enum drx_lock_status lock_status) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + struct drxj_data *ext_attr = demod->my_ext_attr; + struct drx39xxj_state *state = dev_addr->user_data; + struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache; + enum drx_standard standard = ext_attr->standard; + int rc; + u32 ber, cnt, err, pkt; + u16 mer, strength = 0; + + rc = get_sig_strength(demod, &strength); + if (rc < 0) { + pr_err("error getting signal strength %d\n", rc); + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } else { + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->strength.stat[0].uvalue = 65535UL * strength/ 100; + } + + switch (standard) { + case DRX_STANDARD_8VSB: +#ifdef DRXJ_SIGNAL_ACCUM_ERR + rc = get_acc_pkt_err(demod, &pkt); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } +#endif + if (lock_status != DRXJ_DEMOD_LOCK && lock_status != DRX_LOCKED) { + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } else { + rc = get_vsb_post_rs_pck_err(dev_addr, &err, &pkt); + if (rc != 0) { + pr_err("error %d getting UCB\n", rc); + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } else { + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue += err; + p->block_count.stat[0].scale = FE_SCALE_COUNTER; + p->block_count.stat[0].uvalue += pkt; + } + + /* PostViterbi is compute in steps of 10^(-6) */ + rc = get_vs_bpre_viterbi_ber(dev_addr, &ber, &cnt); + if (rc != 0) { + pr_err("error %d getting pre-ber\n", rc); + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } else { + p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_error.stat[0].uvalue += ber; + p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_count.stat[0].uvalue += cnt; + } + + rc = get_vs_bpost_viterbi_ber(dev_addr, &ber, &cnt); + if (rc != 0) { + pr_err("error %d getting post-ber\n", rc); + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } else { + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue += ber; + p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_count.stat[0].uvalue += cnt; + } + rc = get_vsbmer(dev_addr, &mer); + if (rc != 0) { + pr_err("error %d getting MER\n", rc); + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } else { + p->cnr.stat[0].svalue = mer * 100; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } + } + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + rc = ctrl_get_qam_sig_quality(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; +#endif + default: + return -EIO; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int ctrl_lock_status() +* \brief Retrieve lock status . +* \param dev_addr Pointer to demodulator device address. +* \param lock_stat Pointer to lock status structure. +* \return int. +* +*/ +static int +ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_stat) +{ + enum drx_standard standard = DRX_STANDARD_UNKNOWN; + struct drxj_data *ext_attr = NULL; + struct i2c_device_addr *dev_addr = NULL; + struct drxjscu_cmd cmd_scu = { /* command */ 0, + /* parameter_len */ 0, + /* result_len */ 0, + /* *parameter */ NULL, + /* *result */ NULL + }; + int rc; + u16 cmd_result[2] = { 0, 0 }; + u16 demod_lock = SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_DEMOD_LOCKED; + + /* check arguments */ + if ((demod == NULL) || (lock_stat == NULL)) + return -EINVAL; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + standard = ext_attr->standard; + + *lock_stat = DRX_NOT_LOCKED; + + /* define the SCU command code */ + switch (standard) { + case DRX_STANDARD_8VSB: + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB | + SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK; + demod_lock |= 0x6; + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK; + break; +#endif + case DRX_STANDARD_UNKNOWN: + default: + return -EIO; + } + + /* define the SCU command parameters and execute the command */ + cmd_scu.parameter_len = 0; + cmd_scu.result_len = 2; + cmd_scu.parameter = NULL; + cmd_scu.result = cmd_result; + rc = scu_command(dev_addr, &cmd_scu); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* set the lock status */ + if (cmd_scu.result[1] < demod_lock) { + /* 0x0000 NOT LOCKED */ + *lock_stat = DRX_NOT_LOCKED; + } else if (cmd_scu.result[1] < SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_LOCKED) { + *lock_stat = DRXJ_DEMOD_LOCK; + } else if (cmd_scu.result[1] < + SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NEVER_LOCK) { + /* 0x8000 DEMOD + FEC LOCKED (system lock) */ + *lock_stat = DRX_LOCKED; + } else { + /* 0xC000 NEVER LOCKED */ + /* (system will never be able to lock to the signal) */ + *lock_stat = DRX_NEVER_LOCK; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int ctrl_set_standard() +* \brief Set modulation standard to be used. +* \param standard Modulation standard. +* \return int. +* +* Setup stuff for the desired demodulation standard. +* Disable and power down the previous selected demodulation standard +* +*/ +static int +ctrl_set_standard(struct drx_demod_instance *demod, enum drx_standard *standard) +{ + struct drxj_data *ext_attr = NULL; + int rc; + enum drx_standard prev_standard; + + /* check arguments */ + if ((standard == NULL) || (demod == NULL)) + return -EINVAL; + + ext_attr = (struct drxj_data *) demod->my_ext_attr; + prev_standard = ext_attr->standard; + + /* + Stop and power down previous standard + */ + switch (prev_standard) { +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + rc = power_down_qam(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; +#endif + case DRX_STANDARD_8VSB: + rc = power_down_vsb(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_STANDARD_UNKNOWN: + /* Do nothing */ + break; + case DRX_STANDARD_AUTO: + default: + rc = -EINVAL; + goto rw_error; + } + + /* + Initialize channel independent registers + Power up new standard + */ + ext_attr->standard = *standard; + + switch (*standard) { +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + do { + u16 dummy; + rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SCU_RAM_VERSION_HI__A, &dummy, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } while (0); + break; +#endif + case DRX_STANDARD_8VSB: + rc = set_vsb_leak_n_gain(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + default: + ext_attr->standard = DRX_STANDARD_UNKNOWN; + return -EINVAL; + } + + return 0; +rw_error: + /* Don't know what the standard is now ... try again */ + ext_attr->standard = DRX_STANDARD_UNKNOWN; + return rc; +} + +/*============================================================================*/ + +static void drxj_reset_mode(struct drxj_data *ext_attr) +{ + /* Initialize default AFE configuration for QAM */ + if (ext_attr->has_lna) { + /* IF AGC off, PGA active */ +#ifndef DRXJ_VSB_ONLY + ext_attr->qam_if_agc_cfg.standard = DRX_STANDARD_ITU_B; + ext_attr->qam_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_OFF; + ext_attr->qam_pga_cfg = 140 + (11 * 13); +#endif + ext_attr->vsb_if_agc_cfg.standard = DRX_STANDARD_8VSB; + ext_attr->vsb_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_OFF; + ext_attr->vsb_pga_cfg = 140 + (11 * 13); + } else { + /* IF AGC on, PGA not active */ +#ifndef DRXJ_VSB_ONLY + ext_attr->qam_if_agc_cfg.standard = DRX_STANDARD_ITU_B; + ext_attr->qam_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO; + ext_attr->qam_if_agc_cfg.min_output_level = 0; + ext_attr->qam_if_agc_cfg.max_output_level = 0x7FFF; + ext_attr->qam_if_agc_cfg.speed = 3; + ext_attr->qam_if_agc_cfg.top = 1297; + ext_attr->qam_pga_cfg = 140; +#endif + ext_attr->vsb_if_agc_cfg.standard = DRX_STANDARD_8VSB; + ext_attr->vsb_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO; + ext_attr->vsb_if_agc_cfg.min_output_level = 0; + ext_attr->vsb_if_agc_cfg.max_output_level = 0x7FFF; + ext_attr->vsb_if_agc_cfg.speed = 3; + ext_attr->vsb_if_agc_cfg.top = 1024; + ext_attr->vsb_pga_cfg = 140; + } + /* TODO: remove min_output_level and max_output_level for both QAM and VSB after */ + /* mc has not used them */ +#ifndef DRXJ_VSB_ONLY + ext_attr->qam_rf_agc_cfg.standard = DRX_STANDARD_ITU_B; + ext_attr->qam_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO; + ext_attr->qam_rf_agc_cfg.min_output_level = 0; + ext_attr->qam_rf_agc_cfg.max_output_level = 0x7FFF; + ext_attr->qam_rf_agc_cfg.speed = 3; + ext_attr->qam_rf_agc_cfg.top = 9500; + ext_attr->qam_rf_agc_cfg.cut_off_current = 4000; + ext_attr->qam_pre_saw_cfg.standard = DRX_STANDARD_ITU_B; + ext_attr->qam_pre_saw_cfg.reference = 0x07; + ext_attr->qam_pre_saw_cfg.use_pre_saw = true; +#endif + /* Initialize default AFE configuration for VSB */ + ext_attr->vsb_rf_agc_cfg.standard = DRX_STANDARD_8VSB; + ext_attr->vsb_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO; + ext_attr->vsb_rf_agc_cfg.min_output_level = 0; + ext_attr->vsb_rf_agc_cfg.max_output_level = 0x7FFF; + ext_attr->vsb_rf_agc_cfg.speed = 3; + ext_attr->vsb_rf_agc_cfg.top = 9500; + ext_attr->vsb_rf_agc_cfg.cut_off_current = 4000; + ext_attr->vsb_pre_saw_cfg.standard = DRX_STANDARD_8VSB; + ext_attr->vsb_pre_saw_cfg.reference = 0x07; + ext_attr->vsb_pre_saw_cfg.use_pre_saw = true; +} + +/* +* \fn int ctrl_power_mode() +* \brief Set the power mode of the device to the specified power mode +* \param demod Pointer to demodulator instance. +* \param mode Pointer to new power mode. +* \return int. +* \retval 0 Success +* \retval -EIO I2C error or other failure +* \retval -EINVAL Invalid mode argument. +* +* +*/ +static int +ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode) +{ + 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; + int rc; + u16 sio_cc_pwd_mode = 0; + + 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; + + /* Check arguments */ + if (mode == NULL) + return -EINVAL; + + /* If already in requested power mode, do nothing */ + if (common_attr->current_power_mode == *mode) + return 0; + + switch (*mode) { + case DRX_POWER_UP: + case DRXJ_POWER_DOWN_MAIN_PATH: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_NONE; + break; + case DRXJ_POWER_DOWN_CORE: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_CLOCK; + break; + case DRXJ_POWER_DOWN_PLL: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_PLL; + break; + case DRX_POWER_DOWN: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC; + break; + default: + /* Unknown sleep mode */ + return -EINVAL; + } + + /* Check if device needs to be powered up */ + if ((common_attr->current_power_mode != DRX_POWER_UP)) { + rc = power_up_device(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + if (*mode == DRX_POWER_UP) { + /* Restore analog & pin configuration */ + + /* Initialize default AFE configuration for VSB */ + drxj_reset_mode(ext_attr); + } else { + /* Power down to requested mode */ + /* Backup some register settings */ + /* Set pins with possible pull-ups connected to them in input mode */ + /* Analog power down */ + /* ADC power down */ + /* Power down device */ + /* stop all comm_exec */ + /* + Stop and power down previous standard + */ + + switch (ext_attr->standard) { + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + rc = power_down_qam(demod, true); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_STANDARD_8VSB: + rc = power_down_vsb(demod, true); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_STANDARD_PAL_SECAM_BG: + case DRX_STANDARD_PAL_SECAM_DK: + case DRX_STANDARD_PAL_SECAM_I: + case DRX_STANDARD_PAL_SECAM_L: + case DRX_STANDARD_PAL_SECAM_LP: + case DRX_STANDARD_NTSC: + case DRX_STANDARD_FM: + rc = power_down_atv(demod, ext_attr->standard, true); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + break; + case DRX_STANDARD_UNKNOWN: + /* Do nothing */ + break; + case DRX_STANDARD_AUTO: + default: + return -EIO; + } + ext_attr->standard = DRX_STANDARD_UNKNOWN; + } + + if (*mode != DRXJ_POWER_DOWN_MAIN_PATH) { + rc = drxj_dap_write_reg16(dev_addr, SIO_CC_PWD_MODE__A, sio_cc_pwd_mode, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + if ((*mode != DRX_POWER_UP)) { + /* Initialize HI, wakeup key especially before put IC to sleep */ + rc = init_hi(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + ext_attr->hi_cfg_ctrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; + rc = hi_cfg_command(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + } + + common_attr->current_power_mode = *mode; + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ +/*== CTRL Set/Get Config related functions ===================================*/ +/*============================================================================*/ + +/* +* \fn int ctrl_set_cfg_pre_saw() +* \brief Set Pre-saw reference. +* \param demod demod instance +* \param u16 * +* \return int. +* +* Check arguments +* Dispatch handling to standard specific function. +* +*/ +static int +ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw *pre_saw) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + int rc; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + /* check arguments */ + if ((pre_saw == NULL) || (pre_saw->reference > IQM_AF_PDREF__M) + ) { + return -EINVAL; + } + + /* Only if standard is currently active */ + if ((ext_attr->standard == pre_saw->standard) || + (DRXJ_ISQAMSTD(ext_attr->standard) && + DRXJ_ISQAMSTD(pre_saw->standard)) || + (DRXJ_ISATVSTD(ext_attr->standard) && + DRXJ_ISATVSTD(pre_saw->standard))) { + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PDREF__A, pre_saw->reference, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* Store pre-saw settings */ + switch (pre_saw->standard) { + case DRX_STANDARD_8VSB: + ext_attr->vsb_pre_saw_cfg = *pre_saw; + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + ext_attr->qam_pre_saw_cfg = *pre_saw; + break; +#endif + default: + return -EINVAL; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + +/* +* \fn int ctrl_set_cfg_afe_gain() +* \brief Set AFE Gain. +* \param demod demod instance +* \param u16 * +* \return int. +* +* Check arguments +* Dispatch handling to standard specific function. +* +*/ +static int +ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain *afe_gain) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + int rc; + u8 gain = 0; + + /* check arguments */ + if (afe_gain == NULL) + return -EINVAL; + + dev_addr = demod->my_i2c_dev_addr; + ext_attr = (struct drxj_data *) demod->my_ext_attr; + + switch (afe_gain->standard) { + case DRX_STANDARD_8VSB: fallthrough; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: +#endif + /* Do nothing */ + break; + default: + return -EINVAL; + } + + /* TODO PGA gain is also written by microcode (at least by QAM and VSB) + So I (PJ) think interface requires choice between auto, user mode */ + + if (afe_gain->gain >= 329) + gain = 15; + else if (afe_gain->gain <= 147) + gain = 0; + else + gain = (afe_gain->gain - 140 + 6) / 13; + + /* Only if standard is currently active */ + if (ext_attr->standard == afe_gain->standard) { + rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PGA_GAIN__A, gain, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + } + + /* Store AFE Gain settings */ + switch (afe_gain->standard) { + case DRX_STANDARD_8VSB: + ext_attr->vsb_pga_cfg = gain * 13 + 140; + break; +#ifndef DRXJ_VSB_ONLY + case DRX_STANDARD_ITU_A: + case DRX_STANDARD_ITU_B: + case DRX_STANDARD_ITU_C: + ext_attr->qam_pga_cfg = gain * 13 + 140; + break; +#endif + default: + return -EIO; + } + + return 0; +rw_error: + return rc; +} + +/*============================================================================*/ + + +/*============================================================================= +===== EXPORTED FUNCTIONS ====================================================*/ + +static int drx_ctrl_u_code(struct drx_demod_instance *demod, + struct drxu_code_info *mc_info, + enum drxu_code_action action); +static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state); + +/* +* \fn drxj_open() +* \brief Open the demod instance, configure device, configure drxdriver +* \return Status_t Return status. +* +* drxj_open() can be called with a NULL ucode image => no ucode upload. +* This means that drxj_open() must NOT contain SCU commands or, in general, +* rely on SCU or AUD ucode to be present. +* +*/ + +static int drxj_open(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = NULL; + struct drxj_data *ext_attr = NULL; + struct drx_common_attr *common_attr = NULL; + u32 driver_version = 0; + struct drxu_code_info ucode_info; + struct drx_cfg_mpeg_output cfg_mpeg_output; + int rc; + enum drx_power_mode power_mode = DRX_POWER_UP; + + if ((demod == NULL) || + (demod->my_common_attr == NULL) || + (demod->my_ext_attr == NULL) || + (demod->my_i2c_dev_addr == NULL) || + (demod->my_common_attr->is_opened)) { + return -EINVAL; + } + + /* Check arguments */ + if (demod->my_ext_attr == 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; + + rc = ctrl_power_mode(demod, &power_mode); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + if (power_mode != DRX_POWER_UP) { + rc = -EINVAL; + pr_err("failed to powerup device\n"); + goto rw_error; + } + + /* has to be in front of setIqmAf and setOrxNsuAox */ + rc = get_device_capabilities(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* + * Soft reset of sys- and osc-clockdomain + * + * HACK: On windows, it writes a 0x07 here, instead of just 0x03. + * As we didn't load the firmware here yet, we should do the same. + * Btw, this is coherent with DRX-K, where we send reset codes + * for modulation (OFTM, in DRX-k), SYS and OSC clock domains. + */ + rc = drxj_dap_write_reg16(dev_addr, SIO_CC_SOFT_RST__A, (0x04 | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + msleep(1); + + /* TODO first make sure that everything keeps working before enabling this */ + /* PowerDownAnalogBlocks() */ + rc = drxj_dap_write_reg16(dev_addr, ATV_TOP_STDBY__A, (~ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE) | ATV_TOP_STDBY_SIF_STDBY_STANDBY, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = set_iqm_af(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = set_orx_nsu_aox(demod, false); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = init_hi(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* disable mpegoutput pins */ + memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output)); + cfg_mpeg_output.enable_mpeg_output = false; + + rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Stop AUD Inform SetAudio it will need to do all setting */ + rc = power_down_aud(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + /* Stop SCU */ + rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Upload microcode */ + if (common_attr->microcode_file != NULL) { + /* Dirty trick to use common ucode upload & verify, + pretend device is already open */ + common_attr->is_opened = true; + ucode_info.mc_file = common_attr->microcode_file; + + if (DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode)) { + pr_err("Should powerup before loading the firmware."); + rc = -EINVAL; + goto rw_error; + } + + rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_UPLOAD); + if (rc != 0) { + pr_err("error %d while uploading the firmware\n", rc); + goto rw_error; + } + if (common_attr->verify_microcode == true) { + rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_VERIFY); + if (rc != 0) { + pr_err("error %d while verifying the firmware\n", + rc); + goto rw_error; + } + } + common_attr->is_opened = false; + } + + /* Run SCU for a little while to initialize microcode version numbers */ + rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Initialize scan timeout */ + common_attr->scan_demod_lock_timeout = DRXJ_SCAN_TIMEOUT; + common_attr->scan_desired_lock = DRX_LOCKED; + + drxj_reset_mode(ext_attr); + ext_attr->standard = DRX_STANDARD_UNKNOWN; + + rc = smart_ant_init(demod); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* Stamp driver version number in SCU data RAM in BCD code + Done to enable field application engineers to retrieve drxdriver version + via I2C from SCU RAM + */ + driver_version = (VERSION_MAJOR / 100) % 10; + driver_version <<= 4; + driver_version += (VERSION_MAJOR / 10) % 10; + driver_version <<= 4; + driver_version += (VERSION_MAJOR % 10); + driver_version <<= 4; + driver_version += (VERSION_MINOR % 10); + driver_version <<= 4; + driver_version += (VERSION_PATCH / 1000) % 10; + driver_version <<= 4; + driver_version += (VERSION_PATCH / 100) % 10; + driver_version <<= 4; + driver_version += (VERSION_PATCH / 10) % 10; + driver_version <<= 4; + driver_version += (VERSION_PATCH % 10); + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_DRIVER_VER_HI__A, (u16)(driver_version >> 16), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_DRIVER_VER_LO__A, (u16)(driver_version & 0xFFFF), 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = ctrl_set_oob(demod, NULL); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + /* refresh the audio data structure with default */ + ext_attr->aud_data = drxj_default_aud_data_g; + + demod->my_common_attr->is_opened = true; + drxj_set_lna_state(demod, false); + return 0; +rw_error: + common_attr->is_opened = false; + return rc; +} + +/*============================================================================*/ +/* +* \fn drxj_close() +* \brief Close the demod instance, power down the device +* \return Status_t Return status. +* +*/ +static int drxj_close(struct drx_demod_instance *demod) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + enum drx_power_mode power_mode = DRX_POWER_UP; + + if ((demod->my_common_attr == NULL) || + (demod->my_ext_attr == NULL) || + (demod->my_i2c_dev_addr == NULL) || + (!demod->my_common_attr->is_opened)) { + return -EINVAL; + } + + /* power up */ + rc = ctrl_power_mode(demod, &power_mode); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + power_mode = DRX_POWER_DOWN; + rc = ctrl_power_mode(demod, &power_mode); + if (rc != 0) { + pr_err("error %d\n", rc); + goto rw_error; + } + + DRX_ATTR_ISOPENED(demod) = false; + + return 0; +rw_error: + DRX_ATTR_ISOPENED(demod) = false; + + return rc; +} + +/* + * Microcode related functions + */ + +/* + * drx_u_code_compute_crc - Compute CRC of block of microcode data. + * @block_data: Pointer to microcode data. + * @nr_words: Size of microcode block (number of 16 bits words). + * + * returns The computed CRC residue. + */ +static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words) +{ + u16 i = 0; + u16 j = 0; + u32 crc_word = 0; + u32 carry = 0; + + while (i < nr_words) { + crc_word |= (u32)be16_to_cpu(*(__be16 *)(block_data)); + for (j = 0; j < 16; j++) { + crc_word <<= 1; + if (carry != 0) + crc_word ^= 0x80050000UL; + carry = crc_word & 0x80000000UL; + } + i++; + block_data += (sizeof(u16)); + } + return (u16)(crc_word >> 16); +} + +/* + * drx_check_firmware - checks if the loaded firmware is valid + * + * @demod: demod structure + * @mc_data: pointer to the start of the firmware + * @size: firmware size + */ +static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data, + unsigned size) +{ + struct drxu_code_block_hdr block_hdr; + int i; + unsigned count = 2 * sizeof(u16); + u32 mc_dev_type, mc_version, mc_base_version; + u16 mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data + sizeof(u16))); + + /* + * Scan microcode blocks first for version info + * and firmware check + */ + + /* Clear version block */ + DRX_ATTR_MCRECORD(demod).aux_type = 0; + DRX_ATTR_MCRECORD(demod).mc_dev_type = 0; + DRX_ATTR_MCRECORD(demod).mc_version = 0; + DRX_ATTR_MCRECORD(demod).mc_base_version = 0; + + for (i = 0; i < mc_nr_of_blks; i++) { + if (count + 3 * sizeof(u16) + sizeof(u32) > size) + goto eof; + + /* Process block header */ + block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data + count)); + count += sizeof(u32); + block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data + count)); + count += sizeof(u16); + block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data + count)); + count += sizeof(u16); + block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data + count)); + count += sizeof(u16); + + pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", + count, block_hdr.addr, block_hdr.size, block_hdr.flags, + block_hdr.CRC); + + if (block_hdr.flags & 0x8) { + u8 *auxblk = ((void *)mc_data) + block_hdr.addr; + u16 auxtype; + + if (block_hdr.addr + sizeof(u16) > size) + goto eof; + + auxtype = be16_to_cpu(*(__be16 *)(auxblk)); + + /* Aux block. Check type */ + if (DRX_ISMCVERTYPE(auxtype)) { + if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size) + goto eof; + + auxblk += sizeof(u16); + mc_dev_type = be32_to_cpu(*(__be32 *)(auxblk)); + auxblk += sizeof(u32); + mc_version = be32_to_cpu(*(__be32 *)(auxblk)); + auxblk += sizeof(u32); + mc_base_version = be32_to_cpu(*(__be32 *)(auxblk)); + + DRX_ATTR_MCRECORD(demod).aux_type = auxtype; + DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type; + DRX_ATTR_MCRECORD(demod).mc_version = mc_version; + DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version; + + pr_info("Firmware dev %x, ver %x, base ver %x\n", + mc_dev_type, mc_version, mc_base_version); + + } + } else if (count + block_hdr.size * sizeof(u16) > size) + goto eof; + + count += block_hdr.size * sizeof(u16); + } + return 0; +eof: + pr_err("Firmware is truncated at pos %u/%u\n", count, size); + return -EINVAL; +} + +/* + * drx_ctrl_u_code - Handle microcode upload or verify. + * @dev_addr: Address of device. + * @mc_info: Pointer to information about microcode data. + * @action: Either UCODE_UPLOAD or UCODE_VERIFY + * + * This function returns: + * 0: + * - In case of UCODE_UPLOAD: code is successfully uploaded. + * - In case of UCODE_VERIFY: image on device is equal to + * image provided to this control function. + * -EIO: + * - In case of UCODE_UPLOAD: I2C error. + * - In case of UCODE_VERIFY: I2C error or image on device + * is not equal to image provided to this control function. + * -EINVAL: + * - Invalid arguments. + * - Provided image is corrupt + */ +static int drx_ctrl_u_code(struct drx_demod_instance *demod, + struct drxu_code_info *mc_info, + enum drxu_code_action action) +{ + struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr; + int rc; + u16 i = 0; + u16 mc_nr_of_blks = 0; + u16 mc_magic_word = 0; + const u8 *mc_data_init = NULL; + u8 *mc_data = NULL; + unsigned size; + char *mc_file; + + /* Check arguments */ + if (!mc_info || !mc_info->mc_file) + return -EINVAL; + + mc_file = mc_info->mc_file; + + if (!demod->firmware) { + const struct firmware *fw = NULL; + + rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent); + if (rc < 0) { + pr_err("Couldn't read firmware %s\n", mc_file); + return rc; + } + demod->firmware = fw; + + if (demod->firmware->size < 2 * sizeof(u16)) { + rc = -EINVAL; + pr_err("Firmware is too short!\n"); + goto release; + } + + pr_info("Firmware %s, size %zu\n", + mc_file, demod->firmware->size); + } + + mc_data_init = demod->firmware->data; + size = demod->firmware->size; + + mc_data = (void *)mc_data_init; + /* Check data */ + mc_magic_word = be16_to_cpu(*(__be16 *)(mc_data)); + mc_data += sizeof(u16); + mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data)); + mc_data += sizeof(u16); + + if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) { + rc = -EINVAL; + pr_err("Firmware magic word doesn't match\n"); + goto release; + } + + if (action == UCODE_UPLOAD) { + rc = drx_check_firmware(demod, (u8 *)mc_data_init, size); + if (rc) + goto release; + pr_info("Uploading firmware %s\n", mc_file); + } else { + pr_info("Verifying if firmware upload was ok.\n"); + } + + /* Process microcode blocks */ + for (i = 0; i < mc_nr_of_blks; i++) { + struct drxu_code_block_hdr block_hdr; + u16 mc_block_nr_bytes = 0; + + /* Process block header */ + block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data)); + mc_data += sizeof(u32); + block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data)); + mc_data += sizeof(u16); + block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data)); + mc_data += sizeof(u16); + block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data)); + mc_data += sizeof(u16); + + pr_debug("%zd: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", + (mc_data - mc_data_init), block_hdr.addr, + block_hdr.size, block_hdr.flags, block_hdr.CRC); + + /* Check block header on: + - data larger than 64Kb + - if CRC enabled check CRC + */ + if ((block_hdr.size > 0x7FFF) || + (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) && + (block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size))) + ) { + /* Wrong data ! */ + rc = -EINVAL; + pr_err("firmware CRC is wrong\n"); + goto release; + } + + if (!block_hdr.size) + continue; + + mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16)); + + /* Perform the desired action */ + switch (action) { + case UCODE_UPLOAD: /* Upload microcode */ + if (drxdap_fasi_write_block(dev_addr, + block_hdr.addr, + mc_block_nr_bytes, + mc_data, 0x0000)) { + rc = -EIO; + pr_err("error writing firmware at pos %zd\n", + mc_data - mc_data_init); + goto release; + } + break; + case UCODE_VERIFY: { /* Verify uploaded microcode */ + int result = 0; + u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE]; + u32 bytes_to_comp = 0; + u32 bytes_left = mc_block_nr_bytes; + u32 curr_addr = block_hdr.addr; + u8 *curr_ptr = mc_data; + + while (bytes_left != 0) { + if (bytes_left > DRX_UCODE_MAX_BUF_SIZE) + bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE; + else + bytes_to_comp = bytes_left; + + if (drxdap_fasi_read_block(dev_addr, + curr_addr, + (u16)bytes_to_comp, + (u8 *)mc_data_buffer, + 0x0000)) { + pr_err("error reading firmware at pos %zd\n", + mc_data - mc_data_init); + return -EIO; + } + + result = memcmp(curr_ptr, mc_data_buffer, + bytes_to_comp); + + if (result) { + pr_err("error verifying firmware at pos %zd\n", + mc_data - mc_data_init); + return -EIO; + } + + curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2)); + curr_ptr =&(curr_ptr[bytes_to_comp]); + bytes_left -=((u32) bytes_to_comp); + } + break; + } + default: + return -EINVAL; + + } + mc_data += mc_block_nr_bytes; + } + + return 0; + +release: + release_firmware(demod->firmware); + demod->firmware = NULL; + + return rc; +} + +/* caller is expected to check if lna is supported before enabling */ +static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state) +{ + struct drxuio_cfg uio_cfg; + struct drxuio_data uio_data; + int result; + + uio_cfg.uio = DRX_UIO1; + uio_cfg.mode = DRX_UIO_MODE_READWRITE; + /* Configure user-I/O #3: enable read/write */ + result = ctrl_set_uio_cfg(demod, &uio_cfg); + if (result) { + pr_err("Failed to setup LNA GPIO!\n"); + return result; + } + + uio_data.uio = DRX_UIO1; + uio_data.value = state; + result = ctrl_uio_write(demod, &uio_data); + if (result != 0) { + pr_err("Failed to %sable LNA!\n", + state ? "en" : "dis"); + return result; + } + return 0; +} + +/* + * The Linux DVB Driver for Micronas DRX39xx family (drx3933j) + * + * Written by Devin Heitmueller <devin.heitmueller@kernellabs.com> + */ + +static int drx39xxj_set_powerstate(struct dvb_frontend *fe, int enable) +{ + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + int result; + enum drx_power_mode power_mode; + + if (enable) + power_mode = DRX_POWER_UP; + else + power_mode = DRX_POWER_DOWN; + + result = ctrl_power_mode(demod, &power_mode); + if (result != 0) { + pr_err("Power state change failed\n"); + return 0; + } + + return 0; +} + +static int drx39xxj_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + int result; + enum drx_lock_status lock_status; + + *status = 0; + + result = ctrl_lock_status(demod, &lock_status); + if (result != 0) { + pr_err("drx39xxj: could not get lock status!\n"); + *status = 0; + } + + switch (lock_status) { + case DRX_NEVER_LOCK: + *status = 0; + pr_err("drx says NEVER_LOCK\n"); + break; + case DRX_NOT_LOCKED: + *status = 0; + break; + case DRX_LOCK_STATE_1: + case DRX_LOCK_STATE_2: + case DRX_LOCK_STATE_3: + case DRX_LOCK_STATE_4: + case DRX_LOCK_STATE_5: + case DRX_LOCK_STATE_6: + case DRX_LOCK_STATE_7: + case DRX_LOCK_STATE_8: + case DRX_LOCK_STATE_9: + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC; + break; + case DRX_LOCKED: + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + break; + default: + pr_err("Lock state unknown %d\n", lock_status); + } + ctrl_sig_quality(demod, lock_status); + + return 0; +} + +static int drx39xxj_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->pre_bit_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + *ber = 0; + return 0; + } + + if (!p->pre_bit_count.stat[0].uvalue) { + if (!p->pre_bit_error.stat[0].uvalue) + *ber = 0; + else + *ber = 1000000; + } else { + *ber = frac_times1e6(p->pre_bit_error.stat[0].uvalue, + p->pre_bit_count.stat[0].uvalue); + } + return 0; +} + +static int drx39xxj_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + *strength = 0; + return 0; + } + + *strength = p->strength.stat[0].uvalue; + return 0; +} + +static int drx39xxj_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u64 tmp64; + + if (p->cnr.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + *snr = 0; + return 0; + } + + tmp64 = p->cnr.stat[0].svalue; + do_div(tmp64, 10); + *snr = tmp64; + return 0; +} + +static int drx39xxj_read_ucblocks(struct dvb_frontend *fe, u32 *ucb) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->block_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + *ucb = 0; + return 0; + } + + *ucb = p->block_error.stat[0].uvalue; + return 0; +} + +static int drx39xxj_set_frontend(struct dvb_frontend *fe) +{ +#ifdef DJH_DEBUG + int i; +#endif + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + enum drx_standard standard = DRX_STANDARD_8VSB; + struct drx_channel channel; + int result; + static const struct drx_channel def_channel = { + /* frequency */ 0, + /* bandwidth */ DRX_BANDWIDTH_6MHZ, + /* mirror */ DRX_MIRROR_NO, + /* constellation */ DRX_CONSTELLATION_AUTO, + /* hierarchy */ DRX_HIERARCHY_UNKNOWN, + /* priority */ DRX_PRIORITY_UNKNOWN, + /* coderate */ DRX_CODERATE_UNKNOWN, + /* guard */ DRX_GUARD_UNKNOWN, + /* fftmode */ DRX_FFTMODE_UNKNOWN, + /* classification */ DRX_CLASSIFICATION_AUTO, + /* symbolrate */ 5057000, + /* interleavemode */ DRX_INTERLEAVEMODE_UNKNOWN, + /* ldpc */ DRX_LDPC_UNKNOWN, + /* carrier */ DRX_CARRIER_UNKNOWN, + /* frame mode */ DRX_FRAMEMODE_UNKNOWN + }; + u32 constellation = DRX_CONSTELLATION_AUTO; + + /* Bring the demod out of sleep */ + drx39xxj_set_powerstate(fe, 1); + + if (fe->ops.tuner_ops.set_params) { + u32 int_freq; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* Set tuner to desired frequency and standard */ + fe->ops.tuner_ops.set_params(fe); + + /* Use the tuner's IF */ + if (fe->ops.tuner_ops.get_if_frequency) { + fe->ops.tuner_ops.get_if_frequency(fe, &int_freq); + demod->my_common_attr->intermediate_freq = int_freq / 1000; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + switch (p->delivery_system) { + case SYS_ATSC: + standard = DRX_STANDARD_8VSB; + break; + case SYS_DVBC_ANNEX_B: + standard = DRX_STANDARD_ITU_B; + + switch (p->modulation) { + case QAM_64: + constellation = DRX_CONSTELLATION_QAM64; + break; + case QAM_256: + constellation = DRX_CONSTELLATION_QAM256; + break; + default: + constellation = DRX_CONSTELLATION_AUTO; + break; + } + break; + default: + return -EINVAL; + } + /* Set the standard (will be powered up if necessary */ + result = ctrl_set_standard(demod, &standard); + if (result != 0) { + pr_err("Failed to set standard! result=%02x\n", + result); + return -EINVAL; + } + + /* set channel parameters */ + channel = def_channel; + channel.frequency = p->frequency / 1000; + channel.bandwidth = DRX_BANDWIDTH_6MHZ; + channel.constellation = constellation; + + /* program channel */ + result = ctrl_set_channel(demod, &channel); + if (result != 0) { + pr_err("Failed to set channel!\n"); + return -EINVAL; + } + /* Just for giggles, let's shut off the LNA again.... */ + drxj_set_lna_state(demod, false); + + /* After set_frontend, except for strength, stats aren't available */ + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + + return 0; +} + +static int drx39xxj_sleep(struct dvb_frontend *fe) +{ + /* power-down the demodulator */ + return drx39xxj_set_powerstate(fe, 0); +} + +static int drx39xxj_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + bool i2c_gate_state; + int result; + +#ifdef DJH_DEBUG + pr_debug("i2c gate call: enable=%d state=%d\n", enable, + state->i2c_gate_open); +#endif + + if (enable) + i2c_gate_state = true; + else + i2c_gate_state = false; + + if (state->i2c_gate_open == enable) { + /* We're already in the desired state */ + return 0; + } + + result = ctrl_i2c_bridge(demod, &i2c_gate_state); + if (result != 0) { + pr_err("drx39xxj: could not open i2c gate [%d]\n", + result); + dump_stack(); + } else { + state->i2c_gate_open = enable; + } + return 0; +} + +static int drx39xxj_init(struct dvb_frontend *fe) +{ + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + int rc = 0; + + if (fe->exit == DVB_FE_DEVICE_RESUME) { + /* so drxj_open() does what it needs to do */ + demod->my_common_attr->is_opened = false; + rc = drxj_open(demod); + if (rc != 0) + pr_err("drx39xxj_init(): DRX open failed rc=%d!\n", rc); + } else + drx39xxj_set_powerstate(fe, 1); + + return rc; +} + +static int drx39xxj_set_lna(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + struct drxj_data *ext_attr = demod->my_ext_attr; + + if (c->lna) { + if (!ext_attr->has_lna) { + pr_err("LNA is not supported on this device!\n"); + return -EINVAL; + + } + } + + return drxj_set_lna_state(demod, c->lna); +} + +static int drx39xxj_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void drx39xxj_release(struct dvb_frontend *fe) +{ + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + + /* if device is removed don't access it */ + if (fe->exit != DVB_FE_DEVICE_REMOVED) + drxj_close(demod); + + kfree(demod->my_ext_attr); + kfree(demod->my_common_attr); + kfree(demod->my_i2c_dev_addr); + release_firmware(demod->firmware); + kfree(demod); + kfree(state); +} + +static const struct dvb_frontend_ops drx39xxj_ops; + +struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) +{ + struct drx39xxj_state *state = NULL; + struct i2c_device_addr *demod_addr = NULL; + struct drx_common_attr *demod_comm_attr = NULL; + struct drxj_data *demod_ext_attr = NULL; + struct drx_demod_instance *demod = NULL; + struct dtv_frontend_properties *p; + int result; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct drx39xxj_state), GFP_KERNEL); + if (state == NULL) + goto error; + + demod = kmemdup(&drxj_default_demod_g, + sizeof(struct drx_demod_instance), GFP_KERNEL); + if (demod == NULL) + goto error; + + demod_addr = kmemdup(&drxj_default_addr_g, + sizeof(struct i2c_device_addr), GFP_KERNEL); + if (demod_addr == NULL) + goto error; + + demod_comm_attr = kmemdup(&drxj_default_comm_attr_g, + sizeof(struct drx_common_attr), GFP_KERNEL); + if (demod_comm_attr == NULL) + goto error; + + demod_ext_attr = kmemdup(&drxj_data_g, sizeof(struct drxj_data), + GFP_KERNEL); + if (demod_ext_attr == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->demod = demod; + + /* setup the demod data */ + demod->my_i2c_dev_addr = demod_addr; + demod->my_common_attr = demod_comm_attr; + demod->my_i2c_dev_addr->user_data = state; + demod->my_common_attr->microcode_file = DRX39XX_MAIN_FIRMWARE; + demod->my_common_attr->verify_microcode = true; + demod->my_common_attr->intermediate_freq = 5000; + demod->my_common_attr->current_power_mode = DRX_POWER_DOWN; + demod->my_ext_attr = demod_ext_attr; + ((struct drxj_data *)demod_ext_attr)->uio_sma_tx_mode = DRX_UIO_MODE_READWRITE; + demod->i2c = i2c; + + result = drxj_open(demod); + if (result != 0) { + pr_err("DRX open failed! Aborting\n"); + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &drx39xxj_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + + /* Initialize stats - needed for DVBv5 stats to work */ + p = &state->frontend.dtv_property_cache; + p->strength.len = 1; + p->pre_bit_count.len = 1; + p->pre_bit_error.len = 1; + p->post_bit_count.len = 1; + p->post_bit_error.len = 1; + p->block_count.len = 1; + p->block_error.len = 1; + p->cnr.len = 1; + + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return &state->frontend; + +error: + kfree(demod_ext_attr); + kfree(demod_comm_attr); + kfree(demod_addr); + kfree(demod); + kfree(state); + + return NULL; +} +EXPORT_SYMBOL_GPL(drx39xxj_attach); + +static const struct dvb_frontend_ops drx39xxj_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Micronas DRX39xxj family Frontend", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = drx39xxj_init, + .i2c_gate_ctrl = drx39xxj_i2c_gate_ctrl, + .sleep = drx39xxj_sleep, + .set_frontend = drx39xxj_set_frontend, + .get_tune_settings = drx39xxj_get_tune_settings, + .read_status = drx39xxj_read_status, + .read_ber = drx39xxj_read_ber, + .read_signal_strength = drx39xxj_read_signal_strength, + .read_snr = drx39xxj_read_snr, + .read_ucblocks = drx39xxj_read_ucblocks, + .release = drx39xxj_release, + .set_lna = drx39xxj_set_lna, +}; + +MODULE_DESCRIPTION("Micronas DRX39xxj Frontend"); +MODULE_AUTHOR("Devin Heitmueller"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(DRX39XX_MAIN_FIRMWARE); diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.h b/drivers/media/dvb-frontends/drx39xyj/drxj.h new file mode 100644 index 0000000000..232b3b0d68 --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.h @@ -0,0 +1,651 @@ + +/* + 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 header file + + Authors: Dragan Savic, Milos Nikolic, Mihajlo Katona, Tao Ding, Paul Janssen +*/ + +#ifndef __DRXJ_H__ +#define __DRXJ_H__ +/*------------------------------------------------------------------------- +INCLUDES +-------------------------------------------------------------------------*/ + +#include "drx_driver.h" +#include "drx_dap_fasi.h" + +/* Check DRX-J specific dap condition */ +/* Multi master mode and short addr format only will not work. + RMW, CRC reset, broadcast and switching back to single master mode + cannot be done with short addr only in multi master mode. */ +#if ((DRXDAP_SINGLE_MASTER == 0) && (DRXDAPFASI_LONG_ADDR_ALLOWED == 0)) +#error "Multi master mode and short addressing only is an illegal combination" + *; /* Generate a fatal compiler error to make sure it stops here, + this is necessary because not all compilers stop after a #error. */ +#endif + +/*------------------------------------------------------------------------- +TYPEDEFS +-------------------------------------------------------------------------*/ +/*============================================================================*/ +/*============================================================================*/ +/*== code support ============================================================*/ +/*============================================================================*/ +/*============================================================================*/ + +/*============================================================================*/ +/*============================================================================*/ +/*== SCU cmd if =============================================================*/ +/*============================================================================*/ +/*============================================================================*/ + + struct drxjscu_cmd { + u16 command; + /*< Command number */ + u16 parameter_len; + /*< Data length in byte */ + u16 result_len; + /*< result length in byte */ + u16 *parameter; + /*< General purpose param */ + u16 *result; + /*< General purpose param */}; + +/*============================================================================*/ +/*============================================================================*/ +/*== CTRL CFG related data structures ========================================*/ +/*============================================================================*/ +/*============================================================================*/ + +/* extra intermediate lock state for VSB,QAM,NTSC */ +#define DRXJ_DEMOD_LOCK (DRX_LOCK_STATE_1) + +/* OOB lock states */ +#define DRXJ_OOB_AGC_LOCK (DRX_LOCK_STATE_1) /* analog gain control lock */ +#define DRXJ_OOB_SYNC_LOCK (DRX_LOCK_STATE_2) /* digital gain control lock */ + +/* Intermediate powermodes for DRXJ */ +#define DRXJ_POWER_DOWN_MAIN_PATH DRX_POWER_MODE_8 +#define DRXJ_POWER_DOWN_CORE DRX_POWER_MODE_9 +#define DRXJ_POWER_DOWN_PLL DRX_POWER_MODE_10 + +/* supstition for GPIO FNC mux */ +#define APP_O (0x0000) + +/*#define DRX_CTRL_BASE (0x0000)*/ + +#define DRXJ_CTRL_CFG_BASE (0x1000) + enum drxj_cfg_type { + DRXJ_CFG_AGC_RF = DRXJ_CTRL_CFG_BASE, + DRXJ_CFG_AGC_IF, + DRXJ_CFG_AGC_INTERNAL, + DRXJ_CFG_PRE_SAW, + DRXJ_CFG_AFE_GAIN, + DRXJ_CFG_SYMBOL_CLK_OFFSET, + DRXJ_CFG_ACCUM_CR_RS_CW_ERR, + DRXJ_CFG_FEC_MERS_SEQ_COUNT, + DRXJ_CFG_OOB_MISC, + DRXJ_CFG_SMART_ANT, + DRXJ_CFG_OOB_PRE_SAW, + DRXJ_CFG_VSB_MISC, + DRXJ_CFG_RESET_PACKET_ERR, + + /* ATV (FM) */ + DRXJ_CFG_ATV_OUTPUT, /* also for FM (SIF control) but not likely */ + DRXJ_CFG_ATV_MISC, + DRXJ_CFG_ATV_EQU_COEF, + DRXJ_CFG_ATV_AGC_STATUS, /* also for FM ( IF,RF, audioAGC ) */ + + DRXJ_CFG_MPEG_OUTPUT_MISC, + DRXJ_CFG_HW_CFG, + DRXJ_CFG_OOB_LO_POW, + + DRXJ_CFG_MAX /* dummy, never to be used */}; + +/* +* /enum drxj_cfg_smart_ant_io * smart antenna i/o. +*/ +enum drxj_cfg_smart_ant_io { + DRXJ_SMT_ANT_OUTPUT = 0, + DRXJ_SMT_ANT_INPUT +}; + +/* +* /struct drxj_cfg_smart_ant * Set smart antenna. +*/ + struct drxj_cfg_smart_ant { + enum drxj_cfg_smart_ant_io io; + u16 ctrl_data; + }; + +/* +* /struct DRXJAGCSTATUS_t +* AGC status information from the DRXJ-IQM-AF. +*/ +struct drxj_agc_status { + u16 IFAGC; + u16 RFAGC; + u16 digital_agc; +}; + +/* DRXJ_CFG_AGC_RF, DRXJ_CFG_AGC_IF */ + +/* +* /enum drxj_agc_ctrl_mode * Available AGCs modes in the DRXJ. +*/ + enum drxj_agc_ctrl_mode { + DRX_AGC_CTRL_AUTO = 0, + DRX_AGC_CTRL_USER, + DRX_AGC_CTRL_OFF}; + +/* +* /struct drxj_cfg_agc * Generic interface for all AGCs present on the DRXJ. +*/ + struct drxj_cfg_agc { + enum drx_standard standard; /* standard for which these settings apply */ + enum drxj_agc_ctrl_mode ctrl_mode; /* off, user, auto */ + u16 output_level; /* range dependent on AGC */ + u16 min_output_level; /* range dependent on AGC */ + u16 max_output_level; /* range dependent on AGC */ + u16 speed; /* range dependent on AGC */ + u16 top; /* rf-agc take over point */ + u16 cut_off_current; /* rf-agc is accelerated if output current + is below cut-off current */}; + +/* DRXJ_CFG_PRE_SAW */ + +/* +* /struct drxj_cfg_pre_saw * Interface to configure pre SAW sense. +*/ + struct drxj_cfg_pre_saw { + enum drx_standard standard; /* standard to which these settings apply */ + u16 reference; /* pre SAW reference value, range 0 .. 31 */ + bool use_pre_saw; /* true algorithms must use pre SAW sense */}; + +/* DRXJ_CFG_AFE_GAIN */ + +/* +* /struct drxj_cfg_afe_gain * Interface to configure gain of AFE (LNA + PGA). +*/ + struct drxj_cfg_afe_gain { + enum drx_standard standard; /* standard to which these settings apply */ + u16 gain; /* gain in 0.1 dB steps, DRXJ range 140 .. 335 */}; + +/* +* /struct drxjrs_errors +* Available failure information in DRXJ_FEC_RS. +* +* Container for errors that are received in the most recently finished measurement period +* +*/ + struct drxjrs_errors { + u16 nr_bit_errors; + /*< no of pre RS bit errors */ + u16 nr_symbol_errors; + /*< no of pre RS symbol errors */ + u16 nr_packet_errors; + /*< no of pre RS packet errors */ + u16 nr_failures; + /*< no of post RS failures to decode */ + u16 nr_snc_par_fail_count; + /*< no of post RS bit erros */ + }; + +/* +* /struct drxj_cfg_vsb_misc * symbol error rate +*/ + struct drxj_cfg_vsb_misc { + u32 symb_error; + /*< symbol error rate sps */}; + +/* +* /enum drxj_mpeg_output_clock_rate * Mpeg output clock rate. +* +*/ + enum drxj_mpeg_start_width { + DRXJ_MPEG_START_WIDTH_1CLKCYC, + DRXJ_MPEG_START_WIDTH_8CLKCYC}; + +/* +* /enum drxj_mpeg_output_clock_rate * Mpeg output clock rate. +* +*/ + enum drxj_mpeg_output_clock_rate { + DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO, + DRXJ_MPEGOUTPUT_CLOCK_RATE_75973K, + DRXJ_MPEGOUTPUT_CLOCK_RATE_50625K, + DRXJ_MPEGOUTPUT_CLOCK_RATE_37968K, + DRXJ_MPEGOUTPUT_CLOCK_RATE_30375K, + DRXJ_MPEGOUTPUT_CLOCK_RATE_25313K, + DRXJ_MPEGOUTPUT_CLOCK_RATE_21696K}; + +/* +* /struct DRXJCfgMisc_t +* Change TEI bit of MPEG output +* reverse MPEG output bit order +* set MPEG output clock rate +*/ + struct drxj_cfg_mpeg_output_misc { + bool disable_tei_handling; /*< if true pass (not change) TEI bit */ + bool bit_reverse_mpeg_outout; /*< if true, parallel: msb on MD0; serial: lsb out first */ + enum drxj_mpeg_output_clock_rate mpeg_output_clock_rate; + /*< set MPEG output clock rate that overwirtes the derived one from symbol rate */ + enum drxj_mpeg_start_width mpeg_start_width; /*< set MPEG output start width */}; + +/* +* /enum drxj_xtal_freq * Supported external crystal reference frequency. +*/ + enum drxj_xtal_freq { + DRXJ_XTAL_FREQ_RSVD, + DRXJ_XTAL_FREQ_27MHZ, + DRXJ_XTAL_FREQ_20P25MHZ, + DRXJ_XTAL_FREQ_4MHZ}; + +/* +* /enum drxj_xtal_freq * Supported external crystal reference frequency. +*/ + enum drxji2c_speed { + DRXJ_I2C_SPEED_400KBPS, + DRXJ_I2C_SPEED_100KBPS}; + +/* +* /struct drxj_cfg_hw_cfg * Get hw configuration, such as crystal +* reference frequency, I2C speed, etc... +*/ + struct drxj_cfg_hw_cfg { + enum drxj_xtal_freq xtal_freq; + /*< crystal reference frequency */ + enum drxji2c_speed i2c_speed; + /*< 100 or 400 kbps */}; + +/* + * DRXJ_CFG_ATV_MISC + */ + struct drxj_cfg_atv_misc { + s16 peak_filter; /* -8 .. 15 */ + u16 noise_filter; /* 0 .. 15 */}; + +/* + * struct drxj_cfg_oob_misc */ +#define DRXJ_OOB_STATE_RESET 0x0 +#define DRXJ_OOB_STATE_AGN_HUNT 0x1 +#define DRXJ_OOB_STATE_DGN_HUNT 0x2 +#define DRXJ_OOB_STATE_AGC_HUNT 0x3 +#define DRXJ_OOB_STATE_FRQ_HUNT 0x4 +#define DRXJ_OOB_STATE_PHA_HUNT 0x8 +#define DRXJ_OOB_STATE_TIM_HUNT 0x10 +#define DRXJ_OOB_STATE_EQU_HUNT 0x20 +#define DRXJ_OOB_STATE_EQT_HUNT 0x30 +#define DRXJ_OOB_STATE_SYNC 0x40 + +struct drxj_cfg_oob_misc { + struct drxj_agc_status agc; + bool eq_lock; + bool sym_timing_lock; + bool phase_lock; + bool freq_lock; + bool dig_gain_lock; + bool ana_gain_lock; + u8 state; +}; + +/* + * Index of in array of coef + */ + enum drxj_cfg_oob_lo_power { + DRXJ_OOB_LO_POW_MINUS0DB = 0, + DRXJ_OOB_LO_POW_MINUS5DB, + DRXJ_OOB_LO_POW_MINUS10DB, + DRXJ_OOB_LO_POW_MINUS15DB, + DRXJ_OOB_LO_POW_MAX}; + +/* + * DRXJ_CFG_ATV_EQU_COEF + */ + struct drxj_cfg_atv_equ_coef { + s16 coef0; /* -256 .. 255 */ + s16 coef1; /* -256 .. 255 */ + s16 coef2; /* -256 .. 255 */ + s16 coef3; /* -256 .. 255 */}; + +/* + * Index of in array of coef + */ + enum drxj_coef_array_index { + DRXJ_COEF_IDX_MN = 0, + DRXJ_COEF_IDX_FM, + DRXJ_COEF_IDX_L, + DRXJ_COEF_IDX_LP, + DRXJ_COEF_IDX_BG, + DRXJ_COEF_IDX_DK, + DRXJ_COEF_IDX_I, + DRXJ_COEF_IDX_MAX}; + +/* + * DRXJ_CFG_ATV_OUTPUT + */ + +/* +* /enum DRXJAttenuation_t +* Attenuation setting for SIF AGC. +* +*/ + enum drxjsif_attenuation { + DRXJ_SIF_ATTENUATION_0DB, + DRXJ_SIF_ATTENUATION_3DB, + DRXJ_SIF_ATTENUATION_6DB, + DRXJ_SIF_ATTENUATION_9DB}; + +/* +* /struct drxj_cfg_atv_output * SIF attenuation setting. +* +*/ +struct drxj_cfg_atv_output { + bool enable_cvbs_output; /* true= enabled */ + bool enable_sif_output; /* true= enabled */ + enum drxjsif_attenuation sif_attenuation; +}; + +/* + DRXJ_CFG_ATV_AGC_STATUS (get only) +*/ +/* TODO : AFE interface not yet finished, subject to change */ + struct drxj_cfg_atv_agc_status { + u16 rf_agc_gain; /* 0 .. 877 uA */ + u16 if_agc_gain; /* 0 .. 877 uA */ + s16 video_agc_gain; /* -75 .. 1972 in 0.1 dB steps */ + s16 audio_agc_gain; /* -4 .. 1020 in 0.1 dB steps */ + u16 rf_agc_loop_gain; /* 0 .. 7 */ + u16 if_agc_loop_gain; /* 0 .. 7 */ + u16 video_agc_loop_gain; /* 0 .. 7 */}; + +/*============================================================================*/ +/*============================================================================*/ +/*== CTRL related data structures ============================================*/ +/*============================================================================*/ +/*============================================================================*/ + +/* NONE */ + +/*============================================================================*/ +/*============================================================================*/ + +/*========================================*/ +/* +* /struct struct drxj_data * DRXJ specific attributes. +* +* Global data container for DRXJ specific data. +* +*/ + struct drxj_data { + /* device capabilities (determined during drx_open()) */ + bool has_lna; /*< true if LNA (aka PGA) present */ + bool has_oob; /*< true if OOB supported */ + bool has_ntsc; /*< true if NTSC supported */ + bool has_btsc; /*< true if BTSC supported */ + bool has_smatx; /*< true if mat_tx is available */ + bool has_smarx; /*< true if mat_rx is available */ + bool has_gpio; /*< true if GPIO is available */ + bool has_irqn; /*< true if IRQN is available */ + /* A1/A2/A... */ + u8 mfx; /*< metal fix */ + + /* tuner settings */ + bool mirror_freq_spect_oob;/*< tuner inversion (true = tuner mirrors the signal */ + + /* standard/channel settings */ + enum drx_standard standard; /*< current standard information */ + enum drx_modulation constellation; + /*< current constellation */ + s32 frequency; /*< center signal frequency in KHz */ + enum drx_bandwidth curr_bandwidth; + /*< current channel bandwidth */ + enum drx_mirror mirror; /*< current channel mirror */ + + /* signal quality information */ + u32 fec_bits_desired; /*< BER accounting period */ + u16 fec_vd_plen; /*< no of trellis symbols: VD SER measurement period */ + u16 qam_vd_prescale; /*< Viterbi Measurement Prescale */ + u16 qam_vd_period; /*< Viterbi Measurement period */ + u16 fec_rs_plen; /*< defines RS BER measurement period */ + u16 fec_rs_prescale; /*< ReedSolomon Measurement Prescale */ + u16 fec_rs_period; /*< ReedSolomon Measurement period */ + bool reset_pkt_err_acc; /*< Set a flag to reset accumulated packet error */ + u16 pkt_err_acc_start; /*< Set a flag to reset accumulated packet error */ + + /* HI configuration */ + u16 hi_cfg_timing_div; /*< HI Configure() parameter 2 */ + u16 hi_cfg_bridge_delay; /*< HI Configure() parameter 3 */ + u16 hi_cfg_wake_up_key; /*< HI Configure() parameter 4 */ + u16 hi_cfg_ctrl; /*< HI Configure() parameter 5 */ + u16 hi_cfg_transmit; /*< HI Configure() parameter 6 */ + + /* UIO configuration */ + enum drxuio_mode uio_sma_rx_mode;/*< current mode of SmaRx pin */ + enum drxuio_mode uio_sma_tx_mode;/*< current mode of SmaTx pin */ + enum drxuio_mode uio_gpio_mode; /*< current mode of ASEL pin */ + enum drxuio_mode uio_irqn_mode; /*< current mode of IRQN pin */ + + /* IQM fs frequency shift and inversion */ + u32 iqm_fs_rate_ofs; /*< frequency shifter setting after setchannel */ + bool pos_image; /*< True: positive image */ + /* IQM RC frequency shift */ + u32 iqm_rc_rate_ofs; /*< frequency shifter setting after setchannel */ + + /* ATV configuration */ + u32 atv_cfg_changed_flags; /*< flag: flags cfg changes */ + s16 atv_top_equ0[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU0__A */ + s16 atv_top_equ1[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU1__A */ + s16 atv_top_equ2[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU2__A */ + s16 atv_top_equ3[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU3__A */ + bool phase_correction_bypass;/*< flag: true=bypass */ + s16 atv_top_vid_peak; /*< shadow of ATV_TOP_VID_PEAK__A */ + u16 atv_top_noise_th; /*< shadow of ATV_TOP_NOISE_TH__A */ + bool enable_cvbs_output; /*< flag CVBS output enable */ + bool enable_sif_output; /*< flag SIF output enable */ + enum drxjsif_attenuation sif_attenuation; + /*< current SIF att setting */ + /* Agc configuration for QAM and VSB */ + struct drxj_cfg_agc qam_rf_agc_cfg; /*< qam RF AGC config */ + struct drxj_cfg_agc qam_if_agc_cfg; /*< qam IF AGC config */ + struct drxj_cfg_agc vsb_rf_agc_cfg; /*< vsb RF AGC config */ + struct drxj_cfg_agc vsb_if_agc_cfg; /*< vsb IF AGC config */ + + /* PGA gain configuration for QAM and VSB */ + u16 qam_pga_cfg; /*< qam PGA config */ + u16 vsb_pga_cfg; /*< vsb PGA config */ + + /* Pre SAW configuration for QAM and VSB */ + struct drxj_cfg_pre_saw qam_pre_saw_cfg; + /*< qam pre SAW config */ + struct drxj_cfg_pre_saw vsb_pre_saw_cfg; + /*< qam pre SAW config */ + + /* Version information */ + char v_text[2][12]; /*< allocated text versions */ + struct drx_version v_version[2]; /*< allocated versions structs */ + struct drx_version_list v_list_elements[2]; + /*< allocated version list */ + + /* smart antenna configuration */ + bool smart_ant_inverted; + + /* Tracking filter setting for OOB */ + u16 oob_trk_filter_cfg[8]; + bool oob_power_on; + + /* MPEG static bitrate setting */ + u32 mpeg_ts_static_bitrate; /*< bitrate static MPEG output */ + bool disable_te_ihandling; /*< MPEG TS TEI handling */ + bool bit_reverse_mpeg_outout;/*< MPEG output bit order */ + enum drxj_mpeg_output_clock_rate mpeg_output_clock_rate; + /*< MPEG output clock rate */ + enum drxj_mpeg_start_width mpeg_start_width; + /*< MPEG Start width */ + + /* Pre SAW & Agc configuration for ATV */ + struct drxj_cfg_pre_saw atv_pre_saw_cfg; + /*< atv pre SAW config */ + struct drxj_cfg_agc atv_rf_agc_cfg; /*< atv RF AGC config */ + struct drxj_cfg_agc atv_if_agc_cfg; /*< atv IF AGC config */ + u16 atv_pga_cfg; /*< atv pga config */ + + u32 curr_symbol_rate; + + /* pin-safe mode */ + bool pdr_safe_mode; /*< PDR safe mode activated */ + u16 pdr_safe_restore_val_gpio; + u16 pdr_safe_restore_val_v_sync; + u16 pdr_safe_restore_val_sma_rx; + u16 pdr_safe_restore_val_sma_tx; + + /* OOB pre-saw value */ + u16 oob_pre_saw; + enum drxj_cfg_oob_lo_power oob_lo_pow; + + struct drx_aud_data aud_data; + /*< audio storage */}; + +/*------------------------------------------------------------------------- +Access MACROS +-------------------------------------------------------------------------*/ +/* +* \brief Compilable references to attributes +* \param d pointer to demod instance +* +* Used as main reference to an attribute field. +* Can be used by both macro implementation and function implementation. +* These macros are defined to avoid duplication of code in macro and function +* definitions that handle access of demod common or extended attributes. +* +*/ + +#define DRXJ_ATTR_BTSC_DETECT(d) \ + (((struct drxj_data *)(d)->my_ext_attr)->aud_data.btsc_detect) + +/*------------------------------------------------------------------------- +DEFINES +-------------------------------------------------------------------------*/ + +/* +* \def DRXJ_NTSC_CARRIER_FREQ_OFFSET +* \brief Offset from picture carrier to centre frequency in kHz, in RF domain +* +* For NTSC standard. +* NTSC channels are listed by their picture carrier frequency (Fpc). +* The function DRX_CTRL_SET_CHANNEL requires the centre frequency as input. +* In case the tuner module is not used the DRX-J requires that the tuner is +* tuned to the centre frequency of the channel: +* +* Fcentre = Fpc + DRXJ_NTSC_CARRIER_FREQ_OFFSET +* +*/ +#define DRXJ_NTSC_CARRIER_FREQ_OFFSET ((s32)(1750)) + +/* +* \def DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET +* \brief Offset from picture carrier to centre frequency in kHz, in RF domain +* +* For PAL/SECAM - BG standard. This define is needed in case the tuner module +* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc). +* The DRX-J requires that the tuner is tuned to: +* Fpc + DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET +* +* In case the tuner module is used the drxdriver takes care of this. +* In case the tuner module is NOT used the application programmer must take +* care of this. +* +*/ +#define DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET ((s32)(2375)) + +/* +* \def DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET +* \brief Offset from picture carrier to centre frequency in kHz, in RF domain +* +* For PAL/SECAM - DK, I, L standards. This define is needed in case the tuner module +* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc). +* The DRX-J requires that the tuner is tuned to: +* Fpc + DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET +* +* In case the tuner module is used the drxdriver takes care of this. +* In case the tuner module is NOT used the application programmer must take +* care of this. +* +*/ +#define DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET ((s32)(2775)) + +/* +* \def DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET +* \brief Offset from picture carrier to centre frequency in kHz, in RF domain +* +* For PAL/SECAM - LP standard. This define is needed in case the tuner module +* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc). +* The DRX-J requires that the tuner is tuned to: +* Fpc + DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET +* +* In case the tuner module is used the drxdriver takes care of this. +* In case the tuner module is NOT used the application programmer must take +* care of this. +*/ +#define DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET ((s32)(-3255)) + +/* +* \def DRXJ_FM_CARRIER_FREQ_OFFSET +* \brief Offset from sound carrier to centre frequency in kHz, in RF domain +* +* For FM standard. +* FM channels are listed by their sound carrier frequency (Fsc). +* The function DRX_CTRL_SET_CHANNEL requires the Ffm frequency (see below) as +* input. +* In case the tuner module is not used the DRX-J requires that the tuner is +* tuned to the Ffm frequency of the channel. +* +* Ffm = Fsc + DRXJ_FM_CARRIER_FREQ_OFFSET +* +*/ +#define DRXJ_FM_CARRIER_FREQ_OFFSET ((s32)(-3000)) + +/* Revision types -------------------------------------------------------*/ + +#define DRXJ_TYPE_ID (0x3946000DUL) + +/* Macros ---------------------------------------------------------------*/ + +/* Convert OOB lock status to string */ +#define DRXJ_STR_OOB_LOCKSTATUS(x) ( \ + (x == DRX_NEVER_LOCK) ? "Never" : \ + (x == DRX_NOT_LOCKED) ? "No" : \ + (x == DRX_LOCKED) ? "Locked" : \ + (x == DRX_LOCK_STATE_1) ? "AGC lock" : \ + (x == DRX_LOCK_STATE_2) ? "sync lock" : \ + "(Invalid)") + +#endif /* __DRXJ_H__ */ diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj_map.h b/drivers/media/dvb-frontends/drx39xyj/drxj_map.h new file mode 100644 index 0000000000..0bbd4ae1f5 --- /dev/null +++ b/drivers/media/dvb-frontends/drx39xyj/drxj_map.h @@ -0,0 +1,15055 @@ +/* + 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. +*/ + +/* + *********************************************************************************************************************** + * WARNING - THIS FILE HAS BEEN GENERATED - DO NOT CHANGE + * + * Filename: drxj_map.h + * Generated on: Mon Jan 18 12:09:24 2010 + * Generated by: IDF:x 1.3.0 + * Generated from: reg_map + * Output start: [entry point] + * + * filename last modified re-use + * ----------------------------------------------------- + * reg_map.1.tmp Mon Jan 18 12:09:24 2010 - + * + */ + +#ifndef __DRXJ_MAP__H__ +#define __DRXJ_MAP__H__ INCLUDED + +#ifdef _REGISTERTABLE_ +#include <registertable.h> + extern register_table_t drxj_map[]; + extern register_table_info_t drxj_map_info[]; +#endif + +#define ATV_COMM_EXEC__A 0xC00000 +#define ATV_COMM_EXEC__W 2 +#define ATV_COMM_EXEC__M 0x3 +#define ATV_COMM_EXEC__PRE 0x0 +#define ATV_COMM_EXEC_STOP 0x0 +#define ATV_COMM_EXEC_ACTIVE 0x1 +#define ATV_COMM_EXEC_HOLD 0x2 + +#define ATV_COMM_STATE__A 0xC00001 +#define ATV_COMM_STATE__W 16 +#define ATV_COMM_STATE__M 0xFFFF +#define ATV_COMM_STATE__PRE 0x0 +#define ATV_COMM_MB__A 0xC00002 +#define ATV_COMM_MB__W 16 +#define ATV_COMM_MB__M 0xFFFF +#define ATV_COMM_MB__PRE 0x0 +#define ATV_COMM_INT_REQ__A 0xC00003 +#define ATV_COMM_INT_REQ__W 16 +#define ATV_COMM_INT_REQ__M 0xFFFF +#define ATV_COMM_INT_REQ__PRE 0x0 +#define ATV_COMM_INT_REQ_COMM_INT_REQ__B 0 +#define ATV_COMM_INT_REQ_COMM_INT_REQ__W 1 +#define ATV_COMM_INT_REQ_COMM_INT_REQ__M 0x1 +#define ATV_COMM_INT_REQ_COMM_INT_REQ__PRE 0x0 + +#define ATV_COMM_INT_STA__A 0xC00005 +#define ATV_COMM_INT_STA__W 16 +#define ATV_COMM_INT_STA__M 0xFFFF +#define ATV_COMM_INT_STA__PRE 0x0 +#define ATV_COMM_INT_MSK__A 0xC00006 +#define ATV_COMM_INT_MSK__W 16 +#define ATV_COMM_INT_MSK__M 0xFFFF +#define ATV_COMM_INT_MSK__PRE 0x0 +#define ATV_COMM_INT_STM__A 0xC00007 +#define ATV_COMM_INT_STM__W 16 +#define ATV_COMM_INT_STM__M 0xFFFF +#define ATV_COMM_INT_STM__PRE 0x0 + +#define ATV_COMM_KEY__A 0xC0000F +#define ATV_COMM_KEY__W 16 +#define ATV_COMM_KEY__M 0xFFFF +#define ATV_COMM_KEY__PRE 0x0 +#define ATV_COMM_KEY_KEY 0xFABA +#define ATV_COMM_KEY_MIN 0x0 +#define ATV_COMM_KEY_MAX 0xFFFF + +#define ATV_TOP_COMM_EXEC__A 0xC10000 +#define ATV_TOP_COMM_EXEC__W 2 +#define ATV_TOP_COMM_EXEC__M 0x3 +#define ATV_TOP_COMM_EXEC__PRE 0x0 +#define ATV_TOP_COMM_EXEC_STOP 0x0 +#define ATV_TOP_COMM_EXEC_ACTIVE 0x1 +#define ATV_TOP_COMM_EXEC_HOLD 0x2 + +#define ATV_TOP_COMM_STATE__A 0xC10001 +#define ATV_TOP_COMM_STATE__W 16 +#define ATV_TOP_COMM_STATE__M 0xFFFF +#define ATV_TOP_COMM_STATE__PRE 0x0 +#define ATV_TOP_COMM_STATE_STATE__B 0 +#define ATV_TOP_COMM_STATE_STATE__W 16 +#define ATV_TOP_COMM_STATE_STATE__M 0xFFFF +#define ATV_TOP_COMM_STATE_STATE__PRE 0x0 + +#define ATV_TOP_COMM_MB__A 0xC10002 +#define ATV_TOP_COMM_MB__W 16 +#define ATV_TOP_COMM_MB__M 0xFFFF +#define ATV_TOP_COMM_MB__PRE 0x0 +#define ATV_TOP_COMM_MB_CTL__B 0 +#define ATV_TOP_COMM_MB_CTL__W 1 +#define ATV_TOP_COMM_MB_CTL__M 0x1 +#define ATV_TOP_COMM_MB_CTL__PRE 0x0 +#define ATV_TOP_COMM_MB_OBS__B 1 +#define ATV_TOP_COMM_MB_OBS__W 1 +#define ATV_TOP_COMM_MB_OBS__M 0x2 +#define ATV_TOP_COMM_MB_OBS__PRE 0x0 + +#define ATV_TOP_COMM_MB_MUX_CTRL__B 2 +#define ATV_TOP_COMM_MB_MUX_CTRL__W 4 +#define ATV_TOP_COMM_MB_MUX_CTRL__M 0x3C +#define ATV_TOP_COMM_MB_MUX_CTRL__PRE 0x0 +#define ATV_TOP_COMM_MB_MUX_CTRL_PEAK_S 0x0 +#define ATV_TOP_COMM_MB_MUX_CTRL_VID_GAIN 0x4 +#define ATV_TOP_COMM_MB_MUX_CTRL_CORR_O 0x8 +#define ATV_TOP_COMM_MB_MUX_CTRL_CR_ROT_O 0xC +#define ATV_TOP_COMM_MB_MUX_CTRL_CR_IIR_IQ 0x10 +#define ATV_TOP_COMM_MB_MUX_CTRL_VIDEO_O 0x14 +#define ATV_TOP_COMM_MB_MUX_CTRL_SIF_O 0x18 +#define ATV_TOP_COMM_MB_MUX_CTRL_SIF2025_O 0x1C +#define ATV_TOP_COMM_MB_MUX_CTRL_POST_S 0x20 + +#define ATV_TOP_COMM_MB_MUX_OBS__B 6 +#define ATV_TOP_COMM_MB_MUX_OBS__W 4 +#define ATV_TOP_COMM_MB_MUX_OBS__M 0x3C0 +#define ATV_TOP_COMM_MB_MUX_OBS__PRE 0x0 +#define ATV_TOP_COMM_MB_MUX_OBS_PEAK_S 0x0 +#define ATV_TOP_COMM_MB_MUX_OBS_VID_GAIN 0x40 +#define ATV_TOP_COMM_MB_MUX_OBS_CORR_O 0x80 +#define ATV_TOP_COMM_MB_MUX_OBS_CR_ROT_O 0xC0 +#define ATV_TOP_COMM_MB_MUX_OBS_CR_IIR_IQ 0x100 +#define ATV_TOP_COMM_MB_MUX_OBS_VIDEO_O 0x140 +#define ATV_TOP_COMM_MB_MUX_OBS_SIF_O 0x180 +#define ATV_TOP_COMM_MB_MUX_OBS_SIF2025_O 0x1C0 +#define ATV_TOP_COMM_MB_MUX_OBS_POST_S 0x200 + +#define ATV_TOP_COMM_INT_REQ__A 0xC10003 +#define ATV_TOP_COMM_INT_REQ__W 16 +#define ATV_TOP_COMM_INT_REQ__M 0xFFFF +#define ATV_TOP_COMM_INT_REQ__PRE 0x0 +#define ATV_TOP_COMM_INT_STA__A 0xC10005 +#define ATV_TOP_COMM_INT_STA__W 16 +#define ATV_TOP_COMM_INT_STA__M 0xFFFF +#define ATV_TOP_COMM_INT_STA__PRE 0x0 + +#define ATV_TOP_COMM_INT_STA_FAGC_STA__B 0 +#define ATV_TOP_COMM_INT_STA_FAGC_STA__W 1 +#define ATV_TOP_COMM_INT_STA_FAGC_STA__M 0x1 +#define ATV_TOP_COMM_INT_STA_FAGC_STA__PRE 0x0 + +#define ATV_TOP_COMM_INT_STA_OVM_STA__B 1 +#define ATV_TOP_COMM_INT_STA_OVM_STA__W 1 +#define ATV_TOP_COMM_INT_STA_OVM_STA__M 0x2 +#define ATV_TOP_COMM_INT_STA_OVM_STA__PRE 0x0 + +#define ATV_TOP_COMM_INT_STA_AMPTH_STA__B 2 +#define ATV_TOP_COMM_INT_STA_AMPTH_STA__W 1 +#define ATV_TOP_COMM_INT_STA_AMPTH_STA__M 0x4 +#define ATV_TOP_COMM_INT_STA_AMPTH_STA__PRE 0x0 + +#define ATV_TOP_COMM_INT_MSK__A 0xC10006 +#define ATV_TOP_COMM_INT_MSK__W 16 +#define ATV_TOP_COMM_INT_MSK__M 0xFFFF +#define ATV_TOP_COMM_INT_MSK__PRE 0x0 + +#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__B 0 +#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__W 1 +#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__M 0x1 +#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__PRE 0x0 + +#define ATV_TOP_COMM_INT_MSK_OVM_MSK__B 1 +#define ATV_TOP_COMM_INT_MSK_OVM_MSK__W 1 +#define ATV_TOP_COMM_INT_MSK_OVM_MSK__M 0x2 +#define ATV_TOP_COMM_INT_MSK_OVM_MSK__PRE 0x0 + +#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__B 2 +#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__W 1 +#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__M 0x4 +#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__PRE 0x0 + +#define ATV_TOP_COMM_INT_STM__A 0xC10007 +#define ATV_TOP_COMM_INT_STM__W 16 +#define ATV_TOP_COMM_INT_STM__M 0xFFFF +#define ATV_TOP_COMM_INT_STM__PRE 0x0 + +#define ATV_TOP_COMM_INT_STM_FAGC_STM__B 0 +#define ATV_TOP_COMM_INT_STM_FAGC_STM__W 1 +#define ATV_TOP_COMM_INT_STM_FAGC_STM__M 0x1 +#define ATV_TOP_COMM_INT_STM_FAGC_STM__PRE 0x0 + +#define ATV_TOP_COMM_INT_STM_OVM_STM__B 1 +#define ATV_TOP_COMM_INT_STM_OVM_STM__W 1 +#define ATV_TOP_COMM_INT_STM_OVM_STM__M 0x2 +#define ATV_TOP_COMM_INT_STM_OVM_STM__PRE 0x0 + +#define ATV_TOP_COMM_INT_STM_AMPTH_STM__B 2 +#define ATV_TOP_COMM_INT_STM_AMPTH_STM__W 1 +#define ATV_TOP_COMM_INT_STM_AMPTH_STM__M 0x4 +#define ATV_TOP_COMM_INT_STM_AMPTH_STM__PRE 0x0 + +#define ATV_TOP_COMM_KEY__A 0xC1000F +#define ATV_TOP_COMM_KEY__W 16 +#define ATV_TOP_COMM_KEY__M 0xFFFF +#define ATV_TOP_COMM_KEY__PRE 0x0 + +#define ATV_TOP_COMM_KEY_KEY__B 0 +#define ATV_TOP_COMM_KEY_KEY__W 16 +#define ATV_TOP_COMM_KEY_KEY__M 0xFFFF +#define ATV_TOP_COMM_KEY_KEY__PRE 0x0 +#define ATV_TOP_COMM_KEY_KEY_KEY 0xFABA +#define ATV_TOP_COMM_KEY_KEY_MIN 0x0 +#define ATV_TOP_COMM_KEY_KEY_MAX 0xFFFF + +#define ATV_TOP_CR_AMP_TH__A 0xC10010 +#define ATV_TOP_CR_AMP_TH__W 8 +#define ATV_TOP_CR_AMP_TH__M 0xFF +#define ATV_TOP_CR_AMP_TH__PRE 0x8 +#define ATV_TOP_CR_AMP_TH_MN 0x8 + +#define ATV_TOP_CR_CONT__A 0xC10011 +#define ATV_TOP_CR_CONT__W 9 +#define ATV_TOP_CR_CONT__M 0x1FF +#define ATV_TOP_CR_CONT__PRE 0x9C + +#define ATV_TOP_CR_CONT_CR_P__B 0 +#define ATV_TOP_CR_CONT_CR_P__W 3 +#define ATV_TOP_CR_CONT_CR_P__M 0x7 +#define ATV_TOP_CR_CONT_CR_P__PRE 0x4 +#define ATV_TOP_CR_CONT_CR_P_MN 0x4 +#define ATV_TOP_CR_CONT_CR_P_FM 0x0 + +#define ATV_TOP_CR_CONT_CR_D__B 3 +#define ATV_TOP_CR_CONT_CR_D__W 3 +#define ATV_TOP_CR_CONT_CR_D__M 0x38 +#define ATV_TOP_CR_CONT_CR_D__PRE 0x18 +#define ATV_TOP_CR_CONT_CR_D_MN 0x18 +#define ATV_TOP_CR_CONT_CR_D_FM 0x0 + +#define ATV_TOP_CR_CONT_CR_I__B 6 +#define ATV_TOP_CR_CONT_CR_I__W 3 +#define ATV_TOP_CR_CONT_CR_I__M 0x1C0 +#define ATV_TOP_CR_CONT_CR_I__PRE 0x80 +#define ATV_TOP_CR_CONT_CR_I_MN 0x80 +#define ATV_TOP_CR_CONT_CR_I_FM 0x0 + +#define ATV_TOP_CR_OVM_TH__A 0xC10012 +#define ATV_TOP_CR_OVM_TH__W 8 +#define ATV_TOP_CR_OVM_TH__M 0xFF +#define ATV_TOP_CR_OVM_TH__PRE 0xA0 +#define ATV_TOP_CR_OVM_TH_MN 0xA0 +#define ATV_TOP_CR_OVM_TH_FM 0x0 + +#define ATV_TOP_NOISE_TH__A 0xC10013 +#define ATV_TOP_NOISE_TH__W 4 +#define ATV_TOP_NOISE_TH__M 0xF +#define ATV_TOP_NOISE_TH__PRE 0x8 +#define ATV_TOP_NOISE_TH_MN 0x8 + +#define ATV_TOP_EQU0__A 0xC10014 +#define ATV_TOP_EQU0__W 9 +#define ATV_TOP_EQU0__M 0x1FF +#define ATV_TOP_EQU0__PRE 0x1FB + +#define ATV_TOP_EQU0_EQU_C0__B 0 +#define ATV_TOP_EQU0_EQU_C0__W 9 +#define ATV_TOP_EQU0_EQU_C0__M 0x1FF +#define ATV_TOP_EQU0_EQU_C0__PRE 0x1FB +#define ATV_TOP_EQU0_EQU_C0_MN 0xFB + +#define ATV_TOP_EQU1__A 0xC10015 +#define ATV_TOP_EQU1__W 9 +#define ATV_TOP_EQU1__M 0x1FF +#define ATV_TOP_EQU1__PRE 0x1CE + +#define ATV_TOP_EQU1_EQU_C1__B 0 +#define ATV_TOP_EQU1_EQU_C1__W 9 +#define ATV_TOP_EQU1_EQU_C1__M 0x1FF +#define ATV_TOP_EQU1_EQU_C1__PRE 0x1CE +#define ATV_TOP_EQU1_EQU_C1_MN 0xCE + +#define ATV_TOP_EQU2__A 0xC10016 +#define ATV_TOP_EQU2__W 9 +#define ATV_TOP_EQU2__M 0x1FF +#define ATV_TOP_EQU2__PRE 0xD2 + +#define ATV_TOP_EQU2_EQU_C2__B 0 +#define ATV_TOP_EQU2_EQU_C2__W 9 +#define ATV_TOP_EQU2_EQU_C2__M 0x1FF +#define ATV_TOP_EQU2_EQU_C2__PRE 0xD2 +#define ATV_TOP_EQU2_EQU_C2_MN 0xD2 + +#define ATV_TOP_EQU3__A 0xC10017 +#define ATV_TOP_EQU3__W 9 +#define ATV_TOP_EQU3__M 0x1FF +#define ATV_TOP_EQU3__PRE 0x160 + +#define ATV_TOP_EQU3_EQU_C3__B 0 +#define ATV_TOP_EQU3_EQU_C3__W 9 +#define ATV_TOP_EQU3_EQU_C3__M 0x1FF +#define ATV_TOP_EQU3_EQU_C3__PRE 0x160 +#define ATV_TOP_EQU3_EQU_C3_MN 0x60 + +#define ATV_TOP_ROT_MODE__A 0xC10018 +#define ATV_TOP_ROT_MODE__W 1 +#define ATV_TOP_ROT_MODE__M 0x1 +#define ATV_TOP_ROT_MODE__PRE 0x0 +#define ATV_TOP_ROT_MODE_AMPTH_DEPEND 0x0 +#define ATV_TOP_ROT_MODE_ALWAYS 0x1 + +#define ATV_TOP_MOD_CONTROL__A 0xC10019 +#define ATV_TOP_MOD_CONTROL__W 12 +#define ATV_TOP_MOD_CONTROL__M 0xFFF +#define ATV_TOP_MOD_CONTROL__PRE 0x5B1 + +#define ATV_TOP_MOD_CONTROL_MOD_IR__B 0 +#define ATV_TOP_MOD_CONTROL_MOD_IR__W 3 +#define ATV_TOP_MOD_CONTROL_MOD_IR__M 0x7 +#define ATV_TOP_MOD_CONTROL_MOD_IR__PRE 0x1 +#define ATV_TOP_MOD_CONTROL_MOD_IR_MN 0x1 +#define ATV_TOP_MOD_CONTROL_MOD_IR_FM 0x0 + +#define ATV_TOP_MOD_CONTROL_MOD_IF__B 3 +#define ATV_TOP_MOD_CONTROL_MOD_IF__W 4 +#define ATV_TOP_MOD_CONTROL_MOD_IF__M 0x78 +#define ATV_TOP_MOD_CONTROL_MOD_IF__PRE 0x30 +#define ATV_TOP_MOD_CONTROL_MOD_IF_MN 0x30 +#define ATV_TOP_MOD_CONTROL_MOD_IF_FM 0x0 + +#define ATV_TOP_MOD_CONTROL_MOD_MODE__B 7 +#define ATV_TOP_MOD_CONTROL_MOD_MODE__W 1 +#define ATV_TOP_MOD_CONTROL_MOD_MODE__M 0x80 +#define ATV_TOP_MOD_CONTROL_MOD_MODE__PRE 0x80 +#define ATV_TOP_MOD_CONTROL_MOD_MODE_RISE 0x0 +#define ATV_TOP_MOD_CONTROL_MOD_MODE_RISE_FALL 0x80 + +#define ATV_TOP_MOD_CONTROL_MOD_TH__B 8 +#define ATV_TOP_MOD_CONTROL_MOD_TH__W 4 +#define ATV_TOP_MOD_CONTROL_MOD_TH__M 0xF00 +#define ATV_TOP_MOD_CONTROL_MOD_TH__PRE 0x500 +#define ATV_TOP_MOD_CONTROL_MOD_TH_MN 0x500 +#define ATV_TOP_MOD_CONTROL_MOD_TH_FM 0x0 + +#define ATV_TOP_STD__A 0xC1001A +#define ATV_TOP_STD__W 2 +#define ATV_TOP_STD__M 0x3 +#define ATV_TOP_STD__PRE 0x0 + +#define ATV_TOP_STD_MODE__B 0 +#define ATV_TOP_STD_MODE__W 1 +#define ATV_TOP_STD_MODE__M 0x1 +#define ATV_TOP_STD_MODE__PRE 0x0 +#define ATV_TOP_STD_MODE_MN 0x0 +#define ATV_TOP_STD_MODE_FM 0x1 + +#define ATV_TOP_STD_VID_POL__B 1 +#define ATV_TOP_STD_VID_POL__W 1 +#define ATV_TOP_STD_VID_POL__M 0x2 +#define ATV_TOP_STD_VID_POL__PRE 0x0 +#define ATV_TOP_STD_VID_POL_NEG 0x0 +#define ATV_TOP_STD_VID_POL_POS 0x2 + +#define ATV_TOP_VID_AMP__A 0xC1001B +#define ATV_TOP_VID_AMP__W 12 +#define ATV_TOP_VID_AMP__M 0xFFF +#define ATV_TOP_VID_AMP__PRE 0x380 +#define ATV_TOP_VID_AMP_MN 0x380 +#define ATV_TOP_VID_AMP_FM 0x0 + +#define ATV_TOP_VID_PEAK__A 0xC1001C +#define ATV_TOP_VID_PEAK__W 5 +#define ATV_TOP_VID_PEAK__M 0x1F +#define ATV_TOP_VID_PEAK__PRE 0x1 + +#define ATV_TOP_FAGC_TH__A 0xC1001D +#define ATV_TOP_FAGC_TH__W 11 +#define ATV_TOP_FAGC_TH__M 0x7FF +#define ATV_TOP_FAGC_TH__PRE 0x2B2 +#define ATV_TOP_FAGC_TH_MN 0x2B2 + +#define ATV_TOP_SYNC_SLICE__A 0xC1001E +#define ATV_TOP_SYNC_SLICE__W 11 +#define ATV_TOP_SYNC_SLICE__M 0x7FF +#define ATV_TOP_SYNC_SLICE__PRE 0x243 +#define ATV_TOP_SYNC_SLICE_MN 0x243 + +#define ATV_TOP_SIF_GAIN__A 0xC1001F +#define ATV_TOP_SIF_GAIN__W 11 +#define ATV_TOP_SIF_GAIN__M 0x7FF +#define ATV_TOP_SIF_GAIN__PRE 0x0 + +#define ATV_TOP_SIF_TP__A 0xC10020 +#define ATV_TOP_SIF_TP__W 6 +#define ATV_TOP_SIF_TP__M 0x3F +#define ATV_TOP_SIF_TP__PRE 0x0 + +#define ATV_TOP_MOD_ACCU__A 0xC10021 +#define ATV_TOP_MOD_ACCU__W 10 +#define ATV_TOP_MOD_ACCU__M 0x3FF +#define ATV_TOP_MOD_ACCU__PRE 0x0 + +#define ATV_TOP_CR_FREQ__A 0xC10022 +#define ATV_TOP_CR_FREQ__W 8 +#define ATV_TOP_CR_FREQ__M 0xFF +#define ATV_TOP_CR_FREQ__PRE 0x0 + +#define ATV_TOP_CR_PHAD__A 0xC10023 +#define ATV_TOP_CR_PHAD__W 12 +#define ATV_TOP_CR_PHAD__M 0xFFF +#define ATV_TOP_CR_PHAD__PRE 0x0 + +#define ATV_TOP_AF_SIF_ATT__A 0xC10024 +#define ATV_TOP_AF_SIF_ATT__W 2 +#define ATV_TOP_AF_SIF_ATT__M 0x3 +#define ATV_TOP_AF_SIF_ATT__PRE 0x0 +#define ATV_TOP_AF_SIF_ATT_0DB 0x0 +#define ATV_TOP_AF_SIF_ATT_M3DB 0x1 +#define ATV_TOP_AF_SIF_ATT_M6DB 0x2 +#define ATV_TOP_AF_SIF_ATT_M9DB 0x3 + +#define ATV_TOP_STDBY__A 0xC10025 +#define ATV_TOP_STDBY__W 2 +#define ATV_TOP_STDBY__M 0x3 +#define ATV_TOP_STDBY__PRE 0x1 + +#define ATV_TOP_STDBY_SIF_STDBY__B 0 +#define ATV_TOP_STDBY_SIF_STDBY__W 1 +#define ATV_TOP_STDBY_SIF_STDBY__M 0x1 +#define ATV_TOP_STDBY_SIF_STDBY__PRE 0x1 +#define ATV_TOP_STDBY_SIF_STDBY_ACTIVE 0x0 +#define ATV_TOP_STDBY_SIF_STDBY_STANDBY 0x1 + +#define ATV_TOP_STDBY_CVBS_STDBY__B 1 +#define ATV_TOP_STDBY_CVBS_STDBY__W 1 +#define ATV_TOP_STDBY_CVBS_STDBY__M 0x2 +#define ATV_TOP_STDBY_CVBS_STDBY__PRE 0x0 +#define ATV_TOP_STDBY_CVBS_STDBY_A1_ACTIVE 0x0 +#define ATV_TOP_STDBY_CVBS_STDBY_A1_STANDBY 0x2 +#define ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE 0x2 +#define ATV_TOP_STDBY_CVBS_STDBY_A2_STANDBY 0x0 + +#define ATV_TOP_OVERRIDE_SFR__A 0xC10026 +#define ATV_TOP_OVERRIDE_SFR__W 1 +#define ATV_TOP_OVERRIDE_SFR__M 0x1 +#define ATV_TOP_OVERRIDE_SFR__PRE 0x0 +#define ATV_TOP_OVERRIDE_SFR_ACTIVE 0x0 +#define ATV_TOP_OVERRIDE_SFR_OVERRIDE 0x1 + +#define ATV_TOP_SFR_VID_GAIN__A 0xC10027 +#define ATV_TOP_SFR_VID_GAIN__W 16 +#define ATV_TOP_SFR_VID_GAIN__M 0xFFFF +#define ATV_TOP_SFR_VID_GAIN__PRE 0x0 + +#define ATV_TOP_SFR_AGC_RES__A 0xC10028 +#define ATV_TOP_SFR_AGC_RES__W 5 +#define ATV_TOP_SFR_AGC_RES__M 0x1F +#define ATV_TOP_SFR_AGC_RES__PRE 0x0 + +#define ATV_TOP_OVM_COMP__A 0xC10029 +#define ATV_TOP_OVM_COMP__W 12 +#define ATV_TOP_OVM_COMP__M 0xFFF +#define ATV_TOP_OVM_COMP__PRE 0x0 +#define ATV_TOP_OUT_CONF__A 0xC1002A +#define ATV_TOP_OUT_CONF__W 5 +#define ATV_TOP_OUT_CONF__M 0x1F +#define ATV_TOP_OUT_CONF__PRE 0x0 + +#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__B 0 +#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__W 1 +#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__M 0x1 +#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__PRE 0x0 +#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN_UNSIGNED 0x0 +#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN_SIGNED 0x1 + +#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__B 1 +#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__W 1 +#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__M 0x2 +#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__PRE 0x0 +#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN_UNSIGNED 0x0 +#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN_SIGNED 0x2 + +#define ATV_TOP_OUT_CONF_SIF20_SIGN__B 2 +#define ATV_TOP_OUT_CONF_SIF20_SIGN__W 1 +#define ATV_TOP_OUT_CONF_SIF20_SIGN__M 0x4 +#define ATV_TOP_OUT_CONF_SIF20_SIGN__PRE 0x0 +#define ATV_TOP_OUT_CONF_SIF20_SIGN_UNSIGNED 0x0 +#define ATV_TOP_OUT_CONF_SIF20_SIGN_SIGNED 0x4 + +#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__B 3 +#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__W 1 +#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__M 0x8 +#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__PRE 0x0 +#define ATV_TOP_OUT_CONF_CVBS_DAC_BR_NORMAL 0x0 +#define ATV_TOP_OUT_CONF_CVBS_DAC_BR_BITREVERSED 0x8 + +#define ATV_TOP_OUT_CONF_SIF_DAC_BR__B 4 +#define ATV_TOP_OUT_CONF_SIF_DAC_BR__W 1 +#define ATV_TOP_OUT_CONF_SIF_DAC_BR__M 0x10 +#define ATV_TOP_OUT_CONF_SIF_DAC_BR__PRE 0x0 +#define ATV_TOP_OUT_CONF_SIF_DAC_BR_NORMAL 0x0 +#define ATV_TOP_OUT_CONF_SIF_DAC_BR_BITREVERSED 0x10 + +#define ATV_AFT_COMM_EXEC__A 0xFF0000 +#define ATV_AFT_COMM_EXEC__W 2 +#define ATV_AFT_COMM_EXEC__M 0x3 +#define ATV_AFT_COMM_EXEC__PRE 0x0 +#define ATV_AFT_COMM_EXEC_STOP 0x0 +#define ATV_AFT_COMM_EXEC_ACTIVE 0x1 +#define ATV_AFT_COMM_EXEC_HOLD 0x2 + +#define ATV_AFT_TST__A 0xFF0010 +#define ATV_AFT_TST__W 4 +#define ATV_AFT_TST__M 0xF +#define ATV_AFT_TST__PRE 0x0 + +#define AUD_COMM_EXEC__A 0x1000000 +#define AUD_COMM_EXEC__W 2 +#define AUD_COMM_EXEC__M 0x3 +#define AUD_COMM_EXEC__PRE 0x0 +#define AUD_COMM_EXEC_STOP 0x0 +#define AUD_COMM_EXEC_ACTIVE 0x1 + +#define AUD_COMM_MB__A 0x1000002 +#define AUD_COMM_MB__W 16 +#define AUD_COMM_MB__M 0xFFFF +#define AUD_COMM_MB__PRE 0x0 + +#define AUD_TOP_COMM_EXEC__A 0x1010000 +#define AUD_TOP_COMM_EXEC__W 2 +#define AUD_TOP_COMM_EXEC__M 0x3 +#define AUD_TOP_COMM_EXEC__PRE 0x0 +#define AUD_TOP_COMM_EXEC_STOP 0x0 +#define AUD_TOP_COMM_EXEC_ACTIVE 0x1 + +#define AUD_TOP_COMM_MB__A 0x1010002 +#define AUD_TOP_COMM_MB__W 16 +#define AUD_TOP_COMM_MB__M 0xFFFF +#define AUD_TOP_COMM_MB__PRE 0x0 + +#define AUD_TOP_COMM_MB_CTL__B 0 +#define AUD_TOP_COMM_MB_CTL__W 1 +#define AUD_TOP_COMM_MB_CTL__M 0x1 +#define AUD_TOP_COMM_MB_CTL__PRE 0x0 +#define AUD_TOP_COMM_MB_CTL_CTR_OFF 0x0 +#define AUD_TOP_COMM_MB_CTL_CTR_ON 0x1 + +#define AUD_TOP_COMM_MB_OBS__B 1 +#define AUD_TOP_COMM_MB_OBS__W 1 +#define AUD_TOP_COMM_MB_OBS__M 0x2 +#define AUD_TOP_COMM_MB_OBS__PRE 0x0 +#define AUD_TOP_COMM_MB_OBS_OBS_OFF 0x0 +#define AUD_TOP_COMM_MB_OBS_OBS_ON 0x2 + +#define AUD_TOP_COMM_MB_MUX_CTRL__B 2 +#define AUD_TOP_COMM_MB_MUX_CTRL__W 4 +#define AUD_TOP_COMM_MB_MUX_CTRL__M 0x3C +#define AUD_TOP_COMM_MB_MUX_CTRL__PRE 0x0 +#define AUD_TOP_COMM_MB_MUX_CTRL_DEMOD_TBO 0x0 +#define AUD_TOP_COMM_MB_MUX_CTRL_XDFP_IRQS 0x4 +#define AUD_TOP_COMM_MB_MUX_CTRL_OBSERVEPC 0x8 +#define AUD_TOP_COMM_MB_MUX_CTRL_SAOUT 0xC +#define AUD_TOP_COMM_MB_MUX_CTRL_XDFP_SCHEQ 0x10 + +#define AUD_TOP_COMM_MB_MUX_OBS__B 6 +#define AUD_TOP_COMM_MB_MUX_OBS__W 4 +#define AUD_TOP_COMM_MB_MUX_OBS__M 0x3C0 +#define AUD_TOP_COMM_MB_MUX_OBS__PRE 0x0 +#define AUD_TOP_COMM_MB_MUX_OBS_DEMOD_TBO 0x0 +#define AUD_TOP_COMM_MB_MUX_OBS_XDFP_IRQS 0x40 +#define AUD_TOP_COMM_MB_MUX_OBS_OBSERVEPC 0x80 +#define AUD_TOP_COMM_MB_MUX_OBS_SAOUT 0xC0 +#define AUD_TOP_COMM_MB_MUX_OBS_XDFP_SCHEQ 0x100 + +#define AUD_TOP_TR_MDE__A 0x1010010 +#define AUD_TOP_TR_MDE__W 5 +#define AUD_TOP_TR_MDE__M 0x1F +#define AUD_TOP_TR_MDE__PRE 0x18 + +#define AUD_TOP_TR_MDE_FIFO_SIZE__B 0 +#define AUD_TOP_TR_MDE_FIFO_SIZE__W 4 +#define AUD_TOP_TR_MDE_FIFO_SIZE__M 0xF +#define AUD_TOP_TR_MDE_FIFO_SIZE__PRE 0x8 + +#define AUD_TOP_TR_MDE_RD_LOCK__B 4 +#define AUD_TOP_TR_MDE_RD_LOCK__W 1 +#define AUD_TOP_TR_MDE_RD_LOCK__M 0x10 +#define AUD_TOP_TR_MDE_RD_LOCK__PRE 0x10 +#define AUD_TOP_TR_MDE_RD_LOCK_NORMAL 0x0 +#define AUD_TOP_TR_MDE_RD_LOCK_LOCK 0x10 + +#define AUD_TOP_TR_CTR__A 0x1010011 +#define AUD_TOP_TR_CTR__W 4 +#define AUD_TOP_TR_CTR__M 0xF +#define AUD_TOP_TR_CTR__PRE 0x0 + +#define AUD_TOP_TR_CTR_FIFO_RD_RDY__B 0 +#define AUD_TOP_TR_CTR_FIFO_RD_RDY__W 1 +#define AUD_TOP_TR_CTR_FIFO_RD_RDY__M 0x1 +#define AUD_TOP_TR_CTR_FIFO_RD_RDY__PRE 0x0 +#define AUD_TOP_TR_CTR_FIFO_RD_RDY_NOT_READY 0x0 +#define AUD_TOP_TR_CTR_FIFO_RD_RDY_READY 0x1 + +#define AUD_TOP_TR_CTR_FIFO_EMPTY__B 1 +#define AUD_TOP_TR_CTR_FIFO_EMPTY__W 1 +#define AUD_TOP_TR_CTR_FIFO_EMPTY__M 0x2 +#define AUD_TOP_TR_CTR_FIFO_EMPTY__PRE 0x0 +#define AUD_TOP_TR_CTR_FIFO_EMPTY_NOT_EMPTY 0x0 +#define AUD_TOP_TR_CTR_FIFO_EMPTY_EMPTY 0x2 + +#define AUD_TOP_TR_CTR_FIFO_LOCK__B 2 +#define AUD_TOP_TR_CTR_FIFO_LOCK__W 1 +#define AUD_TOP_TR_CTR_FIFO_LOCK__M 0x4 +#define AUD_TOP_TR_CTR_FIFO_LOCK__PRE 0x0 +#define AUD_TOP_TR_CTR_FIFO_LOCK_UNLOCKED 0x0 +#define AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED 0x4 + +#define AUD_TOP_TR_CTR_FIFO_FULL__B 3 +#define AUD_TOP_TR_CTR_FIFO_FULL__W 1 +#define AUD_TOP_TR_CTR_FIFO_FULL__M 0x8 +#define AUD_TOP_TR_CTR_FIFO_FULL__PRE 0x0 +#define AUD_TOP_TR_CTR_FIFO_FULL_EMPTY 0x0 +#define AUD_TOP_TR_CTR_FIFO_FULL_FULL 0x8 + +#define AUD_TOP_TR_RD_REG__A 0x1010012 +#define AUD_TOP_TR_RD_REG__W 16 +#define AUD_TOP_TR_RD_REG__M 0xFFFF +#define AUD_TOP_TR_RD_REG__PRE 0x0 + +#define AUD_TOP_TR_RD_REG_RESULT__B 0 +#define AUD_TOP_TR_RD_REG_RESULT__W 16 +#define AUD_TOP_TR_RD_REG_RESULT__M 0xFFFF +#define AUD_TOP_TR_RD_REG_RESULT__PRE 0x0 + +#define AUD_TOP_TR_TIMER__A 0x1010013 +#define AUD_TOP_TR_TIMER__W 16 +#define AUD_TOP_TR_TIMER__M 0xFFFF +#define AUD_TOP_TR_TIMER__PRE 0x0 + +#define AUD_TOP_TR_TIMER_CYCLES__B 0 +#define AUD_TOP_TR_TIMER_CYCLES__W 16 +#define AUD_TOP_TR_TIMER_CYCLES__M 0xFFFF +#define AUD_TOP_TR_TIMER_CYCLES__PRE 0x0 + +#define AUD_TOP_DEMOD_TBO_SEL__A 0x1010014 +#define AUD_TOP_DEMOD_TBO_SEL__W 5 +#define AUD_TOP_DEMOD_TBO_SEL__M 0x1F +#define AUD_TOP_DEMOD_TBO_SEL__PRE 0x0 + +#define AUD_DEM_WR_MODUS__A 0x1030030 +#define AUD_DEM_WR_MODUS__W 16 +#define AUD_DEM_WR_MODUS__M 0xFFFF +#define AUD_DEM_WR_MODUS__PRE 0x0 + +#define AUD_DEM_WR_MODUS_MOD_ASS__B 0 +#define AUD_DEM_WR_MODUS_MOD_ASS__W 1 +#define AUD_DEM_WR_MODUS_MOD_ASS__M 0x1 +#define AUD_DEM_WR_MODUS_MOD_ASS__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_ASS_OFF 0x0 +#define AUD_DEM_WR_MODUS_MOD_ASS_ON 0x1 + +#define AUD_DEM_WR_MODUS_MOD_STATINTERR__B 1 +#define AUD_DEM_WR_MODUS_MOD_STATINTERR__W 1 +#define AUD_DEM_WR_MODUS_MOD_STATINTERR__M 0x2 +#define AUD_DEM_WR_MODUS_MOD_STATINTERR__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_STATINTERR_DISABLE 0x0 +#define AUD_DEM_WR_MODUS_MOD_STATINTERR_ENABLE 0x2 + +#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__B 2 +#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__W 1 +#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__M 0x4 +#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG_ENABLED 0x0 +#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG_DISABLED 0x4 + +#define AUD_DEM_WR_MODUS_MOD_HDEV_A__B 8 +#define AUD_DEM_WR_MODUS_MOD_HDEV_A__W 1 +#define AUD_DEM_WR_MODUS_MOD_HDEV_A__M 0x100 +#define AUD_DEM_WR_MODUS_MOD_HDEV_A__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_HDEV_A_NORMAL 0x0 +#define AUD_DEM_WR_MODUS_MOD_HDEV_A_HIGH_DEVIATION 0x100 + +#define AUD_DEM_WR_MODUS_MOD_CM_A__B 9 +#define AUD_DEM_WR_MODUS_MOD_CM_A__W 1 +#define AUD_DEM_WR_MODUS_MOD_CM_A__M 0x200 +#define AUD_DEM_WR_MODUS_MOD_CM_A__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_CM_A_MUTE 0x0 +#define AUD_DEM_WR_MODUS_MOD_CM_A_NOISE 0x200 + +#define AUD_DEM_WR_MODUS_MOD_CM_B__B 10 +#define AUD_DEM_WR_MODUS_MOD_CM_B__W 1 +#define AUD_DEM_WR_MODUS_MOD_CM_B__M 0x400 +#define AUD_DEM_WR_MODUS_MOD_CM_B__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_CM_B_MUTE 0x0 +#define AUD_DEM_WR_MODUS_MOD_CM_B_NOISE 0x400 + +#define AUD_DEM_WR_MODUS_MOD_FMRADIO__B 11 +#define AUD_DEM_WR_MODUS_MOD_FMRADIO__W 1 +#define AUD_DEM_WR_MODUS_MOD_FMRADIO__M 0x800 +#define AUD_DEM_WR_MODUS_MOD_FMRADIO__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_FMRADIO_US_75U 0x0 +#define AUD_DEM_WR_MODUS_MOD_FMRADIO_EU_50U 0x800 + +#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__B 12 +#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__W 1 +#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__M 0x1000 +#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_6_5MHZ_SECAM 0x0 +#define AUD_DEM_WR_MODUS_MOD_6_5MHZ_D_K 0x1000 + +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__B 13 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__W 2 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__M 0x6000 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_KOREA 0x0 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_BTSC 0x2000 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_EIAJ 0x4000 +#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_CHROMA 0x6000 + +#define AUD_DEM_WR_MODUS_MOD_BTSC__B 15 +#define AUD_DEM_WR_MODUS_MOD_BTSC__W 1 +#define AUD_DEM_WR_MODUS_MOD_BTSC__M 0x8000 +#define AUD_DEM_WR_MODUS_MOD_BTSC__PRE 0x0 +#define AUD_DEM_WR_MODUS_MOD_BTSC_BTSC_STEREO 0x0 +#define AUD_DEM_WR_MODUS_MOD_BTSC_BTSC_SAP 0x8000 + +#define AUD_DEM_WR_STANDARD_SEL__A 0x1030020 +#define AUD_DEM_WR_STANDARD_SEL__W 16 +#define AUD_DEM_WR_STANDARD_SEL__M 0xFFFF +#define AUD_DEM_WR_STANDARD_SEL__PRE 0x0 + +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__B 0 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__W 12 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__M 0xFFF +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__PRE 0x0 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_AUTO 0x1 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_M_KOREA 0x2 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BG_FM 0x3 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K1 0x4 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K2 0x5 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K3 0x7 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BG_NICAM_FM 0x8 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_L_NICAM_AM 0x9 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_I_NICAM_FM 0xA +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K_NICAM_FM 0xB +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BTSC_STEREO 0x20 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BTSC_SAP 0x21 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_EIA_J 0x30 +#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_FM_RADIO 0x40 + +#define AUD_DEM_RD_STANDARD_RES__A 0x102007E +#define AUD_DEM_RD_STANDARD_RES__W 16 +#define AUD_DEM_RD_STANDARD_RES__M 0xFFFF +#define AUD_DEM_RD_STANDARD_RES__PRE 0x0 + +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__B 0 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__W 16 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__M 0xFFFF +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__PRE 0x0 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NO_SOUND_STANDARD 0x0 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NTSC_M_DUAL_CARRIER_FM 0x2 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_B_G_DUAL_CARRIER_FM 0x3 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K1_DUAL_CARRIER_FM 0x4 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K2_DUAL_CARRIER_FM 0x5 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K3_DUAL_CARRIER_FM 0x7 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_B_G_NICAM_FM 0x8 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_L_NICAM_AM 0x9 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_I_NICAM_FM 0xA +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K_NICAM_FM 0xB +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_BTSC_STEREO 0x20 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_BTSC_MONO_SAP 0x21 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NTSC_EIA_J 0x30 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_FM_RADIO 0x40 +#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_DETECTION_STILL_ACTIVE 0x7FF + +#define AUD_DEM_RD_STATUS__A 0x1020200 +#define AUD_DEM_RD_STATUS__W 16 +#define AUD_DEM_RD_STATUS__M 0xFFFF +#define AUD_DEM_RD_STATUS__PRE 0x0 + +#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__B 0 +#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__W 1 +#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__M 0x1 +#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_NEW_RDS_NO_RDS_DATA 0x0 +#define AUD_DEM_RD_STATUS_STAT_NEW_RDS_NEW_RDS_DATA 0x1 + +#define AUD_DEM_RD_STATUS_STAT_CARR_A__B 1 +#define AUD_DEM_RD_STATUS_STAT_CARR_A__W 1 +#define AUD_DEM_RD_STATUS_STAT_CARR_A__M 0x2 +#define AUD_DEM_RD_STATUS_STAT_CARR_A__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_CARR_A_DETECTED 0x0 +#define AUD_DEM_RD_STATUS_STAT_CARR_A_NOT_DETECTED 0x2 + +#define AUD_DEM_RD_STATUS_STAT_CARR_B__B 2 +#define AUD_DEM_RD_STATUS_STAT_CARR_B__W 1 +#define AUD_DEM_RD_STATUS_STAT_CARR_B__M 0x4 +#define AUD_DEM_RD_STATUS_STAT_CARR_B__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_CARR_B_DETECTED 0x0 +#define AUD_DEM_RD_STATUS_STAT_CARR_B_NOT_DETECTED 0x4 + +#define AUD_DEM_RD_STATUS_STAT_NICAM__B 5 +#define AUD_DEM_RD_STATUS_STAT_NICAM__W 1 +#define AUD_DEM_RD_STATUS_STAT_NICAM__M 0x20 +#define AUD_DEM_RD_STATUS_STAT_NICAM__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_NICAM_NO_NICAM 0x0 +#define AUD_DEM_RD_STATUS_STAT_NICAM_NICAM_DETECTED 0x20 + +#define AUD_DEM_RD_STATUS_STAT_STEREO__B 6 +#define AUD_DEM_RD_STATUS_STAT_STEREO__W 1 +#define AUD_DEM_RD_STATUS_STAT_STEREO__M 0x40 +#define AUD_DEM_RD_STATUS_STAT_STEREO__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_STEREO_NO_STEREO 0x0 +#define AUD_DEM_RD_STATUS_STAT_STEREO_STEREO 0x40 + +#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__B 7 +#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__W 1 +#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__M 0x80 +#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO_DEPENDENT_FM_MONO_PROGRAM 0x0 +#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO_INDEPENDENT_FM_MONO_PROGRAM 0x80 + +#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__B 8 +#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__W 1 +#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__M 0x100 +#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__PRE 0x0 +#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP_NO_SAP 0x0 +#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP_SAP 0x100 + +#define AUD_DEM_RD_STATUS_BAD_NICAM__B 9 +#define AUD_DEM_RD_STATUS_BAD_NICAM__W 1 +#define AUD_DEM_RD_STATUS_BAD_NICAM__M 0x200 +#define AUD_DEM_RD_STATUS_BAD_NICAM__PRE 0x0 +#define AUD_DEM_RD_STATUS_BAD_NICAM_OK 0x0 +#define AUD_DEM_RD_STATUS_BAD_NICAM_BAD 0x200 + +#define AUD_DEM_RD_RDS_ARRAY_CNT__A 0x102020F +#define AUD_DEM_RD_RDS_ARRAY_CNT__W 12 +#define AUD_DEM_RD_RDS_ARRAY_CNT__M 0xFFF +#define AUD_DEM_RD_RDS_ARRAY_CNT__PRE 0x0 + +#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__B 0 +#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__W 12 +#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__M 0xFFF +#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__PRE 0x0 +#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT_RDS_DATA_NOT_VALID 0xFFF + +#define AUD_DEM_RD_RDS_DATA__A 0x1020210 +#define AUD_DEM_RD_RDS_DATA__W 12 +#define AUD_DEM_RD_RDS_DATA__M 0xFFF +#define AUD_DEM_RD_RDS_DATA__PRE 0x0 + +#define AUD_DSP_WR_FM_PRESC__A 0x105000E +#define AUD_DSP_WR_FM_PRESC__W 16 +#define AUD_DSP_WR_FM_PRESC__M 0xFFFF +#define AUD_DSP_WR_FM_PRESC__PRE 0x0 + +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__B 8 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__W 8 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__M 0xFF00 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__PRE 0x0 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_28_KHZ_FM_DEVIATION 0x7F00 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_50_KHZ_FM_DEVIATION 0x4800 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_75_KHZ_FM_DEVIATION 0x3000 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_100_KHZ_FM_DEVIATION 0x2400 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_150_KHZ_FM_DEVIATION 0x1800 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_180_KHZ_FM_DEVIATION 0x1300 +#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_380_KHZ_FM_DEVIATION 0x900 + +#define AUD_DSP_WR_NICAM_PRESC__A 0x1050010 +#define AUD_DSP_WR_NICAM_PRESC__W 16 +#define AUD_DSP_WR_NICAM_PRESC__M 0xFFFF +#define AUD_DSP_WR_NICAM_PRESC__PRE 0x0 +#define AUD_DSP_WR_VOLUME__A 0x1050000 +#define AUD_DSP_WR_VOLUME__W 16 +#define AUD_DSP_WR_VOLUME__M 0xFFFF +#define AUD_DSP_WR_VOLUME__PRE 0x0 + +#define AUD_DSP_WR_VOLUME_VOL_MAIN__B 8 +#define AUD_DSP_WR_VOLUME_VOL_MAIN__W 8 +#define AUD_DSP_WR_VOLUME_VOL_MAIN__M 0xFF00 +#define AUD_DSP_WR_VOLUME_VOL_MAIN__PRE 0x0 + +#define AUD_DSP_WR_SRC_I2S_MATR__A 0x1050038 +#define AUD_DSP_WR_SRC_I2S_MATR__W 16 +#define AUD_DSP_WR_SRC_I2S_MATR__M 0xFFFF +#define AUD_DSP_WR_SRC_I2S_MATR__PRE 0x0 + +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__B 8 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__W 8 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__M 0xFF00 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__PRE 0x0 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_MONO 0x0 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_AB 0x100 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_A 0x300 +#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_B 0x400 + +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__B 0 +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__W 8 +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__M 0xFF +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__PRE 0x0 +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_SOUND_A 0x0 +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_SOUND_B 0x10 +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_STEREO 0x20 +#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_MONO 0x30 + +#define AUD_DSP_WR_AVC__A 0x1050029 +#define AUD_DSP_WR_AVC__W 16 +#define AUD_DSP_WR_AVC__M 0xFFFF +#define AUD_DSP_WR_AVC__PRE 0x0 + +#define AUD_DSP_WR_AVC_AVC_ON__B 14 +#define AUD_DSP_WR_AVC_AVC_ON__W 2 +#define AUD_DSP_WR_AVC_AVC_ON__M 0xC000 +#define AUD_DSP_WR_AVC_AVC_ON__PRE 0x0 +#define AUD_DSP_WR_AVC_AVC_ON_OFF 0x0 +#define AUD_DSP_WR_AVC_AVC_ON_ON 0xC000 + +#define AUD_DSP_WR_AVC_AVC_DECAY__B 8 +#define AUD_DSP_WR_AVC_AVC_DECAY__W 4 +#define AUD_DSP_WR_AVC_AVC_DECAY__M 0xF00 +#define AUD_DSP_WR_AVC_AVC_DECAY__PRE 0x0 +#define AUD_DSP_WR_AVC_AVC_DECAY_8_SEC 0x800 +#define AUD_DSP_WR_AVC_AVC_DECAY_4_SEC 0x400 +#define AUD_DSP_WR_AVC_AVC_DECAY_2_SEC 0x200 +#define AUD_DSP_WR_AVC_AVC_DECAY_20_MSEC 0x100 + +#define AUD_DSP_WR_AVC_AVC_REF_LEV__B 4 +#define AUD_DSP_WR_AVC_AVC_REF_LEV__W 4 +#define AUD_DSP_WR_AVC_AVC_REF_LEV__M 0xF0 +#define AUD_DSP_WR_AVC_AVC_REF_LEV__PRE 0x0 + +#define AUD_DSP_WR_AVC_AVC_MAX_ATT__B 2 +#define AUD_DSP_WR_AVC_AVC_MAX_ATT__W 2 +#define AUD_DSP_WR_AVC_AVC_MAX_ATT__M 0xC +#define AUD_DSP_WR_AVC_AVC_MAX_ATT__PRE 0x0 +#define AUD_DSP_WR_AVC_AVC_MAX_ATT_24DB 0x0 +#define AUD_DSP_WR_AVC_AVC_MAX_ATT_18DB 0x4 +#define AUD_DSP_WR_AVC_AVC_MAX_ATT_12DB 0x8 + +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__B 0 +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__W 2 +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__M 0x3 +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__PRE 0x0 +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_6DB 0x0 +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_12DB 0x1 +#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_0DB 0x3 + +#define AUD_DSP_WR_QPEAK__A 0x105000C +#define AUD_DSP_WR_QPEAK__W 16 +#define AUD_DSP_WR_QPEAK__M 0xFFFF +#define AUD_DSP_WR_QPEAK__PRE 0x0 + +#define AUD_DSP_WR_QPEAK_SRC_QP__B 8 +#define AUD_DSP_WR_QPEAK_SRC_QP__W 8 +#define AUD_DSP_WR_QPEAK_SRC_QP__M 0xFF00 +#define AUD_DSP_WR_QPEAK_SRC_QP__PRE 0x0 +#define AUD_DSP_WR_QPEAK_SRC_QP_MONO 0x0 +#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_AB 0x100 +#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_A 0x300 +#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_B 0x400 + +#define AUD_DSP_WR_QPEAK_MAT_QP__B 0 +#define AUD_DSP_WR_QPEAK_MAT_QP__W 8 +#define AUD_DSP_WR_QPEAK_MAT_QP__M 0xFF +#define AUD_DSP_WR_QPEAK_MAT_QP__PRE 0x0 +#define AUD_DSP_WR_QPEAK_MAT_QP_SOUND_A 0x0 +#define AUD_DSP_WR_QPEAK_MAT_QP_SOUND_B 0x10 +#define AUD_DSP_WR_QPEAK_MAT_QP_STEREO 0x20 +#define AUD_DSP_WR_QPEAK_MAT_QP_MONO 0x30 + +#define AUD_DSP_RD_QPEAK_L__A 0x1040019 +#define AUD_DSP_RD_QPEAK_L__W 16 +#define AUD_DSP_RD_QPEAK_L__M 0xFFFF +#define AUD_DSP_RD_QPEAK_L__PRE 0x0 + +#define AUD_DSP_RD_QPEAK_R__A 0x104001A +#define AUD_DSP_RD_QPEAK_R__W 16 +#define AUD_DSP_RD_QPEAK_R__M 0xFFFF +#define AUD_DSP_RD_QPEAK_R__PRE 0x0 + +#define AUD_DSP_WR_BEEPER__A 0x1050014 +#define AUD_DSP_WR_BEEPER__W 16 +#define AUD_DSP_WR_BEEPER__M 0xFFFF +#define AUD_DSP_WR_BEEPER__PRE 0x0 + +#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__B 8 +#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__W 7 +#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__M 0x7F00 +#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__PRE 0x0 + +#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__B 0 +#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__W 7 +#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__M 0x7F +#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__PRE 0x0 + +#define AUD_DEM_WR_I2S_CONFIG2__A 0x1030050 +#define AUD_DEM_WR_I2S_CONFIG2__W 16 +#define AUD_DEM_WR_I2S_CONFIG2__M 0xFFFF +#define AUD_DEM_WR_I2S_CONFIG2__PRE 0x0 + +#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__B 6 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__W 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__M 0x40 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__PRE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL_NORMAL 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL_INVERTED 0x40 + +#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__B 4 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__W 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__M 0x10 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__PRE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE_DISABLE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE_ENABLE 0x10 + +#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__B 3 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__W 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__M 0x8 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__PRE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST_MASTER 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST_SLAVE 0x8 + +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__B 2 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__W 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__M 0x4 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__PRE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL_LEFT_LOW 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL_LEFT_HIGH 0x4 + +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__B 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__W 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__M 0x2 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__PRE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE_NO_DELAY 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE_DELAY 0x2 + +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__B 0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__W 1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__M 0x1 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__PRE 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN_BIT_32 0x0 +#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN_BIT_16 0x1 + +#define AUD_DSP_WR_I2S_OUT_FS__A 0x105002A +#define AUD_DSP_WR_I2S_OUT_FS__W 16 +#define AUD_DSP_WR_I2S_OUT_FS__M 0xFFFF +#define AUD_DSP_WR_I2S_OUT_FS__PRE 0x0 + +#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__B 0 +#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__W 16 +#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__M 0xFFFF +#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__PRE 0x0 + +#define AUD_DSP_WR_AV_SYNC__A 0x105002B +#define AUD_DSP_WR_AV_SYNC__W 16 +#define AUD_DSP_WR_AV_SYNC__M 0xFFFF +#define AUD_DSP_WR_AV_SYNC__PRE 0x0 + +#define AUD_DSP_WR_AV_SYNC_AV_ON__B 15 +#define AUD_DSP_WR_AV_SYNC_AV_ON__W 1 +#define AUD_DSP_WR_AV_SYNC_AV_ON__M 0x8000 +#define AUD_DSP_WR_AV_SYNC_AV_ON__PRE 0x0 +#define AUD_DSP_WR_AV_SYNC_AV_ON_DISABLE 0x0 +#define AUD_DSP_WR_AV_SYNC_AV_ON_ENABLE 0x8000 + +#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__B 14 +#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__W 1 +#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__M 0x4000 +#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__PRE 0x0 +#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ_MONOCHROME 0x0 +#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ_NTSC 0x4000 + +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__B 0 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__W 2 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__M 0x3 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__PRE 0x0 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_AUTO 0x0 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_PAL_SECAM 0x1 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_NTSC 0x2 +#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_MONOCHROME 0x3 + +#define AUD_DSP_RD_STATUS2__A 0x104007B +#define AUD_DSP_RD_STATUS2__W 16 +#define AUD_DSP_RD_STATUS2__M 0xFFFF +#define AUD_DSP_RD_STATUS2__PRE 0x0 + +#define AUD_DSP_RD_STATUS2_AV_ACTIVE__B 15 +#define AUD_DSP_RD_STATUS2_AV_ACTIVE__W 1 +#define AUD_DSP_RD_STATUS2_AV_ACTIVE__M 0x8000 +#define AUD_DSP_RD_STATUS2_AV_ACTIVE__PRE 0x0 +#define AUD_DSP_RD_STATUS2_AV_ACTIVE_NO_SYNC 0x0 +#define AUD_DSP_RD_STATUS2_AV_ACTIVE_SYNC_ACTIVE 0x8000 + +#define AUD_DSP_RD_XDFP_FW__A 0x104001D +#define AUD_DSP_RD_XDFP_FW__W 16 +#define AUD_DSP_RD_XDFP_FW__M 0xFFFF +#define AUD_DSP_RD_XDFP_FW__PRE 0x344 + +#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__B 0 +#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__W 16 +#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__M 0xFFFF +#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__PRE 0x344 + +#define AUD_DSP_RD_XFP_FW__A 0x10404B8 +#define AUD_DSP_RD_XFP_FW__W 16 +#define AUD_DSP_RD_XFP_FW__M 0xFFFF +#define AUD_DSP_RD_XFP_FW__PRE 0x42 + +#define AUD_DSP_RD_XFP_FW_FP_FW_REV__B 0 +#define AUD_DSP_RD_XFP_FW_FP_FW_REV__W 16 +#define AUD_DSP_RD_XFP_FW_FP_FW_REV__M 0xFFFF +#define AUD_DSP_RD_XFP_FW_FP_FW_REV__PRE 0x42 + +#define AUD_DEM_WR_DCO_B_HI__A 0x103009B +#define AUD_DEM_WR_DCO_B_HI__W 16 +#define AUD_DEM_WR_DCO_B_HI__M 0xFFFF +#define AUD_DEM_WR_DCO_B_HI__PRE 0x0 + +#define AUD_DEM_WR_DCO_B_LO__A 0x1030093 +#define AUD_DEM_WR_DCO_B_LO__W 16 +#define AUD_DEM_WR_DCO_B_LO__M 0xFFFF +#define AUD_DEM_WR_DCO_B_LO__PRE 0x0 + +#define AUD_DEM_WR_DCO_A_HI__A 0x10300AB +#define AUD_DEM_WR_DCO_A_HI__W 16 +#define AUD_DEM_WR_DCO_A_HI__M 0xFFFF +#define AUD_DEM_WR_DCO_A_HI__PRE 0x0 + +#define AUD_DEM_WR_DCO_A_LO__A 0x10300A3 +#define AUD_DEM_WR_DCO_A_LO__W 16 +#define AUD_DEM_WR_DCO_A_LO__M 0xFFFF +#define AUD_DEM_WR_DCO_A_LO__PRE 0x0 +#define AUD_DEM_WR_NICAM_THRSHLD__A 0x1030021 +#define AUD_DEM_WR_NICAM_THRSHLD__W 16 +#define AUD_DEM_WR_NICAM_THRSHLD__M 0xFFFF +#define AUD_DEM_WR_NICAM_THRSHLD__PRE 0x2BC + +#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__B 0 +#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__W 12 +#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__M 0xFFF +#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__PRE 0x2BC + +#define AUD_DEM_WR_A2_THRSHLD__A 0x1030022 +#define AUD_DEM_WR_A2_THRSHLD__W 16 +#define AUD_DEM_WR_A2_THRSHLD__M 0xFFFF +#define AUD_DEM_WR_A2_THRSHLD__PRE 0x190 + +#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__B 0 +#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__W 12 +#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__M 0xFFF +#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__PRE 0x190 + +#define AUD_DEM_WR_BTSC_THRSHLD__A 0x1030023 +#define AUD_DEM_WR_BTSC_THRSHLD__W 16 +#define AUD_DEM_WR_BTSC_THRSHLD__M 0xFFFF +#define AUD_DEM_WR_BTSC_THRSHLD__PRE 0xC + +#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__B 0 +#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__W 12 +#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__M 0xFFF +#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__PRE 0xC + +#define AUD_DEM_WR_CM_A_THRSHLD__A 0x1030024 +#define AUD_DEM_WR_CM_A_THRSHLD__W 16 +#define AUD_DEM_WR_CM_A_THRSHLD__M 0xFFFF +#define AUD_DEM_WR_CM_A_THRSHLD__PRE 0x2A + +#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__B 0 +#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__W 12 +#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__M 0xFFF +#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__PRE 0x2A + +#define AUD_DEM_WR_CM_B_THRSHLD__A 0x1030025 +#define AUD_DEM_WR_CM_B_THRSHLD__W 16 +#define AUD_DEM_WR_CM_B_THRSHLD__M 0xFFFF +#define AUD_DEM_WR_CM_B_THRSHLD__PRE 0x2A + +#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__B 0 +#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__W 12 +#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__M 0xFFF +#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__PRE 0x2A + +#define AUD_DEM_RD_NIC_C_AD_BITS__A 0x1020023 +#define AUD_DEM_RD_NIC_C_AD_BITS__W 16 +#define AUD_DEM_RD_NIC_C_AD_BITS__M 0xFFFF +#define AUD_DEM_RD_NIC_C_AD_BITS__PRE 0x0 + +#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__B 0 +#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__W 1 +#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__M 0x1 +#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__PRE 0x0 +#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC_NOT_SYNCED 0x0 +#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC_SYNCED 0x1 + +#define AUD_DEM_RD_NIC_C_AD_BITS_C__B 1 +#define AUD_DEM_RD_NIC_C_AD_BITS_C__W 4 +#define AUD_DEM_RD_NIC_C_AD_BITS_C__M 0x1E +#define AUD_DEM_RD_NIC_C_AD_BITS_C__PRE 0x0 + +#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__B 5 +#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__W 3 +#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__M 0xE0 +#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__PRE 0x0 + +#define AUD_DEM_RD_NIC_ADD_BITS_HI__A 0x1020038 +#define AUD_DEM_RD_NIC_ADD_BITS_HI__W 16 +#define AUD_DEM_RD_NIC_ADD_BITS_HI__M 0xFFFF +#define AUD_DEM_RD_NIC_ADD_BITS_HI__PRE 0x0 + +#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__B 0 +#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__W 8 +#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__M 0xFF +#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__PRE 0x0 + +#define AUD_DEM_RD_NIC_CIB__A 0x1020038 +#define AUD_DEM_RD_NIC_CIB__W 16 +#define AUD_DEM_RD_NIC_CIB__M 0xFFFF +#define AUD_DEM_RD_NIC_CIB__PRE 0x0 + +#define AUD_DEM_RD_NIC_CIB_CIB2__B 0 +#define AUD_DEM_RD_NIC_CIB_CIB2__W 1 +#define AUD_DEM_RD_NIC_CIB_CIB2__M 0x1 +#define AUD_DEM_RD_NIC_CIB_CIB2__PRE 0x0 + +#define AUD_DEM_RD_NIC_CIB_CIB1__B 1 +#define AUD_DEM_RD_NIC_CIB_CIB1__W 1 +#define AUD_DEM_RD_NIC_CIB_CIB1__M 0x2 +#define AUD_DEM_RD_NIC_CIB_CIB1__PRE 0x0 + +#define AUD_DEM_RD_NIC_ERROR_RATE__A 0x1020057 +#define AUD_DEM_RD_NIC_ERROR_RATE__W 16 +#define AUD_DEM_RD_NIC_ERROR_RATE__M 0xFFFF +#define AUD_DEM_RD_NIC_ERROR_RATE__PRE 0x0 + +#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__B 0 +#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__W 12 +#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__M 0xFFF +#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__PRE 0x0 + +#define AUD_DEM_WR_FM_DEEMPH__A 0x103000F +#define AUD_DEM_WR_FM_DEEMPH__W 16 +#define AUD_DEM_WR_FM_DEEMPH__M 0xFFFF +#define AUD_DEM_WR_FM_DEEMPH__PRE 0x0 +#define AUD_DEM_WR_FM_DEEMPH_50US 0x0 +#define AUD_DEM_WR_FM_DEEMPH_75US 0x1 +#define AUD_DEM_WR_FM_DEEMPH_OFF 0x3F + +#define AUD_DEM_WR_FM_MATRIX__A 0x103006F +#define AUD_DEM_WR_FM_MATRIX__W 16 +#define AUD_DEM_WR_FM_MATRIX__M 0xFFFF +#define AUD_DEM_WR_FM_MATRIX__PRE 0x0 +#define AUD_DEM_WR_FM_MATRIX_NO_MATRIX 0x0 +#define AUD_DEM_WR_FM_MATRIX_GERMAN_MATRIX 0x1 +#define AUD_DEM_WR_FM_MATRIX_KOREAN_MATRIX 0x2 +#define AUD_DEM_WR_FM_MATRIX_SOUND_A 0x3 +#define AUD_DEM_WR_FM_MATRIX_SOUND_B 0x4 + +#define AUD_DSP_RD_FM_IDENT_VALUE__A 0x1040018 +#define AUD_DSP_RD_FM_IDENT_VALUE__W 16 +#define AUD_DSP_RD_FM_IDENT_VALUE__M 0xFFFF +#define AUD_DSP_RD_FM_IDENT_VALUE__PRE 0x0 + +#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__B 8 +#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__W 8 +#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__M 0xFF00 +#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__PRE 0x0 + +#define AUD_DSP_RD_FM_DC_LEVEL_A__A 0x104001B +#define AUD_DSP_RD_FM_DC_LEVEL_A__W 16 +#define AUD_DSP_RD_FM_DC_LEVEL_A__M 0xFFFF +#define AUD_DSP_RD_FM_DC_LEVEL_A__PRE 0x0 + +#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__B 0 +#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__W 16 +#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__M 0xFFFF +#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__PRE 0x0 + +#define AUD_DSP_RD_FM_DC_LEVEL_B__A 0x104001C +#define AUD_DSP_RD_FM_DC_LEVEL_B__W 16 +#define AUD_DSP_RD_FM_DC_LEVEL_B__M 0xFFFF +#define AUD_DSP_RD_FM_DC_LEVEL_B__PRE 0x0 + +#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__B 0 +#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__W 16 +#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__M 0xFFFF +#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__PRE 0x0 + +#define AUD_DEM_WR_FM_DC_NOTCH_SW__A 0x1030017 +#define AUD_DEM_WR_FM_DC_NOTCH_SW__W 16 +#define AUD_DEM_WR_FM_DC_NOTCH_SW__M 0xFFFF +#define AUD_DEM_WR_FM_DC_NOTCH_SW__PRE 0x0 + +#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__B 0 +#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__W 16 +#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__M 0xFFFF +#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__PRE 0x0 +#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW_ON 0x0 +#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW_OFF 0x3F + +#define AUD_DSP_WR_SYNC_OUT__A 0x1050026 +#define AUD_DSP_WR_SYNC_OUT__W 16 +#define AUD_DSP_WR_SYNC_OUT__M 0xFFFF +#define AUD_DSP_WR_SYNC_OUT__PRE 0x0 +#define AUD_DSP_WR_SYNC_OUT_OFF 0x0 +#define AUD_DSP_WR_SYNC_OUT_SYNCHRONOUS 0x1 + +#define AUD_XFP_DRAM_1K__A 0x1060000 +#define AUD_XFP_DRAM_1K__W 16 +#define AUD_XFP_DRAM_1K__M 0xFFFF +#define AUD_XFP_DRAM_1K__PRE 0x0 +#define AUD_XFP_DRAM_1K_D__B 0 +#define AUD_XFP_DRAM_1K_D__W 16 +#define AUD_XFP_DRAM_1K_D__M 0xFFFF +#define AUD_XFP_DRAM_1K_D__PRE 0x0 + +#define AUD_XFP_PRAM_4K__A 0x1070000 +#define AUD_XFP_PRAM_4K__W 16 +#define AUD_XFP_PRAM_4K__M 0xFFFF +#define AUD_XFP_PRAM_4K__PRE 0x0 +#define AUD_XFP_PRAM_4K_D__B 0 +#define AUD_XFP_PRAM_4K_D__W 16 +#define AUD_XFP_PRAM_4K_D__M 0xFFFF +#define AUD_XFP_PRAM_4K_D__PRE 0x0 + +#define AUD_XDFP_DRAM_1K__A 0x1080000 +#define AUD_XDFP_DRAM_1K__W 16 +#define AUD_XDFP_DRAM_1K__M 0xFFFF +#define AUD_XDFP_DRAM_1K__PRE 0x0 +#define AUD_XDFP_DRAM_1K_D__B 0 +#define AUD_XDFP_DRAM_1K_D__W 16 +#define AUD_XDFP_DRAM_1K_D__M 0xFFFF +#define AUD_XDFP_DRAM_1K_D__PRE 0x0 + +#define AUD_XDFP_PRAM_4K__A 0x1090000 +#define AUD_XDFP_PRAM_4K__W 16 +#define AUD_XDFP_PRAM_4K__M 0xFFFF +#define AUD_XDFP_PRAM_4K__PRE 0x0 +#define AUD_XDFP_PRAM_4K_D__B 0 +#define AUD_XDFP_PRAM_4K_D__W 16 +#define AUD_XDFP_PRAM_4K_D__M 0xFFFF +#define AUD_XDFP_PRAM_4K_D__PRE 0x0 + +#define FEC_COMM_EXEC__A 0x2400000 +#define FEC_COMM_EXEC__W 2 +#define FEC_COMM_EXEC__M 0x3 +#define FEC_COMM_EXEC__PRE 0x0 +#define FEC_COMM_EXEC_STOP 0x0 +#define FEC_COMM_EXEC_ACTIVE 0x1 +#define FEC_COMM_EXEC_HOLD 0x2 + +#define FEC_COMM_MB__A 0x2400002 +#define FEC_COMM_MB__W 16 +#define FEC_COMM_MB__M 0xFFFF +#define FEC_COMM_MB__PRE 0x0 +#define FEC_COMM_INT_REQ__A 0x2400003 +#define FEC_COMM_INT_REQ__W 16 +#define FEC_COMM_INT_REQ__M 0xFFFF +#define FEC_COMM_INT_REQ__PRE 0x0 +#define FEC_COMM_INT_REQ_OC_REQ__B 0 +#define FEC_COMM_INT_REQ_OC_REQ__W 1 +#define FEC_COMM_INT_REQ_OC_REQ__M 0x1 +#define FEC_COMM_INT_REQ_OC_REQ__PRE 0x0 +#define FEC_COMM_INT_REQ_RS_REQ__B 1 +#define FEC_COMM_INT_REQ_RS_REQ__W 1 +#define FEC_COMM_INT_REQ_RS_REQ__M 0x2 +#define FEC_COMM_INT_REQ_RS_REQ__PRE 0x0 +#define FEC_COMM_INT_REQ_DI_REQ__B 2 +#define FEC_COMM_INT_REQ_DI_REQ__W 1 +#define FEC_COMM_INT_REQ_DI_REQ__M 0x4 +#define FEC_COMM_INT_REQ_DI_REQ__PRE 0x0 + +#define FEC_COMM_INT_STA__A 0x2400005 +#define FEC_COMM_INT_STA__W 16 +#define FEC_COMM_INT_STA__M 0xFFFF +#define FEC_COMM_INT_STA__PRE 0x0 +#define FEC_COMM_INT_MSK__A 0x2400006 +#define FEC_COMM_INT_MSK__W 16 +#define FEC_COMM_INT_MSK__M 0xFFFF +#define FEC_COMM_INT_MSK__PRE 0x0 +#define FEC_COMM_INT_STM__A 0x2400007 +#define FEC_COMM_INT_STM__W 16 +#define FEC_COMM_INT_STM__M 0xFFFF +#define FEC_COMM_INT_STM__PRE 0x0 + +#define FEC_TOP_COMM_EXEC__A 0x2410000 +#define FEC_TOP_COMM_EXEC__W 2 +#define FEC_TOP_COMM_EXEC__M 0x3 +#define FEC_TOP_COMM_EXEC__PRE 0x0 +#define FEC_TOP_COMM_EXEC_STOP 0x0 +#define FEC_TOP_COMM_EXEC_ACTIVE 0x1 +#define FEC_TOP_COMM_EXEC_HOLD 0x2 + +#define FEC_TOP_ANNEX__A 0x2410010 +#define FEC_TOP_ANNEX__W 2 +#define FEC_TOP_ANNEX__M 0x3 +#define FEC_TOP_ANNEX__PRE 0x0 +#define FEC_TOP_ANNEX_A 0x0 +#define FEC_TOP_ANNEX_B 0x1 +#define FEC_TOP_ANNEX_C 0x2 +#define FEC_TOP_ANNEX_D 0x3 + +#define FEC_DI_COMM_EXEC__A 0x2420000 +#define FEC_DI_COMM_EXEC__W 2 +#define FEC_DI_COMM_EXEC__M 0x3 +#define FEC_DI_COMM_EXEC__PRE 0x0 +#define FEC_DI_COMM_EXEC_STOP 0x0 +#define FEC_DI_COMM_EXEC_ACTIVE 0x1 +#define FEC_DI_COMM_EXEC_HOLD 0x2 + +#define FEC_DI_COMM_MB__A 0x2420002 +#define FEC_DI_COMM_MB__W 2 +#define FEC_DI_COMM_MB__M 0x3 +#define FEC_DI_COMM_MB__PRE 0x0 +#define FEC_DI_COMM_MB_CTL__B 0 +#define FEC_DI_COMM_MB_CTL__W 1 +#define FEC_DI_COMM_MB_CTL__M 0x1 +#define FEC_DI_COMM_MB_CTL__PRE 0x0 +#define FEC_DI_COMM_MB_CTL_OFF 0x0 +#define FEC_DI_COMM_MB_CTL_ON 0x1 +#define FEC_DI_COMM_MB_OBS__B 1 +#define FEC_DI_COMM_MB_OBS__W 1 +#define FEC_DI_COMM_MB_OBS__M 0x2 +#define FEC_DI_COMM_MB_OBS__PRE 0x0 +#define FEC_DI_COMM_MB_OBS_OFF 0x0 +#define FEC_DI_COMM_MB_OBS_ON 0x2 + +#define FEC_DI_COMM_INT_REQ__A 0x2420003 +#define FEC_DI_COMM_INT_REQ__W 1 +#define FEC_DI_COMM_INT_REQ__M 0x1 +#define FEC_DI_COMM_INT_REQ__PRE 0x0 +#define FEC_DI_COMM_INT_STA__A 0x2420005 +#define FEC_DI_COMM_INT_STA__W 2 +#define FEC_DI_COMM_INT_STA__M 0x3 +#define FEC_DI_COMM_INT_STA__PRE 0x0 + +#define FEC_DI_COMM_INT_STA_STAT_INT__B 0 +#define FEC_DI_COMM_INT_STA_STAT_INT__W 1 +#define FEC_DI_COMM_INT_STA_STAT_INT__M 0x1 +#define FEC_DI_COMM_INT_STA_STAT_INT__PRE 0x0 + +#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__B 1 +#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__W 1 +#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__M 0x2 +#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__PRE 0x0 + +#define FEC_DI_COMM_INT_MSK__A 0x2420006 +#define FEC_DI_COMM_INT_MSK__W 2 +#define FEC_DI_COMM_INT_MSK__M 0x3 +#define FEC_DI_COMM_INT_MSK__PRE 0x0 +#define FEC_DI_COMM_INT_MSK_STAT_INT__B 0 +#define FEC_DI_COMM_INT_MSK_STAT_INT__W 1 +#define FEC_DI_COMM_INT_MSK_STAT_INT__M 0x1 +#define FEC_DI_COMM_INT_MSK_STAT_INT__PRE 0x0 +#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__B 1 +#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__W 1 +#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__M 0x2 +#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__PRE 0x0 + +#define FEC_DI_COMM_INT_STM__A 0x2420007 +#define FEC_DI_COMM_INT_STM__W 2 +#define FEC_DI_COMM_INT_STM__M 0x3 +#define FEC_DI_COMM_INT_STM__PRE 0x0 +#define FEC_DI_COMM_INT_STM_STAT_INT__B 0 +#define FEC_DI_COMM_INT_STM_STAT_INT__W 1 +#define FEC_DI_COMM_INT_STM_STAT_INT__M 0x1 +#define FEC_DI_COMM_INT_STM_STAT_INT__PRE 0x0 +#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__B 1 +#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__W 1 +#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__M 0x2 +#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__PRE 0x0 + +#define FEC_DI_STATUS__A 0x2420010 +#define FEC_DI_STATUS__W 1 +#define FEC_DI_STATUS__M 0x1 +#define FEC_DI_STATUS__PRE 0x0 +#define FEC_DI_MODE__A 0x2420011 +#define FEC_DI_MODE__W 3 +#define FEC_DI_MODE__M 0x7 +#define FEC_DI_MODE__PRE 0x0 + +#define FEC_DI_MODE_NO_SYNC__B 0 +#define FEC_DI_MODE_NO_SYNC__W 1 +#define FEC_DI_MODE_NO_SYNC__M 0x1 +#define FEC_DI_MODE_NO_SYNC__PRE 0x0 + +#define FEC_DI_MODE_IGNORE_LOST_SYNC__B 1 +#define FEC_DI_MODE_IGNORE_LOST_SYNC__W 1 +#define FEC_DI_MODE_IGNORE_LOST_SYNC__M 0x2 +#define FEC_DI_MODE_IGNORE_LOST_SYNC__PRE 0x0 + +#define FEC_DI_MODE_IGNORE_TIMEOUT__B 2 +#define FEC_DI_MODE_IGNORE_TIMEOUT__W 1 +#define FEC_DI_MODE_IGNORE_TIMEOUT__M 0x4 +#define FEC_DI_MODE_IGNORE_TIMEOUT__PRE 0x0 + +#define FEC_DI_CONTROL_WORD__A 0x2420012 +#define FEC_DI_CONTROL_WORD__W 4 +#define FEC_DI_CONTROL_WORD__M 0xF +#define FEC_DI_CONTROL_WORD__PRE 0x0 + +#define FEC_DI_RESTART__A 0x2420013 +#define FEC_DI_RESTART__W 1 +#define FEC_DI_RESTART__M 0x1 +#define FEC_DI_RESTART__PRE 0x0 + +#define FEC_DI_TIMEOUT_LO__A 0x2420014 +#define FEC_DI_TIMEOUT_LO__W 16 +#define FEC_DI_TIMEOUT_LO__M 0xFFFF +#define FEC_DI_TIMEOUT_LO__PRE 0x0 + +#define FEC_DI_TIMEOUT_HI__A 0x2420015 +#define FEC_DI_TIMEOUT_HI__W 8 +#define FEC_DI_TIMEOUT_HI__M 0xFF +#define FEC_DI_TIMEOUT_HI__PRE 0xA + +#define FEC_RS_COMM_EXEC__A 0x2430000 +#define FEC_RS_COMM_EXEC__W 2 +#define FEC_RS_COMM_EXEC__M 0x3 +#define FEC_RS_COMM_EXEC__PRE 0x0 +#define FEC_RS_COMM_EXEC_STOP 0x0 +#define FEC_RS_COMM_EXEC_ACTIVE 0x1 +#define FEC_RS_COMM_EXEC_HOLD 0x2 + +#define FEC_RS_COMM_MB__A 0x2430002 +#define FEC_RS_COMM_MB__W 2 +#define FEC_RS_COMM_MB__M 0x3 +#define FEC_RS_COMM_MB__PRE 0x0 +#define FEC_RS_COMM_MB_CTL__B 0 +#define FEC_RS_COMM_MB_CTL__W 1 +#define FEC_RS_COMM_MB_CTL__M 0x1 +#define FEC_RS_COMM_MB_CTL__PRE 0x0 +#define FEC_RS_COMM_MB_CTL_OFF 0x0 +#define FEC_RS_COMM_MB_CTL_ON 0x1 +#define FEC_RS_COMM_MB_OBS__B 1 +#define FEC_RS_COMM_MB_OBS__W 1 +#define FEC_RS_COMM_MB_OBS__M 0x2 +#define FEC_RS_COMM_MB_OBS__PRE 0x0 +#define FEC_RS_COMM_MB_OBS_OFF 0x0 +#define FEC_RS_COMM_MB_OBS_ON 0x2 + +#define FEC_RS_COMM_INT_REQ__A 0x2430003 +#define FEC_RS_COMM_INT_REQ__W 1 +#define FEC_RS_COMM_INT_REQ__M 0x1 +#define FEC_RS_COMM_INT_REQ__PRE 0x0 +#define FEC_RS_COMM_INT_STA__A 0x2430005 +#define FEC_RS_COMM_INT_STA__W 2 +#define FEC_RS_COMM_INT_STA__M 0x3 +#define FEC_RS_COMM_INT_STA__PRE 0x0 + +#define FEC_RS_COMM_INT_STA_FAILURE_INT__B 0 +#define FEC_RS_COMM_INT_STA_FAILURE_INT__W 1 +#define FEC_RS_COMM_INT_STA_FAILURE_INT__M 0x1 +#define FEC_RS_COMM_INT_STA_FAILURE_INT__PRE 0x0 + +#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__B 1 +#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__W 1 +#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__M 0x2 +#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__PRE 0x0 + +#define FEC_RS_COMM_INT_MSK__A 0x2430006 +#define FEC_RS_COMM_INT_MSK__W 2 +#define FEC_RS_COMM_INT_MSK__M 0x3 +#define FEC_RS_COMM_INT_MSK__PRE 0x0 +#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__B 0 +#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__W 1 +#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__M 0x1 +#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__PRE 0x0 +#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__B 1 +#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__W 1 +#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__M 0x2 +#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__PRE 0x0 + +#define FEC_RS_COMM_INT_STM__A 0x2430007 +#define FEC_RS_COMM_INT_STM__W 2 +#define FEC_RS_COMM_INT_STM__M 0x3 +#define FEC_RS_COMM_INT_STM__PRE 0x0 +#define FEC_RS_COMM_INT_STM_FAILURE_MSK__B 0 +#define FEC_RS_COMM_INT_STM_FAILURE_MSK__W 1 +#define FEC_RS_COMM_INT_STM_FAILURE_MSK__M 0x1 +#define FEC_RS_COMM_INT_STM_FAILURE_MSK__PRE 0x0 +#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__B 1 +#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__W 1 +#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__M 0x2 +#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__PRE 0x0 + +#define FEC_RS_STATUS__A 0x2430010 +#define FEC_RS_STATUS__W 1 +#define FEC_RS_STATUS__M 0x1 +#define FEC_RS_STATUS__PRE 0x0 +#define FEC_RS_MODE__A 0x2430011 +#define FEC_RS_MODE__W 1 +#define FEC_RS_MODE__M 0x1 +#define FEC_RS_MODE__PRE 0x0 + +#define FEC_RS_MODE_BYPASS__B 0 +#define FEC_RS_MODE_BYPASS__W 1 +#define FEC_RS_MODE_BYPASS__M 0x1 +#define FEC_RS_MODE_BYPASS__PRE 0x0 + +#define FEC_RS_MEASUREMENT_PERIOD__A 0x2430012 +#define FEC_RS_MEASUREMENT_PERIOD__W 16 +#define FEC_RS_MEASUREMENT_PERIOD__M 0xFFFF +#define FEC_RS_MEASUREMENT_PERIOD__PRE 0x1171 + +#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__B 0 +#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__W 16 +#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__M 0xFFFF +#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__PRE 0x1171 + +#define FEC_RS_MEASUREMENT_PRESCALE__A 0x2430013 +#define FEC_RS_MEASUREMENT_PRESCALE__W 16 +#define FEC_RS_MEASUREMENT_PRESCALE__M 0xFFFF +#define FEC_RS_MEASUREMENT_PRESCALE__PRE 0x1 + +#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__B 0 +#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__W 16 +#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__M 0xFFFF +#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__PRE 0x1 + +#define FEC_RS_NR_BIT_ERRORS__A 0x2430014 +#define FEC_RS_NR_BIT_ERRORS__W 16 +#define FEC_RS_NR_BIT_ERRORS__M 0xFFFF +#define FEC_RS_NR_BIT_ERRORS__PRE 0xFFFF + +#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__B 0 +#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__W 12 +#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M 0xFFF +#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__PRE 0xFFF + +#define FEC_RS_NR_BIT_ERRORS_EXP__B 12 +#define FEC_RS_NR_BIT_ERRORS_EXP__W 4 +#define FEC_RS_NR_BIT_ERRORS_EXP__M 0xF000 +#define FEC_RS_NR_BIT_ERRORS_EXP__PRE 0xF000 + +#define FEC_RS_NR_SYMBOL_ERRORS__A 0x2430015 +#define FEC_RS_NR_SYMBOL_ERRORS__W 16 +#define FEC_RS_NR_SYMBOL_ERRORS__M 0xFFFF +#define FEC_RS_NR_SYMBOL_ERRORS__PRE 0xFFFF + +#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__B 0 +#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__W 12 +#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__M 0xFFF +#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__PRE 0xFFF + +#define FEC_RS_NR_SYMBOL_ERRORS_EXP__B 12 +#define FEC_RS_NR_SYMBOL_ERRORS_EXP__W 4 +#define FEC_RS_NR_SYMBOL_ERRORS_EXP__M 0xF000 +#define FEC_RS_NR_SYMBOL_ERRORS_EXP__PRE 0xF000 + +#define FEC_RS_NR_PACKET_ERRORS__A 0x2430016 +#define FEC_RS_NR_PACKET_ERRORS__W 16 +#define FEC_RS_NR_PACKET_ERRORS__M 0xFFFF +#define FEC_RS_NR_PACKET_ERRORS__PRE 0xFFFF + +#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__B 0 +#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__W 12 +#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__M 0xFFF +#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__PRE 0xFFF + +#define FEC_RS_NR_PACKET_ERRORS_EXP__B 12 +#define FEC_RS_NR_PACKET_ERRORS_EXP__W 4 +#define FEC_RS_NR_PACKET_ERRORS_EXP__M 0xF000 +#define FEC_RS_NR_PACKET_ERRORS_EXP__PRE 0xF000 + +#define FEC_RS_NR_FAILURES__A 0x2430017 +#define FEC_RS_NR_FAILURES__W 16 +#define FEC_RS_NR_FAILURES__M 0xFFFF +#define FEC_RS_NR_FAILURES__PRE 0x0 + +#define FEC_RS_NR_FAILURES_FIXED_MANT__B 0 +#define FEC_RS_NR_FAILURES_FIXED_MANT__W 12 +#define FEC_RS_NR_FAILURES_FIXED_MANT__M 0xFFF +#define FEC_RS_NR_FAILURES_FIXED_MANT__PRE 0x0 + +#define FEC_RS_NR_FAILURES_EXP__B 12 +#define FEC_RS_NR_FAILURES_EXP__W 4 +#define FEC_RS_NR_FAILURES_EXP__M 0xF000 +#define FEC_RS_NR_FAILURES_EXP__PRE 0x0 + +#define FEC_OC_COMM_EXEC__A 0x2440000 +#define FEC_OC_COMM_EXEC__W 2 +#define FEC_OC_COMM_EXEC__M 0x3 +#define FEC_OC_COMM_EXEC__PRE 0x0 +#define FEC_OC_COMM_EXEC_STOP 0x0 +#define FEC_OC_COMM_EXEC_ACTIVE 0x1 +#define FEC_OC_COMM_EXEC_HOLD 0x2 + +#define FEC_OC_COMM_MB__A 0x2440002 +#define FEC_OC_COMM_MB__W 2 +#define FEC_OC_COMM_MB__M 0x3 +#define FEC_OC_COMM_MB__PRE 0x0 +#define FEC_OC_COMM_MB_CTL__B 0 +#define FEC_OC_COMM_MB_CTL__W 1 +#define FEC_OC_COMM_MB_CTL__M 0x1 +#define FEC_OC_COMM_MB_CTL__PRE 0x0 +#define FEC_OC_COMM_MB_CTL_OFF 0x0 +#define FEC_OC_COMM_MB_CTL_ON 0x1 +#define FEC_OC_COMM_MB_OBS__B 1 +#define FEC_OC_COMM_MB_OBS__W 1 +#define FEC_OC_COMM_MB_OBS__M 0x2 +#define FEC_OC_COMM_MB_OBS__PRE 0x0 +#define FEC_OC_COMM_MB_OBS_OFF 0x0 +#define FEC_OC_COMM_MB_OBS_ON 0x2 + +#define FEC_OC_COMM_INT_REQ__A 0x2440003 +#define FEC_OC_COMM_INT_REQ__W 1 +#define FEC_OC_COMM_INT_REQ__M 0x1 +#define FEC_OC_COMM_INT_REQ__PRE 0x0 +#define FEC_OC_COMM_INT_STA__A 0x2440005 +#define FEC_OC_COMM_INT_STA__W 8 +#define FEC_OC_COMM_INT_STA__M 0xFF +#define FEC_OC_COMM_INT_STA__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__B 0 +#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__W 1 +#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__M 0x1 +#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__B 1 +#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__W 1 +#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__M 0x2 +#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__B 2 +#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__W 1 +#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__M 0x4 +#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__B 3 +#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__W 1 +#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__M 0x8 +#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__B 4 +#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__W 1 +#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__M 0x10 +#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__B 5 +#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__W 1 +#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__M 0x20 +#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__B 6 +#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__W 1 +#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__M 0x40 +#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__B 7 +#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__W 1 +#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__M 0x80 +#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__PRE 0x0 + +#define FEC_OC_COMM_INT_MSK__A 0x2440006 +#define FEC_OC_COMM_INT_MSK__W 8 +#define FEC_OC_COMM_INT_MSK__M 0xFF +#define FEC_OC_COMM_INT_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__B 0 +#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__M 0x1 +#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__B 1 +#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__M 0x2 +#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__B 2 +#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__M 0x4 +#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__B 3 +#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__M 0x8 +#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__B 4 +#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__M 0x10 +#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__B 5 +#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__M 0x20 +#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__B 6 +#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__M 0x40 +#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__B 7 +#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__W 1 +#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__M 0x80 +#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__PRE 0x0 + +#define FEC_OC_COMM_INT_STM__A 0x2440007 +#define FEC_OC_COMM_INT_STM__W 8 +#define FEC_OC_COMM_INT_STM__M 0xFF +#define FEC_OC_COMM_INT_STM__PRE 0x0 +#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__B 0 +#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__W 1 +#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__M 0x1 +#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__B 1 +#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__W 1 +#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__M 0x2 +#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__B 2 +#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__W 1 +#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__M 0x4 +#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__B 3 +#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__W 1 +#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__M 0x8 +#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__B 4 +#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__W 1 +#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__M 0x10 +#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__B 5 +#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__W 1 +#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__M 0x20 +#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__B 6 +#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__W 1 +#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__M 0x40 +#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__PRE 0x0 +#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__B 7 +#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__W 1 +#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__M 0x80 +#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__PRE 0x0 + +#define FEC_OC_STATUS__A 0x2440010 +#define FEC_OC_STATUS__W 5 +#define FEC_OC_STATUS__M 0x1F +#define FEC_OC_STATUS__PRE 0x0 + +#define FEC_OC_STATUS_DPR_STATUS__B 0 +#define FEC_OC_STATUS_DPR_STATUS__W 1 +#define FEC_OC_STATUS_DPR_STATUS__M 0x1 +#define FEC_OC_STATUS_DPR_STATUS__PRE 0x0 + +#define FEC_OC_STATUS_SNC_STATUS__B 1 +#define FEC_OC_STATUS_SNC_STATUS__W 2 +#define FEC_OC_STATUS_SNC_STATUS__M 0x6 +#define FEC_OC_STATUS_SNC_STATUS__PRE 0x0 + +#define FEC_OC_STATUS_FIFO_FULL__B 3 +#define FEC_OC_STATUS_FIFO_FULL__W 1 +#define FEC_OC_STATUS_FIFO_FULL__M 0x8 +#define FEC_OC_STATUS_FIFO_FULL__PRE 0x0 + +#define FEC_OC_STATUS_FIFO_EMPTY__B 4 +#define FEC_OC_STATUS_FIFO_EMPTY__W 1 +#define FEC_OC_STATUS_FIFO_EMPTY__M 0x10 +#define FEC_OC_STATUS_FIFO_EMPTY__PRE 0x0 + +#define FEC_OC_MODE__A 0x2440011 +#define FEC_OC_MODE__W 4 +#define FEC_OC_MODE__M 0xF +#define FEC_OC_MODE__PRE 0x0 + +#define FEC_OC_MODE_PARITY__B 0 +#define FEC_OC_MODE_PARITY__W 1 +#define FEC_OC_MODE_PARITY__M 0x1 +#define FEC_OC_MODE_PARITY__PRE 0x0 + +#define FEC_OC_MODE_TRANSPARENT__B 1 +#define FEC_OC_MODE_TRANSPARENT__W 1 +#define FEC_OC_MODE_TRANSPARENT__M 0x2 +#define FEC_OC_MODE_TRANSPARENT__PRE 0x0 + +#define FEC_OC_MODE_CLEAR__B 2 +#define FEC_OC_MODE_CLEAR__W 1 +#define FEC_OC_MODE_CLEAR__M 0x4 +#define FEC_OC_MODE_CLEAR__PRE 0x0 + +#define FEC_OC_MODE_RETAIN_FRAMING__B 3 +#define FEC_OC_MODE_RETAIN_FRAMING__W 1 +#define FEC_OC_MODE_RETAIN_FRAMING__M 0x8 +#define FEC_OC_MODE_RETAIN_FRAMING__PRE 0x0 + +#define FEC_OC_DPR_MODE__A 0x2440012 +#define FEC_OC_DPR_MODE__W 2 +#define FEC_OC_DPR_MODE__M 0x3 +#define FEC_OC_DPR_MODE__PRE 0x0 + +#define FEC_OC_DPR_MODE_ERR_DISABLE__B 0 +#define FEC_OC_DPR_MODE_ERR_DISABLE__W 1 +#define FEC_OC_DPR_MODE_ERR_DISABLE__M 0x1 +#define FEC_OC_DPR_MODE_ERR_DISABLE__PRE 0x0 + +#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__B 1 +#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__W 1 +#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__M 0x2 +#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__PRE 0x0 + +#define FEC_OC_DPR_UNLOCK__A 0x2440013 +#define FEC_OC_DPR_UNLOCK__W 1 +#define FEC_OC_DPR_UNLOCK__M 0x1 +#define FEC_OC_DPR_UNLOCK__PRE 0x0 +#define FEC_OC_DTO_MODE__A 0x2440014 +#define FEC_OC_DTO_MODE__W 3 +#define FEC_OC_DTO_MODE__M 0x7 +#define FEC_OC_DTO_MODE__PRE 0x0 + +#define FEC_OC_DTO_MODE_DYNAMIC__B 0 +#define FEC_OC_DTO_MODE_DYNAMIC__W 1 +#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1 +#define FEC_OC_DTO_MODE_DYNAMIC__PRE 0x0 + +#define FEC_OC_DTO_MODE_DUTY_CYCLE__B 1 +#define FEC_OC_DTO_MODE_DUTY_CYCLE__W 1 +#define FEC_OC_DTO_MODE_DUTY_CYCLE__M 0x2 +#define FEC_OC_DTO_MODE_DUTY_CYCLE__PRE 0x0 + +#define FEC_OC_DTO_MODE_OFFSET_ENABLE__B 2 +#define FEC_OC_DTO_MODE_OFFSET_ENABLE__W 1 +#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4 +#define FEC_OC_DTO_MODE_OFFSET_ENABLE__PRE 0x0 + +#define FEC_OC_DTO_PERIOD__A 0x2440015 +#define FEC_OC_DTO_PERIOD__W 8 +#define FEC_OC_DTO_PERIOD__M 0xFF +#define FEC_OC_DTO_PERIOD__PRE 0x0 +#define FEC_OC_DTO_RATE_LO__A 0x2440016 +#define FEC_OC_DTO_RATE_LO__W 16 +#define FEC_OC_DTO_RATE_LO__M 0xFFFF +#define FEC_OC_DTO_RATE_LO__PRE 0x0 + +#define FEC_OC_DTO_RATE_LO_RATE_LO__B 0 +#define FEC_OC_DTO_RATE_LO_RATE_LO__W 16 +#define FEC_OC_DTO_RATE_LO_RATE_LO__M 0xFFFF +#define FEC_OC_DTO_RATE_LO_RATE_LO__PRE 0x0 + +#define FEC_OC_DTO_RATE_HI__A 0x2440017 +#define FEC_OC_DTO_RATE_HI__W 10 +#define FEC_OC_DTO_RATE_HI__M 0x3FF +#define FEC_OC_DTO_RATE_HI__PRE 0xC0 + +#define FEC_OC_DTO_RATE_HI_RATE_HI__B 0 +#define FEC_OC_DTO_RATE_HI_RATE_HI__W 10 +#define FEC_OC_DTO_RATE_HI_RATE_HI__M 0x3FF +#define FEC_OC_DTO_RATE_HI_RATE_HI__PRE 0xC0 + +#define FEC_OC_DTO_BURST_LEN__A 0x2440018 +#define FEC_OC_DTO_BURST_LEN__W 8 +#define FEC_OC_DTO_BURST_LEN__M 0xFF +#define FEC_OC_DTO_BURST_LEN__PRE 0xBC + +#define FEC_OC_DTO_BURST_LEN_BURST_LEN__B 0 +#define FEC_OC_DTO_BURST_LEN_BURST_LEN__W 8 +#define FEC_OC_DTO_BURST_LEN_BURST_LEN__M 0xFF +#define FEC_OC_DTO_BURST_LEN_BURST_LEN__PRE 0xBC + +#define FEC_OC_FCT_MODE__A 0x244001A +#define FEC_OC_FCT_MODE__W 2 +#define FEC_OC_FCT_MODE__M 0x3 +#define FEC_OC_FCT_MODE__PRE 0x0 + +#define FEC_OC_FCT_MODE_RAT_ENA__B 0 +#define FEC_OC_FCT_MODE_RAT_ENA__W 1 +#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1 +#define FEC_OC_FCT_MODE_RAT_ENA__PRE 0x0 + +#define FEC_OC_FCT_MODE_VIRT_ENA__B 1 +#define FEC_OC_FCT_MODE_VIRT_ENA__W 1 +#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2 +#define FEC_OC_FCT_MODE_VIRT_ENA__PRE 0x0 + +#define FEC_OC_FCT_USAGE__A 0x244001B +#define FEC_OC_FCT_USAGE__W 3 +#define FEC_OC_FCT_USAGE__M 0x7 +#define FEC_OC_FCT_USAGE__PRE 0x2 + +#define FEC_OC_FCT_USAGE_USAGE__B 0 +#define FEC_OC_FCT_USAGE_USAGE__W 3 +#define FEC_OC_FCT_USAGE_USAGE__M 0x7 +#define FEC_OC_FCT_USAGE_USAGE__PRE 0x2 + +#define FEC_OC_FCT_OCCUPATION__A 0x244001C +#define FEC_OC_FCT_OCCUPATION__W 12 +#define FEC_OC_FCT_OCCUPATION__M 0xFFF +#define FEC_OC_FCT_OCCUPATION__PRE 0x0 + +#define FEC_OC_FCT_OCCUPATION_OCCUPATION__B 0 +#define FEC_OC_FCT_OCCUPATION_OCCUPATION__W 12 +#define FEC_OC_FCT_OCCUPATION_OCCUPATION__M 0xFFF +#define FEC_OC_FCT_OCCUPATION_OCCUPATION__PRE 0x0 + +#define FEC_OC_TMD_MODE__A 0x244001E +#define FEC_OC_TMD_MODE__W 3 +#define FEC_OC_TMD_MODE__M 0x7 +#define FEC_OC_TMD_MODE__PRE 0x4 + +#define FEC_OC_TMD_MODE_MODE__B 0 +#define FEC_OC_TMD_MODE_MODE__W 3 +#define FEC_OC_TMD_MODE_MODE__M 0x7 +#define FEC_OC_TMD_MODE_MODE__PRE 0x4 + +#define FEC_OC_TMD_COUNT__A 0x244001F +#define FEC_OC_TMD_COUNT__W 10 +#define FEC_OC_TMD_COUNT__M 0x3FF +#define FEC_OC_TMD_COUNT__PRE 0x1F4 + +#define FEC_OC_TMD_COUNT_COUNT__B 0 +#define FEC_OC_TMD_COUNT_COUNT__W 10 +#define FEC_OC_TMD_COUNT_COUNT__M 0x3FF +#define FEC_OC_TMD_COUNT_COUNT__PRE 0x1F4 + +#define FEC_OC_TMD_HI_MARGIN__A 0x2440020 +#define FEC_OC_TMD_HI_MARGIN__W 11 +#define FEC_OC_TMD_HI_MARGIN__M 0x7FF +#define FEC_OC_TMD_HI_MARGIN__PRE 0x200 + +#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__B 0 +#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__W 11 +#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__M 0x7FF +#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__PRE 0x200 + +#define FEC_OC_TMD_LO_MARGIN__A 0x2440021 +#define FEC_OC_TMD_LO_MARGIN__W 11 +#define FEC_OC_TMD_LO_MARGIN__M 0x7FF +#define FEC_OC_TMD_LO_MARGIN__PRE 0x100 + +#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__B 0 +#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__W 11 +#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__M 0x7FF +#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__PRE 0x100 + +#define FEC_OC_TMD_CTL_UPD_RATE__A 0x2440022 +#define FEC_OC_TMD_CTL_UPD_RATE__W 4 +#define FEC_OC_TMD_CTL_UPD_RATE__M 0xF +#define FEC_OC_TMD_CTL_UPD_RATE__PRE 0x1 + +#define FEC_OC_TMD_CTL_UPD_RATE_RATE__B 0 +#define FEC_OC_TMD_CTL_UPD_RATE_RATE__W 4 +#define FEC_OC_TMD_CTL_UPD_RATE_RATE__M 0xF +#define FEC_OC_TMD_CTL_UPD_RATE_RATE__PRE 0x1 + +#define FEC_OC_TMD_INT_UPD_RATE__A 0x2440023 +#define FEC_OC_TMD_INT_UPD_RATE__W 4 +#define FEC_OC_TMD_INT_UPD_RATE__M 0xF +#define FEC_OC_TMD_INT_UPD_RATE__PRE 0x4 + +#define FEC_OC_TMD_INT_UPD_RATE_RATE__B 0 +#define FEC_OC_TMD_INT_UPD_RATE_RATE__W 4 +#define FEC_OC_TMD_INT_UPD_RATE_RATE__M 0xF +#define FEC_OC_TMD_INT_UPD_RATE_RATE__PRE 0x4 + +#define FEC_OC_AVR_PARM_A__A 0x2440026 +#define FEC_OC_AVR_PARM_A__W 4 +#define FEC_OC_AVR_PARM_A__M 0xF +#define FEC_OC_AVR_PARM_A__PRE 0x6 + +#define FEC_OC_AVR_PARM_A_PARM__B 0 +#define FEC_OC_AVR_PARM_A_PARM__W 4 +#define FEC_OC_AVR_PARM_A_PARM__M 0xF +#define FEC_OC_AVR_PARM_A_PARM__PRE 0x6 + +#define FEC_OC_AVR_PARM_B__A 0x2440027 +#define FEC_OC_AVR_PARM_B__W 4 +#define FEC_OC_AVR_PARM_B__M 0xF +#define FEC_OC_AVR_PARM_B__PRE 0x4 + +#define FEC_OC_AVR_PARM_B_PARM__B 0 +#define FEC_OC_AVR_PARM_B_PARM__W 4 +#define FEC_OC_AVR_PARM_B_PARM__M 0xF +#define FEC_OC_AVR_PARM_B_PARM__PRE 0x4 + +#define FEC_OC_AVR_AVG_LO__A 0x2440028 +#define FEC_OC_AVR_AVG_LO__W 16 +#define FEC_OC_AVR_AVG_LO__M 0xFFFF +#define FEC_OC_AVR_AVG_LO__PRE 0x0 + +#define FEC_OC_AVR_AVG_LO_AVG_LO__B 0 +#define FEC_OC_AVR_AVG_LO_AVG_LO__W 16 +#define FEC_OC_AVR_AVG_LO_AVG_LO__M 0xFFFF +#define FEC_OC_AVR_AVG_LO_AVG_LO__PRE 0x0 + +#define FEC_OC_AVR_AVG_HI__A 0x2440029 +#define FEC_OC_AVR_AVG_HI__W 6 +#define FEC_OC_AVR_AVG_HI__M 0x3F +#define FEC_OC_AVR_AVG_HI__PRE 0x0 + +#define FEC_OC_AVR_AVG_HI_AVG_HI__B 0 +#define FEC_OC_AVR_AVG_HI_AVG_HI__W 6 +#define FEC_OC_AVR_AVG_HI_AVG_HI__M 0x3F +#define FEC_OC_AVR_AVG_HI_AVG_HI__PRE 0x0 + +#define FEC_OC_RCN_MODE__A 0x244002C +#define FEC_OC_RCN_MODE__W 5 +#define FEC_OC_RCN_MODE__M 0x1F +#define FEC_OC_RCN_MODE__PRE 0x1F + +#define FEC_OC_RCN_MODE_MODE__B 0 +#define FEC_OC_RCN_MODE_MODE__W 5 +#define FEC_OC_RCN_MODE_MODE__M 0x1F +#define FEC_OC_RCN_MODE_MODE__PRE 0x1F + +#define FEC_OC_RCN_OCC_SETTLE__A 0x244002D +#define FEC_OC_RCN_OCC_SETTLE__W 11 +#define FEC_OC_RCN_OCC_SETTLE__M 0x7FF +#define FEC_OC_RCN_OCC_SETTLE__PRE 0x180 + +#define FEC_OC_RCN_OCC_SETTLE_LEVEL__B 0 +#define FEC_OC_RCN_OCC_SETTLE_LEVEL__W 11 +#define FEC_OC_RCN_OCC_SETTLE_LEVEL__M 0x7FF +#define FEC_OC_RCN_OCC_SETTLE_LEVEL__PRE 0x180 + +#define FEC_OC_RCN_GAIN__A 0x244002E +#define FEC_OC_RCN_GAIN__W 4 +#define FEC_OC_RCN_GAIN__M 0xF +#define FEC_OC_RCN_GAIN__PRE 0xC + +#define FEC_OC_RCN_GAIN_GAIN__B 0 +#define FEC_OC_RCN_GAIN_GAIN__W 4 +#define FEC_OC_RCN_GAIN_GAIN__M 0xF +#define FEC_OC_RCN_GAIN_GAIN__PRE 0xC + +#define FEC_OC_RCN_CTL_RATE_LO__A 0x2440030 +#define FEC_OC_RCN_CTL_RATE_LO__W 16 +#define FEC_OC_RCN_CTL_RATE_LO__M 0xFFFF +#define FEC_OC_RCN_CTL_RATE_LO__PRE 0x0 + +#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__B 0 +#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__W 16 +#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__M 0xFFFF +#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__PRE 0x0 + +#define FEC_OC_RCN_CTL_RATE_HI__A 0x2440031 +#define FEC_OC_RCN_CTL_RATE_HI__W 8 +#define FEC_OC_RCN_CTL_RATE_HI__M 0xFF +#define FEC_OC_RCN_CTL_RATE_HI__PRE 0xC0 + +#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__B 0 +#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__W 8 +#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__M 0xFF +#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__PRE 0xC0 + +#define FEC_OC_RCN_CTL_STEP_LO__A 0x2440032 +#define FEC_OC_RCN_CTL_STEP_LO__W 16 +#define FEC_OC_RCN_CTL_STEP_LO__M 0xFFFF +#define FEC_OC_RCN_CTL_STEP_LO__PRE 0x0 + +#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__B 0 +#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__W 16 +#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__M 0xFFFF +#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__PRE 0x0 + +#define FEC_OC_RCN_CTL_STEP_HI__A 0x2440033 +#define FEC_OC_RCN_CTL_STEP_HI__W 8 +#define FEC_OC_RCN_CTL_STEP_HI__M 0xFF +#define FEC_OC_RCN_CTL_STEP_HI__PRE 0x8 + +#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__B 0 +#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__W 8 +#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__M 0xFF +#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__PRE 0x8 + +#define FEC_OC_RCN_DTO_OFS_LO__A 0x2440034 +#define FEC_OC_RCN_DTO_OFS_LO__W 16 +#define FEC_OC_RCN_DTO_OFS_LO__M 0xFFFF +#define FEC_OC_RCN_DTO_OFS_LO__PRE 0x0 + +#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__B 0 +#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__W 16 +#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__M 0xFFFF +#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__PRE 0x0 + +#define FEC_OC_RCN_DTO_OFS_HI__A 0x2440035 +#define FEC_OC_RCN_DTO_OFS_HI__W 8 +#define FEC_OC_RCN_DTO_OFS_HI__M 0xFF +#define FEC_OC_RCN_DTO_OFS_HI__PRE 0x0 + +#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__B 0 +#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__W 8 +#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__M 0xFF +#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__PRE 0x0 + +#define FEC_OC_RCN_DTO_RATE_LO__A 0x2440036 +#define FEC_OC_RCN_DTO_RATE_LO__W 16 +#define FEC_OC_RCN_DTO_RATE_LO__M 0xFFFF +#define FEC_OC_RCN_DTO_RATE_LO__PRE 0x0 + +#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__B 0 +#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__W 16 +#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__M 0xFFFF +#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__PRE 0x0 + +#define FEC_OC_RCN_DTO_RATE_HI__A 0x2440037 +#define FEC_OC_RCN_DTO_RATE_HI__W 8 +#define FEC_OC_RCN_DTO_RATE_HI__M 0xFF +#define FEC_OC_RCN_DTO_RATE_HI__PRE 0x0 + +#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__B 0 +#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__W 8 +#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__M 0xFF +#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__PRE 0x0 + +#define FEC_OC_RCN_RATE_CLIP_LO__A 0x2440038 +#define FEC_OC_RCN_RATE_CLIP_LO__W 16 +#define FEC_OC_RCN_RATE_CLIP_LO__M 0xFFFF +#define FEC_OC_RCN_RATE_CLIP_LO__PRE 0x0 + +#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__B 0 +#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__W 16 +#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__M 0xFFFF +#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__PRE 0x0 + +#define FEC_OC_RCN_RATE_CLIP_HI__A 0x2440039 +#define FEC_OC_RCN_RATE_CLIP_HI__W 8 +#define FEC_OC_RCN_RATE_CLIP_HI__M 0xFF +#define FEC_OC_RCN_RATE_CLIP_HI__PRE 0xF0 + +#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__B 0 +#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__W 8 +#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__M 0xFF +#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__PRE 0xF0 + +#define FEC_OC_RCN_DYN_RATE_LO__A 0x244003A +#define FEC_OC_RCN_DYN_RATE_LO__W 16 +#define FEC_OC_RCN_DYN_RATE_LO__M 0xFFFF +#define FEC_OC_RCN_DYN_RATE_LO__PRE 0x0 + +#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__B 0 +#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__W 16 +#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__M 0xFFFF +#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__PRE 0x0 + +#define FEC_OC_RCN_DYN_RATE_HI__A 0x244003B +#define FEC_OC_RCN_DYN_RATE_HI__W 8 +#define FEC_OC_RCN_DYN_RATE_HI__M 0xFF +#define FEC_OC_RCN_DYN_RATE_HI__PRE 0x0 + +#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__B 0 +#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__W 8 +#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__M 0xFF +#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__PRE 0x0 + +#define FEC_OC_SNC_MODE__A 0x2440040 +#define FEC_OC_SNC_MODE__W 4 +#define FEC_OC_SNC_MODE__M 0xF +#define FEC_OC_SNC_MODE__PRE 0x0 + +#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__B 0 +#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__W 1 +#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__M 0x1 +#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__PRE 0x0 + +#define FEC_OC_SNC_MODE_ERROR_CTL__B 1 +#define FEC_OC_SNC_MODE_ERROR_CTL__W 2 +#define FEC_OC_SNC_MODE_ERROR_CTL__M 0x6 +#define FEC_OC_SNC_MODE_ERROR_CTL__PRE 0x0 + +#define FEC_OC_SNC_MODE_CORR_DISABLE__B 3 +#define FEC_OC_SNC_MODE_CORR_DISABLE__W 1 +#define FEC_OC_SNC_MODE_CORR_DISABLE__M 0x8 +#define FEC_OC_SNC_MODE_CORR_DISABLE__PRE 0x0 + +#define FEC_OC_SNC_LWM__A 0x2440041 +#define FEC_OC_SNC_LWM__W 4 +#define FEC_OC_SNC_LWM__M 0xF +#define FEC_OC_SNC_LWM__PRE 0x3 + +#define FEC_OC_SNC_LWM_MARK__B 0 +#define FEC_OC_SNC_LWM_MARK__W 4 +#define FEC_OC_SNC_LWM_MARK__M 0xF +#define FEC_OC_SNC_LWM_MARK__PRE 0x3 + +#define FEC_OC_SNC_HWM__A 0x2440042 +#define FEC_OC_SNC_HWM__W 4 +#define FEC_OC_SNC_HWM__M 0xF +#define FEC_OC_SNC_HWM__PRE 0x5 + +#define FEC_OC_SNC_HWM_MARK__B 0 +#define FEC_OC_SNC_HWM_MARK__W 4 +#define FEC_OC_SNC_HWM_MARK__M 0xF +#define FEC_OC_SNC_HWM_MARK__PRE 0x5 + +#define FEC_OC_SNC_UNLOCK__A 0x2440043 +#define FEC_OC_SNC_UNLOCK__W 1 +#define FEC_OC_SNC_UNLOCK__M 0x1 +#define FEC_OC_SNC_UNLOCK__PRE 0x0 + +#define FEC_OC_SNC_UNLOCK_RESTART__B 0 +#define FEC_OC_SNC_UNLOCK_RESTART__W 1 +#define FEC_OC_SNC_UNLOCK_RESTART__M 0x1 +#define FEC_OC_SNC_UNLOCK_RESTART__PRE 0x0 + +#define FEC_OC_SNC_LOCK_COUNT__A 0x2440044 +#define FEC_OC_SNC_LOCK_COUNT__W 12 +#define FEC_OC_SNC_LOCK_COUNT__M 0xFFF +#define FEC_OC_SNC_LOCK_COUNT__PRE 0x0 + +#define FEC_OC_SNC_LOCK_COUNT_COUNT__B 0 +#define FEC_OC_SNC_LOCK_COUNT_COUNT__W 12 +#define FEC_OC_SNC_LOCK_COUNT_COUNT__M 0xFFF +#define FEC_OC_SNC_LOCK_COUNT_COUNT__PRE 0x0 + +#define FEC_OC_SNC_FAIL_COUNT__A 0x2440045 +#define FEC_OC_SNC_FAIL_COUNT__W 12 +#define FEC_OC_SNC_FAIL_COUNT__M 0xFFF +#define FEC_OC_SNC_FAIL_COUNT__PRE 0x0 + +#define FEC_OC_SNC_FAIL_COUNT_COUNT__B 0 +#define FEC_OC_SNC_FAIL_COUNT_COUNT__W 12 +#define FEC_OC_SNC_FAIL_COUNT_COUNT__M 0xFFF +#define FEC_OC_SNC_FAIL_COUNT_COUNT__PRE 0x0 + +#define FEC_OC_SNC_FAIL_PERIOD__A 0x2440046 +#define FEC_OC_SNC_FAIL_PERIOD__W 16 +#define FEC_OC_SNC_FAIL_PERIOD__M 0xFFFF +#define FEC_OC_SNC_FAIL_PERIOD__PRE 0x1171 + +#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__B 0 +#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__W 16 +#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__M 0xFFFF +#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__PRE 0x1171 + +#define FEC_OC_EMS_MODE__A 0x2440047 +#define FEC_OC_EMS_MODE__W 2 +#define FEC_OC_EMS_MODE__M 0x3 +#define FEC_OC_EMS_MODE__PRE 0x0 + +#define FEC_OC_EMS_MODE_MODE__B 0 +#define FEC_OC_EMS_MODE_MODE__W 2 +#define FEC_OC_EMS_MODE_MODE__M 0x3 +#define FEC_OC_EMS_MODE_MODE__PRE 0x0 + +#define FEC_OC_IPR_MODE__A 0x2440048 +#define FEC_OC_IPR_MODE__W 12 +#define FEC_OC_IPR_MODE__M 0xFFF +#define FEC_OC_IPR_MODE__PRE 0x0 + +#define FEC_OC_IPR_MODE_SERIAL__B 0 +#define FEC_OC_IPR_MODE_SERIAL__W 1 +#define FEC_OC_IPR_MODE_SERIAL__M 0x1 +#define FEC_OC_IPR_MODE_SERIAL__PRE 0x0 + +#define FEC_OC_IPR_MODE_REVERSE_ORDER__B 1 +#define FEC_OC_IPR_MODE_REVERSE_ORDER__W 1 +#define FEC_OC_IPR_MODE_REVERSE_ORDER__M 0x2 +#define FEC_OC_IPR_MODE_REVERSE_ORDER__PRE 0x0 + +#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__B 2 +#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__W 1 +#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4 +#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__PRE 0x0 + +#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__B 3 +#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__W 1 +#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__M 0x8 +#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__B 4 +#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__W 1 +#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10 +#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MERR_DIS_PAR__B 5 +#define FEC_OC_IPR_MODE_MERR_DIS_PAR__W 1 +#define FEC_OC_IPR_MODE_MERR_DIS_PAR__M 0x20 +#define FEC_OC_IPR_MODE_MERR_DIS_PAR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MD_DIS_PAR__B 6 +#define FEC_OC_IPR_MODE_MD_DIS_PAR__W 1 +#define FEC_OC_IPR_MODE_MD_DIS_PAR__M 0x40 +#define FEC_OC_IPR_MODE_MD_DIS_PAR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__B 7 +#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__W 1 +#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__M 0x80 +#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__B 8 +#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__W 1 +#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__M 0x100 +#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MERR_DIS_ERR__B 9 +#define FEC_OC_IPR_MODE_MERR_DIS_ERR__W 1 +#define FEC_OC_IPR_MODE_MERR_DIS_ERR__M 0x200 +#define FEC_OC_IPR_MODE_MERR_DIS_ERR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MD_DIS_ERR__B 10 +#define FEC_OC_IPR_MODE_MD_DIS_ERR__W 1 +#define FEC_OC_IPR_MODE_MD_DIS_ERR__M 0x400 +#define FEC_OC_IPR_MODE_MD_DIS_ERR__PRE 0x0 + +#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__B 11 +#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__W 1 +#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__M 0x800 +#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__PRE 0x0 + +#define FEC_OC_IPR_INVERT__A 0x2440049 +#define FEC_OC_IPR_INVERT__W 12 +#define FEC_OC_IPR_INVERT__M 0xFFF +#define FEC_OC_IPR_INVERT__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD0__B 0 +#define FEC_OC_IPR_INVERT_MD0__W 1 +#define FEC_OC_IPR_INVERT_MD0__M 0x1 +#define FEC_OC_IPR_INVERT_MD0__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD1__B 1 +#define FEC_OC_IPR_INVERT_MD1__W 1 +#define FEC_OC_IPR_INVERT_MD1__M 0x2 +#define FEC_OC_IPR_INVERT_MD1__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD2__B 2 +#define FEC_OC_IPR_INVERT_MD2__W 1 +#define FEC_OC_IPR_INVERT_MD2__M 0x4 +#define FEC_OC_IPR_INVERT_MD2__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD3__B 3 +#define FEC_OC_IPR_INVERT_MD3__W 1 +#define FEC_OC_IPR_INVERT_MD3__M 0x8 +#define FEC_OC_IPR_INVERT_MD3__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD4__B 4 +#define FEC_OC_IPR_INVERT_MD4__W 1 +#define FEC_OC_IPR_INVERT_MD4__M 0x10 +#define FEC_OC_IPR_INVERT_MD4__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD5__B 5 +#define FEC_OC_IPR_INVERT_MD5__W 1 +#define FEC_OC_IPR_INVERT_MD5__M 0x20 +#define FEC_OC_IPR_INVERT_MD5__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD6__B 6 +#define FEC_OC_IPR_INVERT_MD6__W 1 +#define FEC_OC_IPR_INVERT_MD6__M 0x40 +#define FEC_OC_IPR_INVERT_MD6__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MD7__B 7 +#define FEC_OC_IPR_INVERT_MD7__W 1 +#define FEC_OC_IPR_INVERT_MD7__M 0x80 +#define FEC_OC_IPR_INVERT_MD7__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MERR__B 8 +#define FEC_OC_IPR_INVERT_MERR__W 1 +#define FEC_OC_IPR_INVERT_MERR__M 0x100 +#define FEC_OC_IPR_INVERT_MERR__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MSTRT__B 9 +#define FEC_OC_IPR_INVERT_MSTRT__W 1 +#define FEC_OC_IPR_INVERT_MSTRT__M 0x200 +#define FEC_OC_IPR_INVERT_MSTRT__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MVAL__B 10 +#define FEC_OC_IPR_INVERT_MVAL__W 1 +#define FEC_OC_IPR_INVERT_MVAL__M 0x400 +#define FEC_OC_IPR_INVERT_MVAL__PRE 0x0 + +#define FEC_OC_IPR_INVERT_MCLK__B 11 +#define FEC_OC_IPR_INVERT_MCLK__W 1 +#define FEC_OC_IPR_INVERT_MCLK__M 0x800 +#define FEC_OC_IPR_INVERT_MCLK__PRE 0x0 + +#define FEC_OC_OCR_MODE__A 0x2440050 +#define FEC_OC_OCR_MODE__W 4 +#define FEC_OC_OCR_MODE__M 0xF +#define FEC_OC_OCR_MODE__PRE 0x0 + +#define FEC_OC_OCR_MODE_MB_SELECT__B 0 +#define FEC_OC_OCR_MODE_MB_SELECT__W 1 +#define FEC_OC_OCR_MODE_MB_SELECT__M 0x1 +#define FEC_OC_OCR_MODE_MB_SELECT__PRE 0x0 + +#define FEC_OC_OCR_MODE_GRAB_ENABLE__B 1 +#define FEC_OC_OCR_MODE_GRAB_ENABLE__W 1 +#define FEC_OC_OCR_MODE_GRAB_ENABLE__M 0x2 +#define FEC_OC_OCR_MODE_GRAB_ENABLE__PRE 0x0 + +#define FEC_OC_OCR_MODE_GRAB_SELECT__B 2 +#define FEC_OC_OCR_MODE_GRAB_SELECT__W 1 +#define FEC_OC_OCR_MODE_GRAB_SELECT__M 0x4 +#define FEC_OC_OCR_MODE_GRAB_SELECT__PRE 0x0 + +#define FEC_OC_OCR_MODE_GRAB_COUNTED__B 3 +#define FEC_OC_OCR_MODE_GRAB_COUNTED__W 1 +#define FEC_OC_OCR_MODE_GRAB_COUNTED__M 0x8 +#define FEC_OC_OCR_MODE_GRAB_COUNTED__PRE 0x0 + +#define FEC_OC_OCR_RATE__A 0x2440051 +#define FEC_OC_OCR_RATE__W 4 +#define FEC_OC_OCR_RATE__M 0xF +#define FEC_OC_OCR_RATE__PRE 0x0 + +#define FEC_OC_OCR_RATE_RATE__B 0 +#define FEC_OC_OCR_RATE_RATE__W 4 +#define FEC_OC_OCR_RATE_RATE__M 0xF +#define FEC_OC_OCR_RATE_RATE__PRE 0x0 + +#define FEC_OC_OCR_INVERT__A 0x2440052 +#define FEC_OC_OCR_INVERT__W 12 +#define FEC_OC_OCR_INVERT__M 0xFFF +#define FEC_OC_OCR_INVERT__PRE 0x800 + +#define FEC_OC_OCR_INVERT_INVERT__B 0 +#define FEC_OC_OCR_INVERT_INVERT__W 12 +#define FEC_OC_OCR_INVERT_INVERT__M 0xFFF +#define FEC_OC_OCR_INVERT_INVERT__PRE 0x800 + +#define FEC_OC_OCR_GRAB_COUNT__A 0x2440053 +#define FEC_OC_OCR_GRAB_COUNT__W 16 +#define FEC_OC_OCR_GRAB_COUNT__M 0xFFFF +#define FEC_OC_OCR_GRAB_COUNT__PRE 0x0 + +#define FEC_OC_OCR_GRAB_COUNT_COUNT__B 0 +#define FEC_OC_OCR_GRAB_COUNT_COUNT__W 16 +#define FEC_OC_OCR_GRAB_COUNT_COUNT__M 0xFFFF +#define FEC_OC_OCR_GRAB_COUNT_COUNT__PRE 0x0 + +#define FEC_OC_OCR_GRAB_SYNC__A 0x2440054 +#define FEC_OC_OCR_GRAB_SYNC__W 8 +#define FEC_OC_OCR_GRAB_SYNC__M 0xFF +#define FEC_OC_OCR_GRAB_SYNC__PRE 0x0 + +#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__B 0 +#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__W 3 +#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__M 0x7 +#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__PRE 0x0 + +#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__B 3 +#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__W 4 +#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__M 0x78 +#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__PRE 0x0 + +#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__B 7 +#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__W 1 +#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__M 0x80 +#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD0__A 0x2440055 +#define FEC_OC_OCR_GRAB_RD0__W 10 +#define FEC_OC_OCR_GRAB_RD0__M 0x3FF +#define FEC_OC_OCR_GRAB_RD0__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD0_DATA__B 0 +#define FEC_OC_OCR_GRAB_RD0_DATA__W 10 +#define FEC_OC_OCR_GRAB_RD0_DATA__M 0x3FF +#define FEC_OC_OCR_GRAB_RD0_DATA__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD1__A 0x2440056 +#define FEC_OC_OCR_GRAB_RD1__W 10 +#define FEC_OC_OCR_GRAB_RD1__M 0x3FF +#define FEC_OC_OCR_GRAB_RD1__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD1_DATA__B 0 +#define FEC_OC_OCR_GRAB_RD1_DATA__W 10 +#define FEC_OC_OCR_GRAB_RD1_DATA__M 0x3FF +#define FEC_OC_OCR_GRAB_RD1_DATA__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD2__A 0x2440057 +#define FEC_OC_OCR_GRAB_RD2__W 10 +#define FEC_OC_OCR_GRAB_RD2__M 0x3FF +#define FEC_OC_OCR_GRAB_RD2__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD2_DATA__B 0 +#define FEC_OC_OCR_GRAB_RD2_DATA__W 10 +#define FEC_OC_OCR_GRAB_RD2_DATA__M 0x3FF +#define FEC_OC_OCR_GRAB_RD2_DATA__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD3__A 0x2440058 +#define FEC_OC_OCR_GRAB_RD3__W 10 +#define FEC_OC_OCR_GRAB_RD3__M 0x3FF +#define FEC_OC_OCR_GRAB_RD3__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD3_DATA__B 0 +#define FEC_OC_OCR_GRAB_RD3_DATA__W 10 +#define FEC_OC_OCR_GRAB_RD3_DATA__M 0x3FF +#define FEC_OC_OCR_GRAB_RD3_DATA__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD4__A 0x2440059 +#define FEC_OC_OCR_GRAB_RD4__W 10 +#define FEC_OC_OCR_GRAB_RD4__M 0x3FF +#define FEC_OC_OCR_GRAB_RD4__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD4_DATA__B 0 +#define FEC_OC_OCR_GRAB_RD4_DATA__W 10 +#define FEC_OC_OCR_GRAB_RD4_DATA__M 0x3FF +#define FEC_OC_OCR_GRAB_RD4_DATA__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD5__A 0x244005A +#define FEC_OC_OCR_GRAB_RD5__W 10 +#define FEC_OC_OCR_GRAB_RD5__M 0x3FF +#define FEC_OC_OCR_GRAB_RD5__PRE 0x0 + +#define FEC_OC_OCR_GRAB_RD5_DATA__B 0 +#define FEC_OC_OCR_GRAB_RD5_DATA__W 10 +#define FEC_OC_OCR_GRAB_RD5_DATA__M 0x3FF +#define FEC_OC_OCR_GRAB_RD5_DATA__PRE 0x0 + +#define FEC_DI_RAM__A 0x2450000 + +#define FEC_RS_RAM__A 0x2460000 + +#define FEC_OC_RAM__A 0x2470000 + +#define IQM_COMM_EXEC__A 0x1800000 +#define IQM_COMM_EXEC__W 2 +#define IQM_COMM_EXEC__M 0x3 +#define IQM_COMM_EXEC__PRE 0x0 +#define IQM_COMM_EXEC_STOP 0x0 +#define IQM_COMM_EXEC_ACTIVE 0x1 +#define IQM_COMM_EXEC_HOLD 0x2 + +#define IQM_COMM_MB__A 0x1800002 +#define IQM_COMM_MB__W 16 +#define IQM_COMM_MB__M 0xFFFF +#define IQM_COMM_MB__PRE 0x0 +#define IQM_COMM_INT_REQ__A 0x1800003 +#define IQM_COMM_INT_REQ__W 2 +#define IQM_COMM_INT_REQ__M 0x3 +#define IQM_COMM_INT_REQ__PRE 0x0 + +#define IQM_COMM_INT_REQ_AF_REQ__B 0 +#define IQM_COMM_INT_REQ_AF_REQ__W 1 +#define IQM_COMM_INT_REQ_AF_REQ__M 0x1 +#define IQM_COMM_INT_REQ_AF_REQ__PRE 0x0 + +#define IQM_COMM_INT_REQ_CF_REQ__B 1 +#define IQM_COMM_INT_REQ_CF_REQ__W 1 +#define IQM_COMM_INT_REQ_CF_REQ__M 0x2 +#define IQM_COMM_INT_REQ_CF_REQ__PRE 0x0 + +#define IQM_COMM_INT_STA__A 0x1800005 +#define IQM_COMM_INT_STA__W 16 +#define IQM_COMM_INT_STA__M 0xFFFF +#define IQM_COMM_INT_STA__PRE 0x0 +#define IQM_COMM_INT_MSK__A 0x1800006 +#define IQM_COMM_INT_MSK__W 16 +#define IQM_COMM_INT_MSK__M 0xFFFF +#define IQM_COMM_INT_MSK__PRE 0x0 +#define IQM_COMM_INT_STM__A 0x1800007 +#define IQM_COMM_INT_STM__W 16 +#define IQM_COMM_INT_STM__M 0xFFFF +#define IQM_COMM_INT_STM__PRE 0x0 + +#define IQM_FS_COMM_EXEC__A 0x1820000 +#define IQM_FS_COMM_EXEC__W 2 +#define IQM_FS_COMM_EXEC__M 0x3 +#define IQM_FS_COMM_EXEC__PRE 0x0 +#define IQM_FS_COMM_EXEC_STOP 0x0 +#define IQM_FS_COMM_EXEC_ACTIVE 0x1 +#define IQM_FS_COMM_EXEC_HOLD 0x2 + +#define IQM_FS_COMM_MB__A 0x1820002 +#define IQM_FS_COMM_MB__W 2 +#define IQM_FS_COMM_MB__M 0x3 +#define IQM_FS_COMM_MB__PRE 0x0 +#define IQM_FS_COMM_MB_CTL__B 0 +#define IQM_FS_COMM_MB_CTL__W 1 +#define IQM_FS_COMM_MB_CTL__M 0x1 +#define IQM_FS_COMM_MB_CTL__PRE 0x0 +#define IQM_FS_COMM_MB_CTL_CTL_OFF 0x0 +#define IQM_FS_COMM_MB_CTL_CTL_ON 0x1 +#define IQM_FS_COMM_MB_OBS__B 1 +#define IQM_FS_COMM_MB_OBS__W 1 +#define IQM_FS_COMM_MB_OBS__M 0x2 +#define IQM_FS_COMM_MB_OBS__PRE 0x0 +#define IQM_FS_COMM_MB_OBS_OBS_OFF 0x0 +#define IQM_FS_COMM_MB_OBS_OBS_ON 0x2 + +#define IQM_FS_RATE_OFS_LO__A 0x1820010 +#define IQM_FS_RATE_OFS_LO__W 16 +#define IQM_FS_RATE_OFS_LO__M 0xFFFF +#define IQM_FS_RATE_OFS_LO__PRE 0x0 +#define IQM_FS_RATE_OFS_HI__A 0x1820011 +#define IQM_FS_RATE_OFS_HI__W 12 +#define IQM_FS_RATE_OFS_HI__M 0xFFF +#define IQM_FS_RATE_OFS_HI__PRE 0x0 +#define IQM_FS_RATE_LO__A 0x1820012 +#define IQM_FS_RATE_LO__W 16 +#define IQM_FS_RATE_LO__M 0xFFFF +#define IQM_FS_RATE_LO__PRE 0x0 +#define IQM_FS_RATE_HI__A 0x1820013 +#define IQM_FS_RATE_HI__W 12 +#define IQM_FS_RATE_HI__M 0xFFF +#define IQM_FS_RATE_HI__PRE 0x0 + +#define IQM_FS_ADJ_SEL__A 0x1820014 +#define IQM_FS_ADJ_SEL__W 2 +#define IQM_FS_ADJ_SEL__M 0x3 +#define IQM_FS_ADJ_SEL__PRE 0x0 +#define IQM_FS_ADJ_SEL_OFF 0x0 +#define IQM_FS_ADJ_SEL_QAM 0x1 +#define IQM_FS_ADJ_SEL_VSB 0x2 + +#define IQM_FD_COMM_EXEC__A 0x1830000 +#define IQM_FD_COMM_EXEC__W 2 +#define IQM_FD_COMM_EXEC__M 0x3 +#define IQM_FD_COMM_EXEC__PRE 0x0 +#define IQM_FD_COMM_EXEC_STOP 0x0 +#define IQM_FD_COMM_EXEC_ACTIVE 0x1 +#define IQM_FD_COMM_EXEC_HOLD 0x2 + +#define IQM_FD_COMM_MB__A 0x1830002 +#define IQM_FD_COMM_MB__W 2 +#define IQM_FD_COMM_MB__M 0x3 +#define IQM_FD_COMM_MB__PRE 0x0 +#define IQM_FD_COMM_MB_CTL__B 0 +#define IQM_FD_COMM_MB_CTL__W 1 +#define IQM_FD_COMM_MB_CTL__M 0x1 +#define IQM_FD_COMM_MB_CTL__PRE 0x0 +#define IQM_FD_COMM_MB_CTL_CTL_OFF 0x0 +#define IQM_FD_COMM_MB_CTL_CTL_ON 0x1 +#define IQM_FD_COMM_MB_OBS__B 1 +#define IQM_FD_COMM_MB_OBS__W 1 +#define IQM_FD_COMM_MB_OBS__M 0x2 +#define IQM_FD_COMM_MB_OBS__PRE 0x0 +#define IQM_FD_COMM_MB_OBS_OBS_OFF 0x0 +#define IQM_FD_COMM_MB_OBS_OBS_ON 0x2 + +#define IQM_RC_COMM_EXEC__A 0x1840000 +#define IQM_RC_COMM_EXEC__W 2 +#define IQM_RC_COMM_EXEC__M 0x3 +#define IQM_RC_COMM_EXEC__PRE 0x0 +#define IQM_RC_COMM_EXEC_STOP 0x0 +#define IQM_RC_COMM_EXEC_ACTIVE 0x1 +#define IQM_RC_COMM_EXEC_HOLD 0x2 + +#define IQM_RC_COMM_MB__A 0x1840002 +#define IQM_RC_COMM_MB__W 2 +#define IQM_RC_COMM_MB__M 0x3 +#define IQM_RC_COMM_MB__PRE 0x0 +#define IQM_RC_COMM_MB_CTL__B 0 +#define IQM_RC_COMM_MB_CTL__W 1 +#define IQM_RC_COMM_MB_CTL__M 0x1 +#define IQM_RC_COMM_MB_CTL__PRE 0x0 +#define IQM_RC_COMM_MB_CTL_CTL_OFF 0x0 +#define IQM_RC_COMM_MB_CTL_CTL_ON 0x1 +#define IQM_RC_COMM_MB_OBS__B 1 +#define IQM_RC_COMM_MB_OBS__W 1 +#define IQM_RC_COMM_MB_OBS__M 0x2 +#define IQM_RC_COMM_MB_OBS__PRE 0x0 +#define IQM_RC_COMM_MB_OBS_OBS_OFF 0x0 +#define IQM_RC_COMM_MB_OBS_OBS_ON 0x2 + +#define IQM_RC_RATE_OFS_LO__A 0x1840010 +#define IQM_RC_RATE_OFS_LO__W 16 +#define IQM_RC_RATE_OFS_LO__M 0xFFFF +#define IQM_RC_RATE_OFS_LO__PRE 0x0 +#define IQM_RC_RATE_OFS_HI__A 0x1840011 +#define IQM_RC_RATE_OFS_HI__W 8 +#define IQM_RC_RATE_OFS_HI__M 0xFF +#define IQM_RC_RATE_OFS_HI__PRE 0x0 +#define IQM_RC_RATE_LO__A 0x1840012 +#define IQM_RC_RATE_LO__W 16 +#define IQM_RC_RATE_LO__M 0xFFFF +#define IQM_RC_RATE_LO__PRE 0x0 +#define IQM_RC_RATE_HI__A 0x1840013 +#define IQM_RC_RATE_HI__W 8 +#define IQM_RC_RATE_HI__M 0xFF +#define IQM_RC_RATE_HI__PRE 0x0 + +#define IQM_RC_ADJ_SEL__A 0x1840014 +#define IQM_RC_ADJ_SEL__W 2 +#define IQM_RC_ADJ_SEL__M 0x3 +#define IQM_RC_ADJ_SEL__PRE 0x0 +#define IQM_RC_ADJ_SEL_OFF 0x0 +#define IQM_RC_ADJ_SEL_QAM 0x1 +#define IQM_RC_ADJ_SEL_VSB 0x2 + +#define IQM_RC_CROUT_ENA__A 0x1840015 +#define IQM_RC_CROUT_ENA__W 1 +#define IQM_RC_CROUT_ENA__M 0x1 +#define IQM_RC_CROUT_ENA__PRE 0x0 + +#define IQM_RC_CROUT_ENA_ENA__B 0 +#define IQM_RC_CROUT_ENA_ENA__W 1 +#define IQM_RC_CROUT_ENA_ENA__M 0x1 +#define IQM_RC_CROUT_ENA_ENA__PRE 0x0 + +#define IQM_RC_STRETCH__A 0x1840016 +#define IQM_RC_STRETCH__W 5 +#define IQM_RC_STRETCH__M 0x1F +#define IQM_RC_STRETCH__PRE 0x0 +#define IQM_RC_STRETCH_QAM_B_64 0x1E +#define IQM_RC_STRETCH_QAM_B_256 0x1C +#define IQM_RC_STRETCH_ATV 0xF + +#define IQM_RT_COMM_EXEC__A 0x1850000 +#define IQM_RT_COMM_EXEC__W 2 +#define IQM_RT_COMM_EXEC__M 0x3 +#define IQM_RT_COMM_EXEC__PRE 0x0 +#define IQM_RT_COMM_EXEC_STOP 0x0 +#define IQM_RT_COMM_EXEC_ACTIVE 0x1 +#define IQM_RT_COMM_EXEC_HOLD 0x2 + +#define IQM_RT_COMM_MB__A 0x1850002 +#define IQM_RT_COMM_MB__W 2 +#define IQM_RT_COMM_MB__M 0x3 +#define IQM_RT_COMM_MB__PRE 0x0 +#define IQM_RT_COMM_MB_CTL__B 0 +#define IQM_RT_COMM_MB_CTL__W 1 +#define IQM_RT_COMM_MB_CTL__M 0x1 +#define IQM_RT_COMM_MB_CTL__PRE 0x0 +#define IQM_RT_COMM_MB_CTL_CTL_OFF 0x0 +#define IQM_RT_COMM_MB_CTL_CTL_ON 0x1 +#define IQM_RT_COMM_MB_OBS__B 1 +#define IQM_RT_COMM_MB_OBS__W 1 +#define IQM_RT_COMM_MB_OBS__M 0x2 +#define IQM_RT_COMM_MB_OBS__PRE 0x0 +#define IQM_RT_COMM_MB_OBS_OBS_OFF 0x0 +#define IQM_RT_COMM_MB_OBS_OBS_ON 0x2 + +#define IQM_RT_ACTIVE__A 0x1850010 +#define IQM_RT_ACTIVE__W 2 +#define IQM_RT_ACTIVE__M 0x3 +#define IQM_RT_ACTIVE__PRE 0x0 + +#define IQM_RT_ACTIVE_ACTIVE_RT__B 0 +#define IQM_RT_ACTIVE_ACTIVE_RT__W 1 +#define IQM_RT_ACTIVE_ACTIVE_RT__M 0x1 +#define IQM_RT_ACTIVE_ACTIVE_RT__PRE 0x0 +#define IQM_RT_ACTIVE_ACTIVE_RT_ATV_FCR_OFF 0x0 +#define IQM_RT_ACTIVE_ACTIVE_RT_ATV_FCR_ON 0x1 + +#define IQM_RT_ACTIVE_ACTIVE_CR__B 1 +#define IQM_RT_ACTIVE_ACTIVE_CR__W 1 +#define IQM_RT_ACTIVE_ACTIVE_CR__M 0x2 +#define IQM_RT_ACTIVE_ACTIVE_CR__PRE 0x0 +#define IQM_RT_ACTIVE_ACTIVE_CR_ATV_CR_OFF 0x0 +#define IQM_RT_ACTIVE_ACTIVE_CR_ATV_CR_ON 0x2 + +#define IQM_RT_LO_INCR__A 0x1850011 +#define IQM_RT_LO_INCR__W 12 +#define IQM_RT_LO_INCR__M 0xFFF +#define IQM_RT_LO_INCR__PRE 0x588 +#define IQM_RT_LO_INCR_FM 0x0 +#define IQM_RT_LO_INCR_MN 0x588 + +#define IQM_RT_ROT_BP__A 0x1850012 +#define IQM_RT_ROT_BP__W 2 +#define IQM_RT_ROT_BP__M 0x3 +#define IQM_RT_ROT_BP__PRE 0x0 + +#define IQM_RT_ROT_BP_ROT_OFF__B 0 +#define IQM_RT_ROT_BP_ROT_OFF__W 1 +#define IQM_RT_ROT_BP_ROT_OFF__M 0x1 +#define IQM_RT_ROT_BP_ROT_OFF__PRE 0x0 +#define IQM_RT_ROT_BP_ROT_OFF_ACTIVE 0x0 +#define IQM_RT_ROT_BP_ROT_OFF_OFF 0x1 + +#define IQM_RT_ROT_BP_ROT_BPF__B 1 +#define IQM_RT_ROT_BP_ROT_BPF__W 1 +#define IQM_RT_ROT_BP_ROT_BPF__M 0x2 +#define IQM_RT_ROT_BP_ROT_BPF__PRE 0x0 + +#define IQM_RT_LP_BP__A 0x1850013 +#define IQM_RT_LP_BP__W 1 +#define IQM_RT_LP_BP__M 0x1 +#define IQM_RT_LP_BP__PRE 0x0 + +#define IQM_RT_DELAY__A 0x1850014 +#define IQM_RT_DELAY__W 7 +#define IQM_RT_DELAY__M 0x7F +#define IQM_RT_DELAY__PRE 0x45 + +#define IQM_CF_COMM_EXEC__A 0x1860000 +#define IQM_CF_COMM_EXEC__W 2 +#define IQM_CF_COMM_EXEC__M 0x3 +#define IQM_CF_COMM_EXEC__PRE 0x0 +#define IQM_CF_COMM_EXEC_STOP 0x0 +#define IQM_CF_COMM_EXEC_ACTIVE 0x1 +#define IQM_CF_COMM_EXEC_HOLD 0x2 + +#define IQM_CF_COMM_MB__A 0x1860002 +#define IQM_CF_COMM_MB__W 2 +#define IQM_CF_COMM_MB__M 0x3 +#define IQM_CF_COMM_MB__PRE 0x0 +#define IQM_CF_COMM_MB_CTL__B 0 +#define IQM_CF_COMM_MB_CTL__W 1 +#define IQM_CF_COMM_MB_CTL__M 0x1 +#define IQM_CF_COMM_MB_CTL__PRE 0x0 +#define IQM_CF_COMM_MB_CTL_CTL_OFF 0x0 +#define IQM_CF_COMM_MB_CTL_CTL_ON 0x1 +#define IQM_CF_COMM_MB_OBS__B 1 +#define IQM_CF_COMM_MB_OBS__W 1 +#define IQM_CF_COMM_MB_OBS__M 0x2 +#define IQM_CF_COMM_MB_OBS__PRE 0x0 +#define IQM_CF_COMM_MB_OBS_OBS_OFF 0x0 +#define IQM_CF_COMM_MB_OBS_OBS_ON 0x2 + +#define IQM_CF_COMM_INT_REQ__A 0x1860003 +#define IQM_CF_COMM_INT_REQ__W 1 +#define IQM_CF_COMM_INT_REQ__M 0x1 +#define IQM_CF_COMM_INT_REQ__PRE 0x0 +#define IQM_CF_COMM_INT_STA__A 0x1860005 +#define IQM_CF_COMM_INT_STA__W 1 +#define IQM_CF_COMM_INT_STA__M 0x1 +#define IQM_CF_COMM_INT_STA__PRE 0x0 +#define IQM_CF_COMM_INT_STA_PM__B 0 +#define IQM_CF_COMM_INT_STA_PM__W 1 +#define IQM_CF_COMM_INT_STA_PM__M 0x1 +#define IQM_CF_COMM_INT_STA_PM__PRE 0x0 + +#define IQM_CF_COMM_INT_MSK__A 0x1860006 +#define IQM_CF_COMM_INT_MSK__W 1 +#define IQM_CF_COMM_INT_MSK__M 0x1 +#define IQM_CF_COMM_INT_MSK__PRE 0x0 +#define IQM_CF_COMM_INT_MSK_PM__B 0 +#define IQM_CF_COMM_INT_MSK_PM__W 1 +#define IQM_CF_COMM_INT_MSK_PM__M 0x1 +#define IQM_CF_COMM_INT_MSK_PM__PRE 0x0 + +#define IQM_CF_COMM_INT_STM__A 0x1860007 +#define IQM_CF_COMM_INT_STM__W 1 +#define IQM_CF_COMM_INT_STM__M 0x1 +#define IQM_CF_COMM_INT_STM__PRE 0x0 +#define IQM_CF_COMM_INT_STM_PM__B 0 +#define IQM_CF_COMM_INT_STM_PM__W 1 +#define IQM_CF_COMM_INT_STM_PM__M 0x1 +#define IQM_CF_COMM_INT_STM_PM__PRE 0x0 + +#define IQM_CF_SYMMETRIC__A 0x1860010 +#define IQM_CF_SYMMETRIC__W 2 +#define IQM_CF_SYMMETRIC__M 0x3 +#define IQM_CF_SYMMETRIC__PRE 0x0 + +#define IQM_CF_SYMMETRIC_RE__B 0 +#define IQM_CF_SYMMETRIC_RE__W 1 +#define IQM_CF_SYMMETRIC_RE__M 0x1 +#define IQM_CF_SYMMETRIC_RE__PRE 0x0 + +#define IQM_CF_SYMMETRIC_IM__B 1 +#define IQM_CF_SYMMETRIC_IM__W 1 +#define IQM_CF_SYMMETRIC_IM__M 0x2 +#define IQM_CF_SYMMETRIC_IM__PRE 0x0 + +#define IQM_CF_MIDTAP__A 0x1860011 +#define IQM_CF_MIDTAP__W 2 +#define IQM_CF_MIDTAP__M 0x3 +#define IQM_CF_MIDTAP__PRE 0x3 + +#define IQM_CF_MIDTAP_RE__B 0 +#define IQM_CF_MIDTAP_RE__W 1 +#define IQM_CF_MIDTAP_RE__M 0x1 +#define IQM_CF_MIDTAP_RE__PRE 0x1 + +#define IQM_CF_MIDTAP_IM__B 1 +#define IQM_CF_MIDTAP_IM__W 1 +#define IQM_CF_MIDTAP_IM__M 0x2 +#define IQM_CF_MIDTAP_IM__PRE 0x2 + +#define IQM_CF_OUT_ENA__A 0x1860012 +#define IQM_CF_OUT_ENA__W 3 +#define IQM_CF_OUT_ENA__M 0x7 +#define IQM_CF_OUT_ENA__PRE 0x0 + +#define IQM_CF_OUT_ENA_ATV__B 0 +#define IQM_CF_OUT_ENA_ATV__W 1 +#define IQM_CF_OUT_ENA_ATV__M 0x1 +#define IQM_CF_OUT_ENA_ATV__PRE 0x0 + +#define IQM_CF_OUT_ENA_QAM__B 1 +#define IQM_CF_OUT_ENA_QAM__W 1 +#define IQM_CF_OUT_ENA_QAM__M 0x2 +#define IQM_CF_OUT_ENA_QAM__PRE 0x0 + +#define IQM_CF_OUT_ENA_VSB__B 2 +#define IQM_CF_OUT_ENA_VSB__W 1 +#define IQM_CF_OUT_ENA_VSB__M 0x4 +#define IQM_CF_OUT_ENA_VSB__PRE 0x0 + +#define IQM_CF_ADJ_SEL__A 0x1860013 +#define IQM_CF_ADJ_SEL__W 2 +#define IQM_CF_ADJ_SEL__M 0x3 +#define IQM_CF_ADJ_SEL__PRE 0x0 +#define IQM_CF_SCALE__A 0x1860014 +#define IQM_CF_SCALE__W 14 +#define IQM_CF_SCALE__M 0x3FFF +#define IQM_CF_SCALE__PRE 0x400 + +#define IQM_CF_SCALE_SH__A 0x1860015 +#define IQM_CF_SCALE_SH__W 2 +#define IQM_CF_SCALE_SH__M 0x3 +#define IQM_CF_SCALE_SH__PRE 0x0 + +#define IQM_CF_AMP__A 0x1860016 +#define IQM_CF_AMP__W 14 +#define IQM_CF_AMP__M 0x3FFF +#define IQM_CF_AMP__PRE 0x0 + +#define IQM_CF_POW_MEAS_LEN__A 0x1860017 +#define IQM_CF_POW_MEAS_LEN__W 3 +#define IQM_CF_POW_MEAS_LEN__M 0x7 +#define IQM_CF_POW_MEAS_LEN__PRE 0x2 +#define IQM_CF_POW_MEAS_LEN_QAM_B_64 0x1 +#define IQM_CF_POW_MEAS_LEN_QAM_B_256 0x1 + +#define IQM_CF_POW__A 0x1860018 +#define IQM_CF_POW__W 16 +#define IQM_CF_POW__M 0xFFFF +#define IQM_CF_POW__PRE 0x2 +#define IQM_CF_TAP_RE0__A 0x1860020 +#define IQM_CF_TAP_RE0__W 7 +#define IQM_CF_TAP_RE0__M 0x7F +#define IQM_CF_TAP_RE0__PRE 0x2 +#define IQM_CF_TAP_RE1__A 0x1860021 +#define IQM_CF_TAP_RE1__W 7 +#define IQM_CF_TAP_RE1__M 0x7F +#define IQM_CF_TAP_RE1__PRE 0x2 +#define IQM_CF_TAP_RE2__A 0x1860022 +#define IQM_CF_TAP_RE2__W 7 +#define IQM_CF_TAP_RE2__M 0x7F +#define IQM_CF_TAP_RE2__PRE 0x2 +#define IQM_CF_TAP_RE3__A 0x1860023 +#define IQM_CF_TAP_RE3__W 7 +#define IQM_CF_TAP_RE3__M 0x7F +#define IQM_CF_TAP_RE3__PRE 0x2 +#define IQM_CF_TAP_RE4__A 0x1860024 +#define IQM_CF_TAP_RE4__W 7 +#define IQM_CF_TAP_RE4__M 0x7F +#define IQM_CF_TAP_RE4__PRE 0x2 +#define IQM_CF_TAP_RE5__A 0x1860025 +#define IQM_CF_TAP_RE5__W 7 +#define IQM_CF_TAP_RE5__M 0x7F +#define IQM_CF_TAP_RE5__PRE 0x2 +#define IQM_CF_TAP_RE6__A 0x1860026 +#define IQM_CF_TAP_RE6__W 7 +#define IQM_CF_TAP_RE6__M 0x7F +#define IQM_CF_TAP_RE6__PRE 0x2 +#define IQM_CF_TAP_RE7__A 0x1860027 +#define IQM_CF_TAP_RE7__W 9 +#define IQM_CF_TAP_RE7__M 0x1FF +#define IQM_CF_TAP_RE7__PRE 0x2 +#define IQM_CF_TAP_RE8__A 0x1860028 +#define IQM_CF_TAP_RE8__W 9 +#define IQM_CF_TAP_RE8__M 0x1FF +#define IQM_CF_TAP_RE8__PRE 0x2 +#define IQM_CF_TAP_RE9__A 0x1860029 +#define IQM_CF_TAP_RE9__W 9 +#define IQM_CF_TAP_RE9__M 0x1FF +#define IQM_CF_TAP_RE9__PRE 0x2 +#define IQM_CF_TAP_RE10__A 0x186002A +#define IQM_CF_TAP_RE10__W 9 +#define IQM_CF_TAP_RE10__M 0x1FF +#define IQM_CF_TAP_RE10__PRE 0x2 +#define IQM_CF_TAP_RE11__A 0x186002B +#define IQM_CF_TAP_RE11__W 9 +#define IQM_CF_TAP_RE11__M 0x1FF +#define IQM_CF_TAP_RE11__PRE 0x2 +#define IQM_CF_TAP_RE12__A 0x186002C +#define IQM_CF_TAP_RE12__W 9 +#define IQM_CF_TAP_RE12__M 0x1FF +#define IQM_CF_TAP_RE12__PRE 0x2 +#define IQM_CF_TAP_RE13__A 0x186002D +#define IQM_CF_TAP_RE13__W 9 +#define IQM_CF_TAP_RE13__M 0x1FF +#define IQM_CF_TAP_RE13__PRE 0x2 +#define IQM_CF_TAP_RE14__A 0x186002E +#define IQM_CF_TAP_RE14__W 9 +#define IQM_CF_TAP_RE14__M 0x1FF +#define IQM_CF_TAP_RE14__PRE 0x2 +#define IQM_CF_TAP_RE15__A 0x186002F +#define IQM_CF_TAP_RE15__W 9 +#define IQM_CF_TAP_RE15__M 0x1FF +#define IQM_CF_TAP_RE15__PRE 0x2 +#define IQM_CF_TAP_RE16__A 0x1860030 +#define IQM_CF_TAP_RE16__W 9 +#define IQM_CF_TAP_RE16__M 0x1FF +#define IQM_CF_TAP_RE16__PRE 0x2 +#define IQM_CF_TAP_RE17__A 0x1860031 +#define IQM_CF_TAP_RE17__W 9 +#define IQM_CF_TAP_RE17__M 0x1FF +#define IQM_CF_TAP_RE17__PRE 0x2 +#define IQM_CF_TAP_RE18__A 0x1860032 +#define IQM_CF_TAP_RE18__W 9 +#define IQM_CF_TAP_RE18__M 0x1FF +#define IQM_CF_TAP_RE18__PRE 0x2 +#define IQM_CF_TAP_RE19__A 0x1860033 +#define IQM_CF_TAP_RE19__W 9 +#define IQM_CF_TAP_RE19__M 0x1FF +#define IQM_CF_TAP_RE19__PRE 0x2 +#define IQM_CF_TAP_RE20__A 0x1860034 +#define IQM_CF_TAP_RE20__W 9 +#define IQM_CF_TAP_RE20__M 0x1FF +#define IQM_CF_TAP_RE20__PRE 0x2 +#define IQM_CF_TAP_RE21__A 0x1860035 +#define IQM_CF_TAP_RE21__W 11 +#define IQM_CF_TAP_RE21__M 0x7FF +#define IQM_CF_TAP_RE21__PRE 0x2 +#define IQM_CF_TAP_RE22__A 0x1860036 +#define IQM_CF_TAP_RE22__W 11 +#define IQM_CF_TAP_RE22__M 0x7FF +#define IQM_CF_TAP_RE22__PRE 0x2 +#define IQM_CF_TAP_RE23__A 0x1860037 +#define IQM_CF_TAP_RE23__W 11 +#define IQM_CF_TAP_RE23__M 0x7FF +#define IQM_CF_TAP_RE23__PRE 0x2 +#define IQM_CF_TAP_RE24__A 0x1860038 +#define IQM_CF_TAP_RE24__W 11 +#define IQM_CF_TAP_RE24__M 0x7FF +#define IQM_CF_TAP_RE24__PRE 0x2 +#define IQM_CF_TAP_RE25__A 0x1860039 +#define IQM_CF_TAP_RE25__W 11 +#define IQM_CF_TAP_RE25__M 0x7FF +#define IQM_CF_TAP_RE25__PRE 0x2 +#define IQM_CF_TAP_RE26__A 0x186003A +#define IQM_CF_TAP_RE26__W 11 +#define IQM_CF_TAP_RE26__M 0x7FF +#define IQM_CF_TAP_RE26__PRE 0x2 +#define IQM_CF_TAP_RE27__A 0x186003B +#define IQM_CF_TAP_RE27__W 11 +#define IQM_CF_TAP_RE27__M 0x7FF +#define IQM_CF_TAP_RE27__PRE 0x2 +#define IQM_CF_TAP_IM0__A 0x1860040 +#define IQM_CF_TAP_IM0__W 7 +#define IQM_CF_TAP_IM0__M 0x7F +#define IQM_CF_TAP_IM0__PRE 0x2 +#define IQM_CF_TAP_IM1__A 0x1860041 +#define IQM_CF_TAP_IM1__W 7 +#define IQM_CF_TAP_IM1__M 0x7F +#define IQM_CF_TAP_IM1__PRE 0x2 +#define IQM_CF_TAP_IM2__A 0x1860042 +#define IQM_CF_TAP_IM2__W 7 +#define IQM_CF_TAP_IM2__M 0x7F +#define IQM_CF_TAP_IM2__PRE 0x2 +#define IQM_CF_TAP_IM3__A 0x1860043 +#define IQM_CF_TAP_IM3__W 7 +#define IQM_CF_TAP_IM3__M 0x7F +#define IQM_CF_TAP_IM3__PRE 0x2 +#define IQM_CF_TAP_IM4__A 0x1860044 +#define IQM_CF_TAP_IM4__W 7 +#define IQM_CF_TAP_IM4__M 0x7F +#define IQM_CF_TAP_IM4__PRE 0x2 +#define IQM_CF_TAP_IM5__A 0x1860045 +#define IQM_CF_TAP_IM5__W 7 +#define IQM_CF_TAP_IM5__M 0x7F +#define IQM_CF_TAP_IM5__PRE 0x2 +#define IQM_CF_TAP_IM6__A 0x1860046 +#define IQM_CF_TAP_IM6__W 7 +#define IQM_CF_TAP_IM6__M 0x7F +#define IQM_CF_TAP_IM6__PRE 0x2 +#define IQM_CF_TAP_IM7__A 0x1860047 +#define IQM_CF_TAP_IM7__W 9 +#define IQM_CF_TAP_IM7__M 0x1FF +#define IQM_CF_TAP_IM7__PRE 0x2 +#define IQM_CF_TAP_IM8__A 0x1860048 +#define IQM_CF_TAP_IM8__W 9 +#define IQM_CF_TAP_IM8__M 0x1FF +#define IQM_CF_TAP_IM8__PRE 0x2 +#define IQM_CF_TAP_IM9__A 0x1860049 +#define IQM_CF_TAP_IM9__W 9 +#define IQM_CF_TAP_IM9__M 0x1FF +#define IQM_CF_TAP_IM9__PRE 0x2 +#define IQM_CF_TAP_IM10__A 0x186004A +#define IQM_CF_TAP_IM10__W 9 +#define IQM_CF_TAP_IM10__M 0x1FF +#define IQM_CF_TAP_IM10__PRE 0x2 +#define IQM_CF_TAP_IM11__A 0x186004B +#define IQM_CF_TAP_IM11__W 9 +#define IQM_CF_TAP_IM11__M 0x1FF +#define IQM_CF_TAP_IM11__PRE 0x2 +#define IQM_CF_TAP_IM12__A 0x186004C +#define IQM_CF_TAP_IM12__W 9 +#define IQM_CF_TAP_IM12__M 0x1FF +#define IQM_CF_TAP_IM12__PRE 0x2 +#define IQM_CF_TAP_IM13__A 0x186004D +#define IQM_CF_TAP_IM13__W 9 +#define IQM_CF_TAP_IM13__M 0x1FF +#define IQM_CF_TAP_IM13__PRE 0x2 +#define IQM_CF_TAP_IM14__A 0x186004E +#define IQM_CF_TAP_IM14__W 9 +#define IQM_CF_TAP_IM14__M 0x1FF +#define IQM_CF_TAP_IM14__PRE 0x2 +#define IQM_CF_TAP_IM15__A 0x186004F +#define IQM_CF_TAP_IM15__W 9 +#define IQM_CF_TAP_IM15__M 0x1FF +#define IQM_CF_TAP_IM15__PRE 0x2 +#define IQM_CF_TAP_IM16__A 0x1860050 +#define IQM_CF_TAP_IM16__W 9 +#define IQM_CF_TAP_IM16__M 0x1FF +#define IQM_CF_TAP_IM16__PRE 0x2 +#define IQM_CF_TAP_IM17__A 0x1860051 +#define IQM_CF_TAP_IM17__W 9 +#define IQM_CF_TAP_IM17__M 0x1FF +#define IQM_CF_TAP_IM17__PRE 0x2 +#define IQM_CF_TAP_IM18__A 0x1860052 +#define IQM_CF_TAP_IM18__W 9 +#define IQM_CF_TAP_IM18__M 0x1FF +#define IQM_CF_TAP_IM18__PRE 0x2 +#define IQM_CF_TAP_IM19__A 0x1860053 +#define IQM_CF_TAP_IM19__W 9 +#define IQM_CF_TAP_IM19__M 0x1FF +#define IQM_CF_TAP_IM19__PRE 0x2 +#define IQM_CF_TAP_IM20__A 0x1860054 +#define IQM_CF_TAP_IM20__W 9 +#define IQM_CF_TAP_IM20__M 0x1FF +#define IQM_CF_TAP_IM20__PRE 0x2 +#define IQM_CF_TAP_IM21__A 0x1860055 +#define IQM_CF_TAP_IM21__W 11 +#define IQM_CF_TAP_IM21__M 0x7FF +#define IQM_CF_TAP_IM21__PRE 0x2 +#define IQM_CF_TAP_IM22__A 0x1860056 +#define IQM_CF_TAP_IM22__W 11 +#define IQM_CF_TAP_IM22__M 0x7FF +#define IQM_CF_TAP_IM22__PRE 0x2 +#define IQM_CF_TAP_IM23__A 0x1860057 +#define IQM_CF_TAP_IM23__W 11 +#define IQM_CF_TAP_IM23__M 0x7FF +#define IQM_CF_TAP_IM23__PRE 0x2 +#define IQM_CF_TAP_IM24__A 0x1860058 +#define IQM_CF_TAP_IM24__W 11 +#define IQM_CF_TAP_IM24__M 0x7FF +#define IQM_CF_TAP_IM24__PRE 0x2 +#define IQM_CF_TAP_IM25__A 0x1860059 +#define IQM_CF_TAP_IM25__W 11 +#define IQM_CF_TAP_IM25__M 0x7FF +#define IQM_CF_TAP_IM25__PRE 0x2 +#define IQM_CF_TAP_IM26__A 0x186005A +#define IQM_CF_TAP_IM26__W 11 +#define IQM_CF_TAP_IM26__M 0x7FF +#define IQM_CF_TAP_IM26__PRE 0x2 +#define IQM_CF_TAP_IM27__A 0x186005B +#define IQM_CF_TAP_IM27__W 11 +#define IQM_CF_TAP_IM27__M 0x7FF +#define IQM_CF_TAP_IM27__PRE 0x2 + +#define IQM_AF_COMM_EXEC__A 0x1870000 +#define IQM_AF_COMM_EXEC__W 2 +#define IQM_AF_COMM_EXEC__M 0x3 +#define IQM_AF_COMM_EXEC__PRE 0x0 +#define IQM_AF_COMM_EXEC_STOP 0x0 +#define IQM_AF_COMM_EXEC_ACTIVE 0x1 +#define IQM_AF_COMM_EXEC_HOLD 0x2 + +#define IQM_AF_COMM_MB__A 0x1870002 +#define IQM_AF_COMM_MB__W 8 +#define IQM_AF_COMM_MB__M 0xFF +#define IQM_AF_COMM_MB__PRE 0x0 +#define IQM_AF_COMM_MB_CTL__B 0 +#define IQM_AF_COMM_MB_CTL__W 1 +#define IQM_AF_COMM_MB_CTL__M 0x1 +#define IQM_AF_COMM_MB_CTL__PRE 0x0 +#define IQM_AF_COMM_MB_CTL_CTL_OFF 0x0 +#define IQM_AF_COMM_MB_CTL_CTL_ON 0x1 +#define IQM_AF_COMM_MB_OBS__B 1 +#define IQM_AF_COMM_MB_OBS__W 1 +#define IQM_AF_COMM_MB_OBS__M 0x2 +#define IQM_AF_COMM_MB_OBS__PRE 0x0 +#define IQM_AF_COMM_MB_OBS_OBS_OFF 0x0 +#define IQM_AF_COMM_MB_OBS_OBS_ON 0x2 +#define IQM_AF_COMM_MB_MUX_CTRL__B 2 +#define IQM_AF_COMM_MB_MUX_CTRL__W 3 +#define IQM_AF_COMM_MB_MUX_CTRL__M 0x1C +#define IQM_AF_COMM_MB_MUX_CTRL__PRE 0x0 +#define IQM_AF_COMM_MB_MUX_CTRL_AF_DATA_INPUT 0x0 +#define IQM_AF_COMM_MB_MUX_CTRL_SENSE_INPUT 0x4 +#define IQM_AF_COMM_MB_MUX_CTRL_AF_DATA_OUTPUT 0x8 +#define IQM_AF_COMM_MB_MUX_CTRL_IF_AGC_OUTPUT 0xC +#define IQM_AF_COMM_MB_MUX_CTRL_RF_AGC_OUTPUT 0x10 +#define IQM_AF_COMM_MB_MUX_OBS__B 5 +#define IQM_AF_COMM_MB_MUX_OBS__W 3 +#define IQM_AF_COMM_MB_MUX_OBS__M 0xE0 +#define IQM_AF_COMM_MB_MUX_OBS__PRE 0x0 +#define IQM_AF_COMM_MB_MUX_OBS_AF_DATA_INPUT 0x0 +#define IQM_AF_COMM_MB_MUX_OBS_SENSE_INPUT 0x20 +#define IQM_AF_COMM_MB_MUX_OBS_AF_DATA_OUTPUT 0x40 +#define IQM_AF_COMM_MB_MUX_OBS_IF_AGC_OUTPUT 0x60 +#define IQM_AF_COMM_MB_MUX_OBS_RF_AGC_OUTPUT 0x80 + +#define IQM_AF_COMM_INT_REQ__A 0x1870003 +#define IQM_AF_COMM_INT_REQ__W 1 +#define IQM_AF_COMM_INT_REQ__M 0x1 +#define IQM_AF_COMM_INT_REQ__PRE 0x0 +#define IQM_AF_COMM_INT_STA__A 0x1870005 +#define IQM_AF_COMM_INT_STA__W 2 +#define IQM_AF_COMM_INT_STA__M 0x3 +#define IQM_AF_COMM_INT_STA__PRE 0x0 +#define IQM_AF_COMM_INT_STA_CLP_INT_STA__B 0 +#define IQM_AF_COMM_INT_STA_CLP_INT_STA__W 1 +#define IQM_AF_COMM_INT_STA_CLP_INT_STA__M 0x1 +#define IQM_AF_COMM_INT_STA_CLP_INT_STA__PRE 0x0 +#define IQM_AF_COMM_INT_STA_SNS_INT_STA__B 1 +#define IQM_AF_COMM_INT_STA_SNS_INT_STA__W 1 +#define IQM_AF_COMM_INT_STA_SNS_INT_STA__M 0x2 +#define IQM_AF_COMM_INT_STA_SNS_INT_STA__PRE 0x0 + +#define IQM_AF_COMM_INT_MSK__A 0x1870006 +#define IQM_AF_COMM_INT_MSK__W 2 +#define IQM_AF_COMM_INT_MSK__M 0x3 +#define IQM_AF_COMM_INT_MSK__PRE 0x0 +#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__B 0 +#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__W 1 +#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__M 0x1 +#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__PRE 0x0 +#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__B 1 +#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__W 1 +#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__M 0x2 +#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__PRE 0x0 + +#define IQM_AF_COMM_INT_STM__A 0x1870007 +#define IQM_AF_COMM_INT_STM__W 2 +#define IQM_AF_COMM_INT_STM__M 0x3 +#define IQM_AF_COMM_INT_STM__PRE 0x0 +#define IQM_AF_COMM_INT_STM_CLP_INT_STA__B 0 +#define IQM_AF_COMM_INT_STM_CLP_INT_STA__W 1 +#define IQM_AF_COMM_INT_STM_CLP_INT_STA__M 0x1 +#define IQM_AF_COMM_INT_STM_CLP_INT_STA__PRE 0x0 +#define IQM_AF_COMM_INT_STM_SNS_INT_STA__B 1 +#define IQM_AF_COMM_INT_STM_SNS_INT_STA__W 1 +#define IQM_AF_COMM_INT_STM_SNS_INT_STA__M 0x2 +#define IQM_AF_COMM_INT_STM_SNS_INT_STA__PRE 0x0 + +#define IQM_AF_FDB_SEL__A 0x1870010 +#define IQM_AF_FDB_SEL__W 1 +#define IQM_AF_FDB_SEL__M 0x1 +#define IQM_AF_FDB_SEL__PRE 0x0 + +#define IQM_AF_INVEXT__A 0x1870011 +#define IQM_AF_INVEXT__W 1 +#define IQM_AF_INVEXT__M 0x1 +#define IQM_AF_INVEXT__PRE 0x0 +#define IQM_AF_CLKNEG__A 0x1870012 +#define IQM_AF_CLKNEG__W 2 +#define IQM_AF_CLKNEG__M 0x3 +#define IQM_AF_CLKNEG__PRE 0x0 + +#define IQM_AF_CLKNEG_CLKNEGPEAK__B 0 +#define IQM_AF_CLKNEG_CLKNEGPEAK__W 1 +#define IQM_AF_CLKNEG_CLKNEGPEAK__M 0x1 +#define IQM_AF_CLKNEG_CLKNEGPEAK__PRE 0x0 +#define IQM_AF_CLKNEG_CLKNEGPEAK_CLK_ADC_PEAK_POS 0x0 +#define IQM_AF_CLKNEG_CLKNEGPEAK_CLK_ADC_PEAK_NEG 0x1 + +#define IQM_AF_CLKNEG_CLKNEGDATA__B 1 +#define IQM_AF_CLKNEG_CLKNEGDATA__W 1 +#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2 +#define IQM_AF_CLKNEG_CLKNEGDATA__PRE 0x0 +#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0 +#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2 + +#define IQM_AF_MON_IN_MUX__A 0x1870013 +#define IQM_AF_MON_IN_MUX__W 2 +#define IQM_AF_MON_IN_MUX__M 0x3 +#define IQM_AF_MON_IN_MUX__PRE 0x0 + +#define IQM_AF_MON_IN5__A 0x1870014 +#define IQM_AF_MON_IN5__W 10 +#define IQM_AF_MON_IN5__M 0x3FF +#define IQM_AF_MON_IN5__PRE 0x0 + +#define IQM_AF_MON_IN4__A 0x1870015 +#define IQM_AF_MON_IN4__W 10 +#define IQM_AF_MON_IN4__M 0x3FF +#define IQM_AF_MON_IN4__PRE 0x0 + +#define IQM_AF_MON_IN3__A 0x1870016 +#define IQM_AF_MON_IN3__W 10 +#define IQM_AF_MON_IN3__M 0x3FF +#define IQM_AF_MON_IN3__PRE 0x0 + +#define IQM_AF_MON_IN2__A 0x1870017 +#define IQM_AF_MON_IN2__W 10 +#define IQM_AF_MON_IN2__M 0x3FF +#define IQM_AF_MON_IN2__PRE 0x0 + +#define IQM_AF_MON_IN1__A 0x1870018 +#define IQM_AF_MON_IN1__W 10 +#define IQM_AF_MON_IN1__M 0x3FF +#define IQM_AF_MON_IN1__PRE 0x0 + +#define IQM_AF_MON_IN0__A 0x1870019 +#define IQM_AF_MON_IN0__W 10 +#define IQM_AF_MON_IN0__M 0x3FF +#define IQM_AF_MON_IN0__PRE 0x0 + +#define IQM_AF_MON_IN_VAL__A 0x187001A +#define IQM_AF_MON_IN_VAL__W 1 +#define IQM_AF_MON_IN_VAL__M 0x1 +#define IQM_AF_MON_IN_VAL__PRE 0x0 + +#define IQM_AF_START_LOCK__A 0x187001B +#define IQM_AF_START_LOCK__W 1 +#define IQM_AF_START_LOCK__M 0x1 +#define IQM_AF_START_LOCK__PRE 0x0 + +#define IQM_AF_PHASE0__A 0x187001C +#define IQM_AF_PHASE0__W 7 +#define IQM_AF_PHASE0__M 0x7F +#define IQM_AF_PHASE0__PRE 0x0 + +#define IQM_AF_PHASE1__A 0x187001D +#define IQM_AF_PHASE1__W 7 +#define IQM_AF_PHASE1__M 0x7F +#define IQM_AF_PHASE1__PRE 0x0 + +#define IQM_AF_PHASE2__A 0x187001E +#define IQM_AF_PHASE2__W 7 +#define IQM_AF_PHASE2__M 0x7F +#define IQM_AF_PHASE2__PRE 0x0 + +#define IQM_AF_SCU_PHASE__A 0x187001F +#define IQM_AF_SCU_PHASE__W 2 +#define IQM_AF_SCU_PHASE__M 0x3 +#define IQM_AF_SCU_PHASE__PRE 0x0 + +#define IQM_AF_SYNC_SEL__A 0x1870020 +#define IQM_AF_SYNC_SEL__W 2 +#define IQM_AF_SYNC_SEL__M 0x3 +#define IQM_AF_SYNC_SEL__PRE 0x0 +#define IQM_AF_ADC_CONF__A 0x1870021 +#define IQM_AF_ADC_CONF__W 4 +#define IQM_AF_ADC_CONF__M 0xF +#define IQM_AF_ADC_CONF__PRE 0x0 + +#define IQM_AF_ADC_CONF_ADC_SIGN__B 0 +#define IQM_AF_ADC_CONF_ADC_SIGN__W 1 +#define IQM_AF_ADC_CONF_ADC_SIGN__M 0x1 +#define IQM_AF_ADC_CONF_ADC_SIGN__PRE 0x0 +#define IQM_AF_ADC_CONF_ADC_SIGN_ADC_SIGNED 0x0 +#define IQM_AF_ADC_CONF_ADC_SIGN_ADC_UNSIGNED 0x1 + +#define IQM_AF_ADC_CONF_BITREVERSE_ADC__B 1 +#define IQM_AF_ADC_CONF_BITREVERSE_ADC__W 1 +#define IQM_AF_ADC_CONF_BITREVERSE_ADC__M 0x2 +#define IQM_AF_ADC_CONF_BITREVERSE_ADC__PRE 0x0 +#define IQM_AF_ADC_CONF_BITREVERSE_ADC_ADC_NORMAL 0x0 +#define IQM_AF_ADC_CONF_BITREVERSE_ADC_ADC_BITREVERSED 0x2 + +#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__B 2 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__W 1 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__M 0x4 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__PRE 0x0 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSI_IFAGC_DAC_NORMAL 0x0 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSI_IFAGC_DAC_BITREVERSED 0x4 + +#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__B 3 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__W 1 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__M 0x8 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__PRE 0x0 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSR_RFAGC_DAC_NORMAL 0x0 +#define IQM_AF_ADC_CONF_BITREVERSE_NSSR_RFAGC_DAC_BITREVERSED 0x8 + +#define IQM_AF_CLP_CLIP__A 0x1870022 +#define IQM_AF_CLP_CLIP__W 16 +#define IQM_AF_CLP_CLIP__M 0xFFFF +#define IQM_AF_CLP_CLIP__PRE 0x0 + +#define IQM_AF_CLP_LEN__A 0x1870023 +#define IQM_AF_CLP_LEN__W 16 +#define IQM_AF_CLP_LEN__M 0xFFFF +#define IQM_AF_CLP_LEN__PRE 0x0 +#define IQM_AF_CLP_LEN_QAM_B_64 0x400 +#define IQM_AF_CLP_LEN_QAM_B_256 0x400 +#define IQM_AF_CLP_LEN_ATV 0x0 + +#define IQM_AF_CLP_TH__A 0x1870024 +#define IQM_AF_CLP_TH__W 9 +#define IQM_AF_CLP_TH__M 0x1FF +#define IQM_AF_CLP_TH__PRE 0x0 +#define IQM_AF_CLP_TH_QAM_B_64 0x80 +#define IQM_AF_CLP_TH_QAM_B_256 0x80 +#define IQM_AF_CLP_TH_ATV 0x1C0 + +#define IQM_AF_DCF_BYPASS__A 0x1870025 +#define IQM_AF_DCF_BYPASS__W 1 +#define IQM_AF_DCF_BYPASS__M 0x1 +#define IQM_AF_DCF_BYPASS__PRE 0x0 +#define IQM_AF_DCF_BYPASS_ACTIVE 0x0 +#define IQM_AF_DCF_BYPASS_BYPASS 0x1 + +#define IQM_AF_SNS_LEN__A 0x1870026 +#define IQM_AF_SNS_LEN__W 16 +#define IQM_AF_SNS_LEN__M 0xFFFF +#define IQM_AF_SNS_LEN__PRE 0x0 +#define IQM_AF_SNS_LEN_QAM_B_64 0x400 +#define IQM_AF_SNS_LEN_QAM_B_256 0x400 +#define IQM_AF_SNS_LEN_ATV 0x0 + +#define IQM_AF_SNS_SENSE__A 0x1870027 +#define IQM_AF_SNS_SENSE__W 16 +#define IQM_AF_SNS_SENSE__M 0xFFFF +#define IQM_AF_SNS_SENSE__PRE 0x0 + +#define IQM_AF_AGC_IF__A 0x1870028 +#define IQM_AF_AGC_IF__W 15 +#define IQM_AF_AGC_IF__M 0x7FFF +#define IQM_AF_AGC_IF__PRE 0x0 + +#define IQM_AF_AGC_RF__A 0x1870029 +#define IQM_AF_AGC_RF__W 15 +#define IQM_AF_AGC_RF__M 0x7FFF +#define IQM_AF_AGC_RF__PRE 0x0 + +#define IQM_AF_PGA_GAIN__A 0x187002A +#define IQM_AF_PGA_GAIN__W 4 +#define IQM_AF_PGA_GAIN__M 0xF +#define IQM_AF_PGA_GAIN__PRE 0x0 + +#define IQM_AF_PDREF__A 0x187002B +#define IQM_AF_PDREF__W 5 +#define IQM_AF_PDREF__M 0x1F +#define IQM_AF_PDREF__PRE 0x0 +#define IQM_AF_PDREF_QAM_B_64 0xF +#define IQM_AF_PDREF_QAM_B_256 0xF +#define IQM_AF_PDREF_ATV 0xF + +#define IQM_AF_STDBY__A 0x187002C +#define IQM_AF_STDBY__W 6 +#define IQM_AF_STDBY__M 0x3F +#define IQM_AF_STDBY__PRE 0x0 + +#define IQM_AF_STDBY_STDBY_BIAS__B 0 +#define IQM_AF_STDBY_STDBY_BIAS__W 1 +#define IQM_AF_STDBY_STDBY_BIAS__M 0x1 +#define IQM_AF_STDBY_STDBY_BIAS__PRE 0x0 +#define IQM_AF_STDBY_STDBY_BIAS_ACTIVE 0x0 +#define IQM_AF_STDBY_STDBY_BIAS_STANDBY 0x1 + +#define IQM_AF_STDBY_STDBY_ADC__B 1 +#define IQM_AF_STDBY_STDBY_ADC__W 1 +#define IQM_AF_STDBY_STDBY_ADC__M 0x2 +#define IQM_AF_STDBY_STDBY_ADC__PRE 0x0 +#define IQM_AF_STDBY_STDBY_ADC_A1_ACTIVE 0x0 +#define IQM_AF_STDBY_STDBY_ADC_A1_STANDBY 0x2 +#define IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE 0x2 +#define IQM_AF_STDBY_STDBY_ADC_A2_STANDBY 0x0 + +#define IQM_AF_STDBY_STDBY_AMP__B 2 +#define IQM_AF_STDBY_STDBY_AMP__W 1 +#define IQM_AF_STDBY_STDBY_AMP__M 0x4 +#define IQM_AF_STDBY_STDBY_AMP__PRE 0x0 +#define IQM_AF_STDBY_STDBY_AMP_A1_ACTIVE 0x0 +#define IQM_AF_STDBY_STDBY_AMP_A1_STANDBY 0x4 +#define IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE 0x4 +#define IQM_AF_STDBY_STDBY_AMP_A2_STANDBY 0x0 + +#define IQM_AF_STDBY_STDBY_PD__B 3 +#define IQM_AF_STDBY_STDBY_PD__W 1 +#define IQM_AF_STDBY_STDBY_PD__M 0x8 +#define IQM_AF_STDBY_STDBY_PD__PRE 0x0 +#define IQM_AF_STDBY_STDBY_PD_A1_ACTIVE 0x0 +#define IQM_AF_STDBY_STDBY_PD_A1_STANDBY 0x8 +#define IQM_AF_STDBY_STDBY_PD_A2_ACTIVE 0x8 +#define IQM_AF_STDBY_STDBY_PD_A2_STANDBY 0x0 + +#define IQM_AF_STDBY_STDBY_TAGC_IF__B 4 +#define IQM_AF_STDBY_STDBY_TAGC_IF__W 1 +#define IQM_AF_STDBY_STDBY_TAGC_IF__M 0x10 +#define IQM_AF_STDBY_STDBY_TAGC_IF__PRE 0x0 +#define IQM_AF_STDBY_STDBY_TAGC_IF_A1_ACTIVE 0x0 +#define IQM_AF_STDBY_STDBY_TAGC_IF_A1_STANDBY 0x10 +#define IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE 0x10 +#define IQM_AF_STDBY_STDBY_TAGC_IF_A2_STANDBY 0x0 + +#define IQM_AF_STDBY_STDBY_TAGC_RF__B 5 +#define IQM_AF_STDBY_STDBY_TAGC_RF__W 1 +#define IQM_AF_STDBY_STDBY_TAGC_RF__M 0x20 +#define IQM_AF_STDBY_STDBY_TAGC_RF__PRE 0x0 +#define IQM_AF_STDBY_STDBY_TAGC_RF_A1_ACTIVE 0x0 +#define IQM_AF_STDBY_STDBY_TAGC_RF_A1_STANDBY 0x20 +#define IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE 0x20 +#define IQM_AF_STDBY_STDBY_TAGC_RF_A2_STANDBY 0x0 + +#define IQM_AF_AMUX__A 0x187002D +#define IQM_AF_AMUX__W 2 +#define IQM_AF_AMUX__M 0x3 +#define IQM_AF_AMUX__PRE 0x0 + +#define IQM_AF_TST_AFEMAIN__A 0x187002E +#define IQM_AF_TST_AFEMAIN__W 8 +#define IQM_AF_TST_AFEMAIN__M 0xFF +#define IQM_AF_TST_AFEMAIN__PRE 0x0 + +#define IQM_RT_RAM__A 0x1880000 + +#define IQM_RT_RAM_DLY__B 0 +#define IQM_RT_RAM_DLY__W 13 +#define IQM_RT_RAM_DLY__M 0x1FFF +#define IQM_RT_RAM_DLY__PRE 0x0 + +#define ORX_COMM_EXEC__A 0x2000000 +#define ORX_COMM_EXEC__W 2 +#define ORX_COMM_EXEC__M 0x3 +#define ORX_COMM_EXEC__PRE 0x0 +#define ORX_COMM_EXEC_STOP 0x0 +#define ORX_COMM_EXEC_ACTIVE 0x1 +#define ORX_COMM_EXEC_HOLD 0x2 + +#define ORX_COMM_STATE__A 0x2000001 +#define ORX_COMM_STATE__W 16 +#define ORX_COMM_STATE__M 0xFFFF +#define ORX_COMM_STATE__PRE 0x0 +#define ORX_COMM_MB__A 0x2000002 +#define ORX_COMM_MB__W 16 +#define ORX_COMM_MB__M 0xFFFF +#define ORX_COMM_MB__PRE 0x0 +#define ORX_COMM_INT_REQ__A 0x2000003 +#define ORX_COMM_INT_REQ__W 16 +#define ORX_COMM_INT_REQ__M 0xFFFF +#define ORX_COMM_INT_REQ__PRE 0x0 +#define ORX_COMM_INT_REQ_EQU_REQ__B 0 +#define ORX_COMM_INT_REQ_EQU_REQ__W 1 +#define ORX_COMM_INT_REQ_EQU_REQ__M 0x1 +#define ORX_COMM_INT_REQ_EQU_REQ__PRE 0x0 +#define ORX_COMM_INT_REQ_DDC_REQ__B 1 +#define ORX_COMM_INT_REQ_DDC_REQ__W 1 +#define ORX_COMM_INT_REQ_DDC_REQ__M 0x2 +#define ORX_COMM_INT_REQ_DDC_REQ__PRE 0x0 +#define ORX_COMM_INT_REQ_FWP_REQ__B 2 +#define ORX_COMM_INT_REQ_FWP_REQ__W 1 +#define ORX_COMM_INT_REQ_FWP_REQ__M 0x4 +#define ORX_COMM_INT_REQ_FWP_REQ__PRE 0x0 +#define ORX_COMM_INT_REQ_CON_REQ__B 3 +#define ORX_COMM_INT_REQ_CON_REQ__W 1 +#define ORX_COMM_INT_REQ_CON_REQ__M 0x8 +#define ORX_COMM_INT_REQ_CON_REQ__PRE 0x0 +#define ORX_COMM_INT_REQ_NSU_REQ__B 4 +#define ORX_COMM_INT_REQ_NSU_REQ__W 1 +#define ORX_COMM_INT_REQ_NSU_REQ__M 0x10 +#define ORX_COMM_INT_REQ_NSU_REQ__PRE 0x0 + +#define ORX_COMM_INT_STA__A 0x2000005 +#define ORX_COMM_INT_STA__W 16 +#define ORX_COMM_INT_STA__M 0xFFFF +#define ORX_COMM_INT_STA__PRE 0x0 +#define ORX_COMM_INT_MSK__A 0x2000006 +#define ORX_COMM_INT_MSK__W 16 +#define ORX_COMM_INT_MSK__M 0xFFFF +#define ORX_COMM_INT_MSK__PRE 0x0 +#define ORX_COMM_INT_STM__A 0x2000007 +#define ORX_COMM_INT_STM__W 16 +#define ORX_COMM_INT_STM__M 0xFFFF +#define ORX_COMM_INT_STM__PRE 0x0 + +#define ORX_TOP_COMM_EXEC__A 0x2010000 +#define ORX_TOP_COMM_EXEC__W 2 +#define ORX_TOP_COMM_EXEC__M 0x3 +#define ORX_TOP_COMM_EXEC__PRE 0x0 +#define ORX_TOP_COMM_EXEC_STOP 0x0 +#define ORX_TOP_COMM_EXEC_ACTIVE 0x1 +#define ORX_TOP_COMM_EXEC_HOLD 0x2 + +#define ORX_TOP_COMM_KEY__A 0x201000F +#define ORX_TOP_COMM_KEY__W 16 +#define ORX_TOP_COMM_KEY__M 0xFFFF +#define ORX_TOP_COMM_KEY__PRE 0x0 +#define ORX_TOP_COMM_KEY_KEY 0xFABA + +#define ORX_TOP_MDE_W__A 0x2010010 +#define ORX_TOP_MDE_W__W 2 +#define ORX_TOP_MDE_W__M 0x3 +#define ORX_TOP_MDE_W__PRE 0x2 +#define ORX_TOP_MDE_W_RATE_1544KBPS 0x0 +#define ORX_TOP_MDE_W_RATE_3088KBPS 0x1 +#define ORX_TOP_MDE_W_RATE_2048KBPS_SQRT 0x2 +#define ORX_TOP_MDE_W_RATE_2048KBPS_RO 0x3 + +#define ORX_TOP_AIF_CTRL_W__A 0x2010011 +#define ORX_TOP_AIF_CTRL_W__W 3 +#define ORX_TOP_AIF_CTRL_W__M 0x7 +#define ORX_TOP_AIF_CTRL_W__PRE 0x0 +#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__B 0 +#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__W 1 +#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__M 0x1 +#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__PRE 0x0 +#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE_ADC_SAMPL_ON_POS_CLK_EDGE 0x0 +#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE_ADC_SAMPL_ON_NEG_CLK_EDGE 0x1 +#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__B 1 +#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__W 1 +#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__M 0x2 +#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__PRE 0x0 +#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE_REGULAR_BIT_ORDER_ADC 0x0 +#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE_REVERSAL_BIT_ORDER_ADC 0x2 +#define ORX_TOP_AIF_CTRL_W_INV_MSB__B 2 +#define ORX_TOP_AIF_CTRL_W_INV_MSB__W 1 +#define ORX_TOP_AIF_CTRL_W_INV_MSB__M 0x4 +#define ORX_TOP_AIF_CTRL_W_INV_MSB__PRE 0x0 +#define ORX_TOP_AIF_CTRL_W_INV_MSB_NO_MSB_INVERSION_ADC 0x0 +#define ORX_TOP_AIF_CTRL_W_INV_MSB_MSB_INVERSION_ADC 0x4 + +#define ORX_FWP_COMM_EXEC__A 0x2020000 +#define ORX_FWP_COMM_EXEC__W 2 +#define ORX_FWP_COMM_EXEC__M 0x3 +#define ORX_FWP_COMM_EXEC__PRE 0x0 +#define ORX_FWP_COMM_EXEC_STOP 0x0 +#define ORX_FWP_COMM_EXEC_ACTIVE 0x1 +#define ORX_FWP_COMM_EXEC_HOLD 0x2 + +#define ORX_FWP_COMM_MB__A 0x2020002 +#define ORX_FWP_COMM_MB__W 8 +#define ORX_FWP_COMM_MB__M 0xFF +#define ORX_FWP_COMM_MB__PRE 0x0 +#define ORX_FWP_COMM_MB_CTL__B 0 +#define ORX_FWP_COMM_MB_CTL__W 1 +#define ORX_FWP_COMM_MB_CTL__M 0x1 +#define ORX_FWP_COMM_MB_CTL__PRE 0x0 +#define ORX_FWP_COMM_MB_CTL_OFF 0x0 +#define ORX_FWP_COMM_MB_CTL_ON 0x1 +#define ORX_FWP_COMM_MB_OBS__B 1 +#define ORX_FWP_COMM_MB_OBS__W 1 +#define ORX_FWP_COMM_MB_OBS__M 0x2 +#define ORX_FWP_COMM_MB_OBS__PRE 0x0 +#define ORX_FWP_COMM_MB_OBS_OFF 0x0 +#define ORX_FWP_COMM_MB_OBS_ON 0x2 + +#define ORX_FWP_COMM_MB_CTL_MUX__B 2 +#define ORX_FWP_COMM_MB_CTL_MUX__W 3 +#define ORX_FWP_COMM_MB_CTL_MUX__M 0x1C +#define ORX_FWP_COMM_MB_CTL_MUX__PRE 0x0 + +#define ORX_FWP_COMM_MB_OBS_MUX__B 5 +#define ORX_FWP_COMM_MB_OBS_MUX__W 3 +#define ORX_FWP_COMM_MB_OBS_MUX__M 0xE0 +#define ORX_FWP_COMM_MB_OBS_MUX__PRE 0x0 + +#define ORX_FWP_AAG_LEN_W__A 0x2020010 +#define ORX_FWP_AAG_LEN_W__W 16 +#define ORX_FWP_AAG_LEN_W__M 0xFFFF +#define ORX_FWP_AAG_LEN_W__PRE 0x800 + +#define ORX_FWP_AAG_THR_W__A 0x2020011 +#define ORX_FWP_AAG_THR_W__W 8 +#define ORX_FWP_AAG_THR_W__M 0xFF +#define ORX_FWP_AAG_THR_W__PRE 0x50 + +#define ORX_FWP_AAG_THR_CNT_R__A 0x2020012 +#define ORX_FWP_AAG_THR_CNT_R__W 16 +#define ORX_FWP_AAG_THR_CNT_R__M 0xFFFF +#define ORX_FWP_AAG_THR_CNT_R__PRE 0x0 + +#define ORX_FWP_AAG_SNS_CNT_R__A 0x2020013 +#define ORX_FWP_AAG_SNS_CNT_R__W 16 +#define ORX_FWP_AAG_SNS_CNT_R__M 0xFFFF +#define ORX_FWP_AAG_SNS_CNT_R__PRE 0x0 + +#define ORX_FWP_PFI_A_W__A 0x2020014 +#define ORX_FWP_PFI_A_W__W 8 +#define ORX_FWP_PFI_A_W__M 0xFF +#define ORX_FWP_PFI_A_W__PRE 0xB0 +#define ORX_FWP_PFI_A_W_RATE_2048KBPS 0xB0 +#define ORX_FWP_PFI_A_W_RATE_1544KBPS 0xA4 +#define ORX_FWP_PFI_A_W_RATE_3088KBPS 0xC0 + +#define ORX_FWP_PFI_B_W__A 0x2020015 +#define ORX_FWP_PFI_B_W__W 8 +#define ORX_FWP_PFI_B_W__M 0xFF +#define ORX_FWP_PFI_B_W__PRE 0x9E +#define ORX_FWP_PFI_B_W_RATE_2048KBPS 0x9E +#define ORX_FWP_PFI_B_W_RATE_1544KBPS 0x94 +#define ORX_FWP_PFI_B_W_RATE_3088KBPS 0xB0 + +#define ORX_FWP_PFI_C_W__A 0x2020016 +#define ORX_FWP_PFI_C_W__W 8 +#define ORX_FWP_PFI_C_W__M 0xFF +#define ORX_FWP_PFI_C_W__PRE 0x5C +#define ORX_FWP_PFI_C_W_RATE_2048KBPS 0x5C +#define ORX_FWP_PFI_C_W_RATE_1544KBPS 0x64 +#define ORX_FWP_PFI_C_W_RATE_3088KBPS 0x50 + +#define ORX_FWP_KR1_AMP_R__A 0x2020017 +#define ORX_FWP_KR1_AMP_R__W 9 +#define ORX_FWP_KR1_AMP_R__M 0x1FF +#define ORX_FWP_KR1_AMP_R__PRE 0x0 + +#define ORX_FWP_KR1_LDT_W__A 0x2020018 +#define ORX_FWP_KR1_LDT_W__W 3 +#define ORX_FWP_KR1_LDT_W__M 0x7 +#define ORX_FWP_KR1_LDT_W__PRE 0x2 +#define ORX_FWP_SRC_DGN_W__A 0x2020019 +#define ORX_FWP_SRC_DGN_W__W 16 +#define ORX_FWP_SRC_DGN_W__M 0xFFFF +#define ORX_FWP_SRC_DGN_W__PRE 0x1FF + +#define ORX_FWP_SRC_DGN_W_MANT__B 0 +#define ORX_FWP_SRC_DGN_W_MANT__W 9 +#define ORX_FWP_SRC_DGN_W_MANT__M 0x1FF +#define ORX_FWP_SRC_DGN_W_MANT__PRE 0x1FF + +#define ORX_FWP_SRC_DGN_W_EXP__B 12 +#define ORX_FWP_SRC_DGN_W_EXP__W 4 +#define ORX_FWP_SRC_DGN_W_EXP__M 0xF000 +#define ORX_FWP_SRC_DGN_W_EXP__PRE 0x0 + +#define ORX_FWP_NYQ_ADR_W__A 0x202001A +#define ORX_FWP_NYQ_ADR_W__W 5 +#define ORX_FWP_NYQ_ADR_W__M 0x1F +#define ORX_FWP_NYQ_ADR_W__PRE 0x1F + +#define ORX_FWP_NYQ_COF_RW__A 0x202001B +#define ORX_FWP_NYQ_COF_RW__W 10 +#define ORX_FWP_NYQ_COF_RW__M 0x3FF +#define ORX_FWP_NYQ_COF_RW__PRE 0x0 + +#define ORX_FWP_IQM_FRQ_W__A 0x202001C +#define ORX_FWP_IQM_FRQ_W__W 16 +#define ORX_FWP_IQM_FRQ_W__M 0xFFFF +#define ORX_FWP_IQM_FRQ_W__PRE 0x4301 + +#define ORX_EQU_COMM_EXEC__A 0x2030000 +#define ORX_EQU_COMM_EXEC__W 2 +#define ORX_EQU_COMM_EXEC__M 0x3 +#define ORX_EQU_COMM_EXEC__PRE 0x0 +#define ORX_EQU_COMM_EXEC_STOP 0x0 +#define ORX_EQU_COMM_EXEC_ACTIVE 0x1 +#define ORX_EQU_COMM_EXEC_HOLD 0x2 + +#define ORX_EQU_COMM_MB__A 0x2030002 +#define ORX_EQU_COMM_MB__W 8 +#define ORX_EQU_COMM_MB__M 0xFF +#define ORX_EQU_COMM_MB__PRE 0x0 +#define ORX_EQU_COMM_MB_CTL__B 0 +#define ORX_EQU_COMM_MB_CTL__W 1 +#define ORX_EQU_COMM_MB_CTL__M 0x1 +#define ORX_EQU_COMM_MB_CTL__PRE 0x0 +#define ORX_EQU_COMM_MB_CTL_OFF 0x0 +#define ORX_EQU_COMM_MB_CTL_ON 0x1 +#define ORX_EQU_COMM_MB_OBS__B 1 +#define ORX_EQU_COMM_MB_OBS__W 1 +#define ORX_EQU_COMM_MB_OBS__M 0x2 +#define ORX_EQU_COMM_MB_OBS__PRE 0x0 +#define ORX_EQU_COMM_MB_OBS_OFF 0x0 +#define ORX_EQU_COMM_MB_OBS_ON 0x2 + +#define ORX_EQU_COMM_MB_CTL_MUX__B 2 +#define ORX_EQU_COMM_MB_CTL_MUX__W 3 +#define ORX_EQU_COMM_MB_CTL_MUX__M 0x1C +#define ORX_EQU_COMM_MB_CTL_MUX__PRE 0x0 + +#define ORX_EQU_COMM_MB_OBS_MUX__B 5 +#define ORX_EQU_COMM_MB_OBS_MUX__W 3 +#define ORX_EQU_COMM_MB_OBS_MUX__M 0xE0 +#define ORX_EQU_COMM_MB_OBS_MUX__PRE 0x0 + +#define ORX_EQU_COMM_INT_REQ__A 0x2030003 +#define ORX_EQU_COMM_INT_REQ__W 1 +#define ORX_EQU_COMM_INT_REQ__M 0x1 +#define ORX_EQU_COMM_INT_REQ__PRE 0x0 +#define ORX_EQU_COMM_INT_STA__A 0x2030005 +#define ORX_EQU_COMM_INT_STA__W 2 +#define ORX_EQU_COMM_INT_STA__M 0x3 +#define ORX_EQU_COMM_INT_STA__PRE 0x0 + +#define ORX_EQU_COMM_INT_STA_FFF_READ__B 0 +#define ORX_EQU_COMM_INT_STA_FFF_READ__W 1 +#define ORX_EQU_COMM_INT_STA_FFF_READ__M 0x1 +#define ORX_EQU_COMM_INT_STA_FFF_READ__PRE 0x0 + +#define ORX_EQU_COMM_INT_STA_FBF_READ__B 1 +#define ORX_EQU_COMM_INT_STA_FBF_READ__W 1 +#define ORX_EQU_COMM_INT_STA_FBF_READ__M 0x2 +#define ORX_EQU_COMM_INT_STA_FBF_READ__PRE 0x0 + +#define ORX_EQU_COMM_INT_MSK__A 0x2030006 +#define ORX_EQU_COMM_INT_MSK__W 2 +#define ORX_EQU_COMM_INT_MSK__M 0x3 +#define ORX_EQU_COMM_INT_MSK__PRE 0x0 +#define ORX_EQU_COMM_INT_MSK_FFF_READ__B 0 +#define ORX_EQU_COMM_INT_MSK_FFF_READ__W 1 +#define ORX_EQU_COMM_INT_MSK_FFF_READ__M 0x1 +#define ORX_EQU_COMM_INT_MSK_FFF_READ__PRE 0x0 +#define ORX_EQU_COMM_INT_MSK_FBF_READ__B 1 +#define ORX_EQU_COMM_INT_MSK_FBF_READ__W 1 +#define ORX_EQU_COMM_INT_MSK_FBF_READ__M 0x2 +#define ORX_EQU_COMM_INT_MSK_FBF_READ__PRE 0x0 + +#define ORX_EQU_COMM_INT_STM__A 0x2030007 +#define ORX_EQU_COMM_INT_STM__W 2 +#define ORX_EQU_COMM_INT_STM__M 0x3 +#define ORX_EQU_COMM_INT_STM__PRE 0x0 +#define ORX_EQU_COMM_INT_STM_FFF_READ__B 0 +#define ORX_EQU_COMM_INT_STM_FFF_READ__W 1 +#define ORX_EQU_COMM_INT_STM_FFF_READ__M 0x1 +#define ORX_EQU_COMM_INT_STM_FFF_READ__PRE 0x0 +#define ORX_EQU_COMM_INT_STM_FBF_READ__B 1 +#define ORX_EQU_COMM_INT_STM_FBF_READ__W 1 +#define ORX_EQU_COMM_INT_STM_FBF_READ__M 0x2 +#define ORX_EQU_COMM_INT_STM_FBF_READ__PRE 0x0 + +#define ORX_EQU_FFF_SCL_W__A 0x2030010 +#define ORX_EQU_FFF_SCL_W__W 1 +#define ORX_EQU_FFF_SCL_W__M 0x1 +#define ORX_EQU_FFF_SCL_W__PRE 0x0 +#define ORX_EQU_FFF_SCL_W_SCALE_GAIN_1 0x0 +#define ORX_EQU_FFF_SCL_W_SCALE_GAIN_2 0x1 + +#define ORX_EQU_FFF_UPD_W__A 0x2030011 +#define ORX_EQU_FFF_UPD_W__W 1 +#define ORX_EQU_FFF_UPD_W__M 0x1 +#define ORX_EQU_FFF_UPD_W__PRE 0x0 +#define ORX_EQU_FFF_UPD_W_NO_UPDATE 0x0 +#define ORX_EQU_FFF_UPD_W_LMS_UPDATE 0x1 + +#define ORX_EQU_FFF_STP_W__A 0x2030012 +#define ORX_EQU_FFF_STP_W__W 3 +#define ORX_EQU_FFF_STP_W__M 0x7 +#define ORX_EQU_FFF_STP_W__PRE 0x2 + +#define ORX_EQU_FFF_LEA_W__A 0x2030013 +#define ORX_EQU_FFF_LEA_W__W 4 +#define ORX_EQU_FFF_LEA_W__M 0xF +#define ORX_EQU_FFF_LEA_W__PRE 0x4 + +#define ORX_EQU_FFF_RWT_W__A 0x2030014 +#define ORX_EQU_FFF_RWT_W__W 2 +#define ORX_EQU_FFF_RWT_W__M 0x3 +#define ORX_EQU_FFF_RWT_W__PRE 0x0 + +#define ORX_EQU_FFF_C0RE_RW__A 0x2030015 +#define ORX_EQU_FFF_C0RE_RW__W 12 +#define ORX_EQU_FFF_C0RE_RW__M 0xFFF +#define ORX_EQU_FFF_C0RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C0IM_RW__A 0x2030016 +#define ORX_EQU_FFF_C0IM_RW__W 12 +#define ORX_EQU_FFF_C0IM_RW__M 0xFFF +#define ORX_EQU_FFF_C0IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C1RE_RW__A 0x2030017 +#define ORX_EQU_FFF_C1RE_RW__W 12 +#define ORX_EQU_FFF_C1RE_RW__M 0xFFF +#define ORX_EQU_FFF_C1RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C1IM_RW__A 0x2030018 +#define ORX_EQU_FFF_C1IM_RW__W 12 +#define ORX_EQU_FFF_C1IM_RW__M 0xFFF +#define ORX_EQU_FFF_C1IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C2RE_RW__A 0x2030019 +#define ORX_EQU_FFF_C2RE_RW__W 12 +#define ORX_EQU_FFF_C2RE_RW__M 0xFFF +#define ORX_EQU_FFF_C2RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C2IM_RW__A 0x203001A +#define ORX_EQU_FFF_C2IM_RW__W 12 +#define ORX_EQU_FFF_C2IM_RW__M 0xFFF +#define ORX_EQU_FFF_C2IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C3RE_RW__A 0x203001B +#define ORX_EQU_FFF_C3RE_RW__W 12 +#define ORX_EQU_FFF_C3RE_RW__M 0xFFF +#define ORX_EQU_FFF_C3RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C3IM_RW__A 0x203001C +#define ORX_EQU_FFF_C3IM_RW__W 12 +#define ORX_EQU_FFF_C3IM_RW__M 0xFFF +#define ORX_EQU_FFF_C3IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C4RE_RW__A 0x203001D +#define ORX_EQU_FFF_C4RE_RW__W 12 +#define ORX_EQU_FFF_C4RE_RW__M 0xFFF +#define ORX_EQU_FFF_C4RE_RW__PRE 0x400 + +#define ORX_EQU_FFF_C4IM_RW__A 0x203001E +#define ORX_EQU_FFF_C4IM_RW__W 12 +#define ORX_EQU_FFF_C4IM_RW__M 0xFFF +#define ORX_EQU_FFF_C4IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C5RE_RW__A 0x203001F +#define ORX_EQU_FFF_C5RE_RW__W 12 +#define ORX_EQU_FFF_C5RE_RW__M 0xFFF +#define ORX_EQU_FFF_C5RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C5IM_RW__A 0x2030020 +#define ORX_EQU_FFF_C5IM_RW__W 12 +#define ORX_EQU_FFF_C5IM_RW__M 0xFFF +#define ORX_EQU_FFF_C5IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C6RE_RW__A 0x2030021 +#define ORX_EQU_FFF_C6RE_RW__W 12 +#define ORX_EQU_FFF_C6RE_RW__M 0xFFF +#define ORX_EQU_FFF_C6RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C6IM_RW__A 0x2030022 +#define ORX_EQU_FFF_C6IM_RW__W 12 +#define ORX_EQU_FFF_C6IM_RW__M 0xFFF +#define ORX_EQU_FFF_C6IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C7RE_RW__A 0x2030023 +#define ORX_EQU_FFF_C7RE_RW__W 12 +#define ORX_EQU_FFF_C7RE_RW__M 0xFFF +#define ORX_EQU_FFF_C7RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C7IM_RW__A 0x2030024 +#define ORX_EQU_FFF_C7IM_RW__W 12 +#define ORX_EQU_FFF_C7IM_RW__M 0xFFF +#define ORX_EQU_FFF_C7IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C8RE_RW__A 0x2030025 +#define ORX_EQU_FFF_C8RE_RW__W 12 +#define ORX_EQU_FFF_C8RE_RW__M 0xFFF +#define ORX_EQU_FFF_C8RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C8IM_RW__A 0x2030026 +#define ORX_EQU_FFF_C8IM_RW__W 12 +#define ORX_EQU_FFF_C8IM_RW__M 0xFFF +#define ORX_EQU_FFF_C8IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C9RE_RW__A 0x2030027 +#define ORX_EQU_FFF_C9RE_RW__W 12 +#define ORX_EQU_FFF_C9RE_RW__M 0xFFF +#define ORX_EQU_FFF_C9RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C9IM_RW__A 0x2030028 +#define ORX_EQU_FFF_C9IM_RW__W 12 +#define ORX_EQU_FFF_C9IM_RW__M 0xFFF +#define ORX_EQU_FFF_C9IM_RW__PRE 0x0 + +#define ORX_EQU_FFF_C10RE_RW__A 0x2030029 +#define ORX_EQU_FFF_C10RE_RW__W 12 +#define ORX_EQU_FFF_C10RE_RW__M 0xFFF +#define ORX_EQU_FFF_C10RE_RW__PRE 0x0 + +#define ORX_EQU_FFF_C10IM_RW__A 0x203002A +#define ORX_EQU_FFF_C10IM_RW__W 12 +#define ORX_EQU_FFF_C10IM_RW__M 0xFFF +#define ORX_EQU_FFF_C10IM_RW__PRE 0x0 + +#define ORX_EQU_MXB_SEL_W__A 0x203002B +#define ORX_EQU_MXB_SEL_W__W 1 +#define ORX_EQU_MXB_SEL_W__M 0x1 +#define ORX_EQU_MXB_SEL_W__PRE 0x0 +#define ORX_EQU_MXB_SEL_W_UNDECIDED_SYMBOLS 0x0 +#define ORX_EQU_MXB_SEL_W_DECIDED_SYMBOLS 0x1 + +#define ORX_EQU_FBF_UPD_W__A 0x203002C +#define ORX_EQU_FBF_UPD_W__W 1 +#define ORX_EQU_FBF_UPD_W__M 0x1 +#define ORX_EQU_FBF_UPD_W__PRE 0x0 +#define ORX_EQU_FBF_UPD_W_NO_UPDATE 0x0 +#define ORX_EQU_FBF_UPD_W_LMS_UPDATE 0x1 + +#define ORX_EQU_FBF_STP_W__A 0x203002D +#define ORX_EQU_FBF_STP_W__W 3 +#define ORX_EQU_FBF_STP_W__M 0x7 +#define ORX_EQU_FBF_STP_W__PRE 0x2 + +#define ORX_EQU_FBF_LEA_W__A 0x203002E +#define ORX_EQU_FBF_LEA_W__W 4 +#define ORX_EQU_FBF_LEA_W__M 0xF +#define ORX_EQU_FBF_LEA_W__PRE 0x4 + +#define ORX_EQU_FBF_RWT_W__A 0x203002F +#define ORX_EQU_FBF_RWT_W__W 2 +#define ORX_EQU_FBF_RWT_W__M 0x3 +#define ORX_EQU_FBF_RWT_W__PRE 0x0 + +#define ORX_EQU_FBF_C0RE_RW__A 0x2030030 +#define ORX_EQU_FBF_C0RE_RW__W 12 +#define ORX_EQU_FBF_C0RE_RW__M 0xFFF +#define ORX_EQU_FBF_C0RE_RW__PRE 0x0 + +#define ORX_EQU_FBF_C0IM_RW__A 0x2030031 +#define ORX_EQU_FBF_C0IM_RW__W 12 +#define ORX_EQU_FBF_C0IM_RW__M 0xFFF +#define ORX_EQU_FBF_C0IM_RW__PRE 0x0 + +#define ORX_EQU_FBF_C1RE_RW__A 0x2030032 +#define ORX_EQU_FBF_C1RE_RW__W 12 +#define ORX_EQU_FBF_C1RE_RW__M 0xFFF +#define ORX_EQU_FBF_C1RE_RW__PRE 0x0 + +#define ORX_EQU_FBF_C1IM_RW__A 0x2030033 +#define ORX_EQU_FBF_C1IM_RW__W 12 +#define ORX_EQU_FBF_C1IM_RW__M 0xFFF +#define ORX_EQU_FBF_C1IM_RW__PRE 0x0 + +#define ORX_EQU_FBF_C2RE_RW__A 0x2030034 +#define ORX_EQU_FBF_C2RE_RW__W 12 +#define ORX_EQU_FBF_C2RE_RW__M 0xFFF +#define ORX_EQU_FBF_C2RE_RW__PRE 0x0 + +#define ORX_EQU_FBF_C2IM_RW__A 0x2030035 +#define ORX_EQU_FBF_C2IM_RW__W 12 +#define ORX_EQU_FBF_C2IM_RW__M 0xFFF +#define ORX_EQU_FBF_C2IM_RW__PRE 0x0 + +#define ORX_EQU_FBF_C3RE_RW__A 0x2030036 +#define ORX_EQU_FBF_C3RE_RW__W 12 +#define ORX_EQU_FBF_C3RE_RW__M 0xFFF +#define ORX_EQU_FBF_C3RE_RW__PRE 0x0 + +#define ORX_EQU_FBF_C3IM_RW__A 0x2030037 +#define ORX_EQU_FBF_C3IM_RW__W 12 +#define ORX_EQU_FBF_C3IM_RW__M 0xFFF +#define ORX_EQU_FBF_C3IM_RW__PRE 0x0 + +#define ORX_EQU_FBF_C4RE_RW__A 0x2030038 +#define ORX_EQU_FBF_C4RE_RW__W 12 +#define ORX_EQU_FBF_C4RE_RW__M 0xFFF +#define ORX_EQU_FBF_C4RE_RW__PRE 0x0 + +#define ORX_EQU_FBF_C4IM_RW__A 0x2030039 +#define ORX_EQU_FBF_C4IM_RW__W 12 +#define ORX_EQU_FBF_C4IM_RW__M 0xFFF +#define ORX_EQU_FBF_C4IM_RW__PRE 0x0 + +#define ORX_EQU_FBF_C5RE_RW__A 0x203003A +#define ORX_EQU_FBF_C5RE_RW__W 12 +#define ORX_EQU_FBF_C5RE_RW__M 0xFFF +#define ORX_EQU_FBF_C5RE_RW__PRE 0x0 + +#define ORX_EQU_FBF_C5IM_RW__A 0x203003B +#define ORX_EQU_FBF_C5IM_RW__W 12 +#define ORX_EQU_FBF_C5IM_RW__M 0xFFF +#define ORX_EQU_FBF_C5IM_RW__PRE 0x0 + +#define ORX_EQU_ERR_SEL_W__A 0x203003C +#define ORX_EQU_ERR_SEL_W__W 1 +#define ORX_EQU_ERR_SEL_W__M 0x1 +#define ORX_EQU_ERR_SEL_W__PRE 0x0 +#define ORX_EQU_ERR_SEL_W_CMA_ERROR 0x0 +#define ORX_EQU_ERR_SEL_W_DDA_ERROR 0x1 + +#define ORX_EQU_ERR_TIS_W__A 0x203003D +#define ORX_EQU_ERR_TIS_W__W 1 +#define ORX_EQU_ERR_TIS_W__M 0x1 +#define ORX_EQU_ERR_TIS_W__PRE 0x0 +#define ORX_EQU_ERR_TIS_W_CMA_SIGNALS 0x0 +#define ORX_EQU_ERR_TIS_W_DDA_SIGNALS 0x1 + +#define ORX_EQU_ERR_EDI_R__A 0x203003E +#define ORX_EQU_ERR_EDI_R__W 5 +#define ORX_EQU_ERR_EDI_R__M 0x1F +#define ORX_EQU_ERR_EDI_R__PRE 0xF + +#define ORX_EQU_ERR_EDQ_R__A 0x203003F +#define ORX_EQU_ERR_EDQ_R__W 5 +#define ORX_EQU_ERR_EDQ_R__M 0x1F +#define ORX_EQU_ERR_EDQ_R__PRE 0xF + +#define ORX_EQU_ERR_ECI_R__A 0x2030040 +#define ORX_EQU_ERR_ECI_R__W 5 +#define ORX_EQU_ERR_ECI_R__M 0x1F +#define ORX_EQU_ERR_ECI_R__PRE 0xF + +#define ORX_EQU_ERR_ECQ_R__A 0x2030041 +#define ORX_EQU_ERR_ECQ_R__W 5 +#define ORX_EQU_ERR_ECQ_R__M 0x1F +#define ORX_EQU_ERR_ECQ_R__PRE 0xF + +#define ORX_EQU_MER_MER_R__A 0x2030042 +#define ORX_EQU_MER_MER_R__W 6 +#define ORX_EQU_MER_MER_R__M 0x3F +#define ORX_EQU_MER_MER_R__PRE 0x3F + +#define ORX_EQU_MER_LDT_W__A 0x2030043 +#define ORX_EQU_MER_LDT_W__W 3 +#define ORX_EQU_MER_LDT_W__M 0x7 +#define ORX_EQU_MER_LDT_W__PRE 0x4 + +#define ORX_EQU_SYN_LEN_W__A 0x2030044 +#define ORX_EQU_SYN_LEN_W__W 16 +#define ORX_EQU_SYN_LEN_W__M 0xFFFF +#define ORX_EQU_SYN_LEN_W__PRE 0x0 + +#define ORX_DDC_COMM_EXEC__A 0x2040000 +#define ORX_DDC_COMM_EXEC__W 2 +#define ORX_DDC_COMM_EXEC__M 0x3 +#define ORX_DDC_COMM_EXEC__PRE 0x0 +#define ORX_DDC_COMM_EXEC_STOP 0x0 +#define ORX_DDC_COMM_EXEC_ACTIVE 0x1 +#define ORX_DDC_COMM_EXEC_HOLD 0x2 + +#define ORX_DDC_COMM_MB__A 0x2040002 +#define ORX_DDC_COMM_MB__W 6 +#define ORX_DDC_COMM_MB__M 0x3F +#define ORX_DDC_COMM_MB__PRE 0x0 +#define ORX_DDC_COMM_MB_CTL__B 0 +#define ORX_DDC_COMM_MB_CTL__W 1 +#define ORX_DDC_COMM_MB_CTL__M 0x1 +#define ORX_DDC_COMM_MB_CTL__PRE 0x0 +#define ORX_DDC_COMM_MB_CTL_OFF 0x0 +#define ORX_DDC_COMM_MB_CTL_ON 0x1 +#define ORX_DDC_COMM_MB_OBS__B 1 +#define ORX_DDC_COMM_MB_OBS__W 1 +#define ORX_DDC_COMM_MB_OBS__M 0x2 +#define ORX_DDC_COMM_MB_OBS__PRE 0x0 +#define ORX_DDC_COMM_MB_OBS_OFF 0x0 +#define ORX_DDC_COMM_MB_OBS_ON 0x2 + +#define ORX_DDC_COMM_MB_CTL_MUX__B 2 +#define ORX_DDC_COMM_MB_CTL_MUX__W 2 +#define ORX_DDC_COMM_MB_CTL_MUX__M 0xC +#define ORX_DDC_COMM_MB_CTL_MUX__PRE 0x0 + +#define ORX_DDC_COMM_MB_OBS_MUX__B 4 +#define ORX_DDC_COMM_MB_OBS_MUX__W 2 +#define ORX_DDC_COMM_MB_OBS_MUX__M 0x30 +#define ORX_DDC_COMM_MB_OBS_MUX__PRE 0x0 + +#define ORX_DDC_COMM_INT_REQ__A 0x2040003 +#define ORX_DDC_COMM_INT_REQ__W 1 +#define ORX_DDC_COMM_INT_REQ__M 0x1 +#define ORX_DDC_COMM_INT_REQ__PRE 0x0 +#define ORX_DDC_COMM_INT_STA__A 0x2040005 +#define ORX_DDC_COMM_INT_STA__W 1 +#define ORX_DDC_COMM_INT_STA__M 0x1 +#define ORX_DDC_COMM_INT_STA__PRE 0x0 +#define ORX_DDC_COMM_INT_MSK__A 0x2040006 +#define ORX_DDC_COMM_INT_MSK__W 1 +#define ORX_DDC_COMM_INT_MSK__M 0x1 +#define ORX_DDC_COMM_INT_MSK__PRE 0x0 +#define ORX_DDC_COMM_INT_STM__A 0x2040007 +#define ORX_DDC_COMM_INT_STM__W 1 +#define ORX_DDC_COMM_INT_STM__M 0x1 +#define ORX_DDC_COMM_INT_STM__PRE 0x0 +#define ORX_DDC_DEC_MAP_W__A 0x2040010 +#define ORX_DDC_DEC_MAP_W__W 9 +#define ORX_DDC_DEC_MAP_W__M 0x1FF +#define ORX_DDC_DEC_MAP_W__PRE 0x178 + +#define ORX_DDC_DEC_MAP_W_QUADR0__B 0 +#define ORX_DDC_DEC_MAP_W_QUADR0__W 2 +#define ORX_DDC_DEC_MAP_W_QUADR0__M 0x3 +#define ORX_DDC_DEC_MAP_W_QUADR0__PRE 0x0 +#define ORX_DDC_DEC_MAP_W_QUADR0_ROTATE_DEFAULT 0x0 +#define ORX_DDC_DEC_MAP_W_QUADR0_ROTATE_ALTERNATE 0x0 + +#define ORX_DDC_DEC_MAP_W_QUADR1__B 2 +#define ORX_DDC_DEC_MAP_W_QUADR1__W 2 +#define ORX_DDC_DEC_MAP_W_QUADR1__M 0xC +#define ORX_DDC_DEC_MAP_W_QUADR1__PRE 0x8 +#define ORX_DDC_DEC_MAP_W_QUADR1_ROTATE_DEFAULT 0x8 +#define ORX_DDC_DEC_MAP_W_QUADR1_ROTATE_ALTERNATE 0x4 + +#define ORX_DDC_DEC_MAP_W_QUADR2__B 4 +#define ORX_DDC_DEC_MAP_W_QUADR2__W 2 +#define ORX_DDC_DEC_MAP_W_QUADR2__M 0x30 +#define ORX_DDC_DEC_MAP_W_QUADR2__PRE 0x30 +#define ORX_DDC_DEC_MAP_W_QUADR2_ROTATE_DEFAULT 0x30 +#define ORX_DDC_DEC_MAP_W_QUADR2_ROTATE_ALTERNATE 0x30 + +#define ORX_DDC_DEC_MAP_W_QUADR3__B 6 +#define ORX_DDC_DEC_MAP_W_QUADR3__W 2 +#define ORX_DDC_DEC_MAP_W_QUADR3__M 0xC0 +#define ORX_DDC_DEC_MAP_W_QUADR3__PRE 0x40 +#define ORX_DDC_DEC_MAP_W_QUADR3_ROTATE_DEFAULT 0x40 +#define ORX_DDC_DEC_MAP_W_QUADR3_ROTATE_ALTERNATE 0x80 +#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__B 8 +#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__W 1 +#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__M 0x100 +#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__PRE 0x100 +#define ORX_DDC_DEC_MAP_W_DIFF_DECOD_COHERENT_DECODING 0x0 +#define ORX_DDC_DEC_MAP_W_DIFF_DECOD_DIFF_DECODING 0x100 + +#define ORX_DDC_OFO_SET_W__A 0x2040011 +#define ORX_DDC_OFO_SET_W__W 16 +#define ORX_DDC_OFO_SET_W__M 0xFFFF +#define ORX_DDC_OFO_SET_W__PRE 0x1402 + +#define ORX_DDC_OFO_SET_W_PHASE__B 0 +#define ORX_DDC_OFO_SET_W_PHASE__W 7 +#define ORX_DDC_OFO_SET_W_PHASE__M 0x7F +#define ORX_DDC_OFO_SET_W_PHASE__PRE 0x2 + +#define ORX_DDC_OFO_SET_W_CRXHITIME__B 7 +#define ORX_DDC_OFO_SET_W_CRXHITIME__W 7 +#define ORX_DDC_OFO_SET_W_CRXHITIME__M 0x3F80 +#define ORX_DDC_OFO_SET_W_CRXHITIME__PRE 0x1400 + +#define ORX_DDC_OFO_SET_W_CRXINV__B 14 +#define ORX_DDC_OFO_SET_W_CRXINV__W 1 +#define ORX_DDC_OFO_SET_W_CRXINV__M 0x4000 +#define ORX_DDC_OFO_SET_W_CRXINV__PRE 0x0 + +#define ORX_DDC_OFO_SET_W_DISABLE__B 15 +#define ORX_DDC_OFO_SET_W_DISABLE__W 1 +#define ORX_DDC_OFO_SET_W_DISABLE__M 0x8000 +#define ORX_DDC_OFO_SET_W_DISABLE__PRE 0x0 + +#define ORX_CON_COMM_EXEC__A 0x2050000 +#define ORX_CON_COMM_EXEC__W 2 +#define ORX_CON_COMM_EXEC__M 0x3 +#define ORX_CON_COMM_EXEC__PRE 0x0 +#define ORX_CON_COMM_EXEC_STOP 0x0 +#define ORX_CON_COMM_EXEC_ACTIVE 0x1 +#define ORX_CON_COMM_EXEC_HOLD 0x2 + +#define ORX_CON_LDT_W__A 0x2050010 +#define ORX_CON_LDT_W__W 3 +#define ORX_CON_LDT_W__M 0x7 +#define ORX_CON_LDT_W__PRE 0x3 + +#define ORX_CON_LDT_W_CON_LDT_W__B 0 +#define ORX_CON_LDT_W_CON_LDT_W__W 3 +#define ORX_CON_LDT_W_CON_LDT_W__M 0x7 +#define ORX_CON_LDT_W_CON_LDT_W__PRE 0x3 + +#define ORX_CON_RST_W__A 0x2050011 +#define ORX_CON_RST_W__W 4 +#define ORX_CON_RST_W__M 0xF +#define ORX_CON_RST_W__PRE 0x0 + +#define ORX_CON_RST_W_CPH__B 0 +#define ORX_CON_RST_W_CPH__W 1 +#define ORX_CON_RST_W_CPH__M 0x1 +#define ORX_CON_RST_W_CPH__PRE 0x0 + +#define ORX_CON_RST_W_CTI__B 1 +#define ORX_CON_RST_W_CTI__W 1 +#define ORX_CON_RST_W_CTI__M 0x2 +#define ORX_CON_RST_W_CTI__PRE 0x0 + +#define ORX_CON_RST_W_KRN__B 2 +#define ORX_CON_RST_W_KRN__W 1 +#define ORX_CON_RST_W_KRN__M 0x4 +#define ORX_CON_RST_W_KRN__PRE 0x0 + +#define ORX_CON_RST_W_KRP__B 3 +#define ORX_CON_RST_W_KRP__W 1 +#define ORX_CON_RST_W_KRP__M 0x8 +#define ORX_CON_RST_W_KRP__PRE 0x0 + +#define ORX_CON_CPH_PHI_R__A 0x2050012 +#define ORX_CON_CPH_PHI_R__W 16 +#define ORX_CON_CPH_PHI_R__M 0xFFFF +#define ORX_CON_CPH_PHI_R__PRE 0x0 + +#define ORX_CON_CPH_FRQ_R__A 0x2050013 +#define ORX_CON_CPH_FRQ_R__W 16 +#define ORX_CON_CPH_FRQ_R__M 0xFFFF +#define ORX_CON_CPH_FRQ_R__PRE 0x0 + +#define ORX_CON_CPH_AMP_R__A 0x2050014 +#define ORX_CON_CPH_AMP_R__W 16 +#define ORX_CON_CPH_AMP_R__M 0xFFFF +#define ORX_CON_CPH_AMP_R__PRE 0x0 + +#define ORX_CON_CPH_KDF_W__A 0x2050015 +#define ORX_CON_CPH_KDF_W__W 4 +#define ORX_CON_CPH_KDF_W__M 0xF +#define ORX_CON_CPH_KDF_W__PRE 0x0 + +#define ORX_CON_CPH_KPF_W__A 0x2050016 +#define ORX_CON_CPH_KPF_W__W 4 +#define ORX_CON_CPH_KPF_W__M 0xF +#define ORX_CON_CPH_KPF_W__PRE 0x0 + +#define ORX_CON_CPH_KIF_W__A 0x2050017 +#define ORX_CON_CPH_KIF_W__W 4 +#define ORX_CON_CPH_KIF_W__M 0xF +#define ORX_CON_CPH_KIF_W__PRE 0x0 +#define ORX_CON_CPH_APT_W__A 0x2050018 +#define ORX_CON_CPH_APT_W__W 16 +#define ORX_CON_CPH_APT_W__M 0xFFFF +#define ORX_CON_CPH_APT_W__PRE 0x804 + +#define ORX_CON_CPH_APT_W_PTH__B 0 +#define ORX_CON_CPH_APT_W_PTH__W 8 +#define ORX_CON_CPH_APT_W_PTH__M 0xFF +#define ORX_CON_CPH_APT_W_PTH__PRE 0x4 + +#define ORX_CON_CPH_APT_W_ATH__B 8 +#define ORX_CON_CPH_APT_W_ATH__W 8 +#define ORX_CON_CPH_APT_W_ATH__M 0xFF00 +#define ORX_CON_CPH_APT_W_ATH__PRE 0x800 + +#define ORX_CON_CPH_WLC_W__A 0x2050019 +#define ORX_CON_CPH_WLC_W__W 8 +#define ORX_CON_CPH_WLC_W__M 0xFF +#define ORX_CON_CPH_WLC_W__PRE 0x81 + +#define ORX_CON_CPH_WLC_W_LATC__B 0 +#define ORX_CON_CPH_WLC_W_LATC__W 4 +#define ORX_CON_CPH_WLC_W_LATC__M 0xF +#define ORX_CON_CPH_WLC_W_LATC__PRE 0x1 + +#define ORX_CON_CPH_WLC_W_WLIM__B 4 +#define ORX_CON_CPH_WLC_W_WLIM__W 4 +#define ORX_CON_CPH_WLC_W_WLIM__M 0xF0 +#define ORX_CON_CPH_WLC_W_WLIM__PRE 0x80 + +#define ORX_CON_CPH_DLY_W__A 0x205001A +#define ORX_CON_CPH_DLY_W__W 3 +#define ORX_CON_CPH_DLY_W__M 0x7 +#define ORX_CON_CPH_DLY_W__PRE 0x4 + +#define ORX_CON_CPH_TCL_W__A 0x205001B +#define ORX_CON_CPH_TCL_W__W 3 +#define ORX_CON_CPH_TCL_W__M 0x7 +#define ORX_CON_CPH_TCL_W__PRE 0x3 + +#define ORX_CON_KRP_AMP_R__A 0x205001C +#define ORX_CON_KRP_AMP_R__W 9 +#define ORX_CON_KRP_AMP_R__M 0x1FF +#define ORX_CON_KRP_AMP_R__PRE 0x0 + +#define ORX_CON_KRN_AMP_R__A 0x205001D +#define ORX_CON_KRN_AMP_R__W 9 +#define ORX_CON_KRN_AMP_R__M 0x1FF +#define ORX_CON_KRN_AMP_R__PRE 0x0 + +#define ORX_CON_CTI_DTI_R__A 0x205001E +#define ORX_CON_CTI_DTI_R__W 16 +#define ORX_CON_CTI_DTI_R__M 0xFFFF +#define ORX_CON_CTI_DTI_R__PRE 0x0 + +#define ORX_CON_CTI_KDT_W__A 0x205001F +#define ORX_CON_CTI_KDT_W__W 4 +#define ORX_CON_CTI_KDT_W__M 0xF +#define ORX_CON_CTI_KDT_W__PRE 0x4 + +#define ORX_CON_CTI_KPT_W__A 0x2050020 +#define ORX_CON_CTI_KPT_W__W 4 +#define ORX_CON_CTI_KPT_W__M 0xF +#define ORX_CON_CTI_KPT_W__PRE 0x3 + +#define ORX_CON_CTI_KIT_W__A 0x2050021 +#define ORX_CON_CTI_KIT_W__W 4 +#define ORX_CON_CTI_KIT_W__M 0xF +#define ORX_CON_CTI_KIT_W__PRE 0xB + +#define ORX_CON_CTI_TAT_W__A 0x2050022 +#define ORX_CON_CTI_TAT_W__W 4 +#define ORX_CON_CTI_TAT_W__M 0xF +#define ORX_CON_CTI_TAT_W__PRE 0x3 + +#define ORX_NSU_COMM_EXEC__A 0x2060000 +#define ORX_NSU_COMM_EXEC__W 2 +#define ORX_NSU_COMM_EXEC__M 0x3 +#define ORX_NSU_COMM_EXEC__PRE 0x0 +#define ORX_NSU_COMM_EXEC_STOP 0x0 +#define ORX_NSU_COMM_EXEC_ACTIVE 0x1 +#define ORX_NSU_COMM_EXEC_HOLD 0x2 + +#define ORX_NSU_AOX_STDBY_W__A 0x2060010 +#define ORX_NSU_AOX_STDBY_W__W 8 +#define ORX_NSU_AOX_STDBY_W__M 0xFF +#define ORX_NSU_AOX_STDBY_W__PRE 0x0 + +#define ORX_NSU_AOX_STDBY_W_STDBYADC__B 0 +#define ORX_NSU_AOX_STDBY_W_STDBYADC__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYADC__M 0x1 +#define ORX_NSU_AOX_STDBY_W_STDBYADC__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYADC_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYADC_A1_OFF 0x1 +#define ORX_NSU_AOX_STDBY_W_STDBYADC_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON 0x1 + +#define ORX_NSU_AOX_STDBY_W_STDBYAMP__B 1 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP__M 0x2 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A1_OFF 0x2 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON 0x2 + +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__B 2 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__M 0x4 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A1_OFF 0x4 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON 0x4 + +#define ORX_NSU_AOX_STDBY_W_STDBYPLL__B 3 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL__M 0x8 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A1_OFF 0x8 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON 0x8 + +#define ORX_NSU_AOX_STDBY_W_STDBYPD__B 4 +#define ORX_NSU_AOX_STDBY_W_STDBYPD__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYPD__M 0x10 +#define ORX_NSU_AOX_STDBY_W_STDBYPD__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYPD_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYPD_A1_OFF 0x10 +#define ORX_NSU_AOX_STDBY_W_STDBYPD_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON 0x10 + +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__B 5 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__M 0x20 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A1_OFF 0x20 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON 0x20 + +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__B 6 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__M 0x40 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A1_OFF 0x40 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON 0x40 + +#define ORX_NSU_AOX_STDBY_W_STDBYFLT__B 7 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT__W 1 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT__M 0x80 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT__PRE 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A1_ON 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A1_OFF 0x80 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_OFF 0x0 +#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON 0x80 + +#define ORX_NSU_AOX_LOFRQ_W__A 0x2060011 +#define ORX_NSU_AOX_LOFRQ_W__W 16 +#define ORX_NSU_AOX_LOFRQ_W__M 0xFFFF +#define ORX_NSU_AOX_LOFRQ_W__PRE 0x0 +#define ORX_NSU_AOX_LOMDE_W__A 0x2060012 +#define ORX_NSU_AOX_LOMDE_W__W 16 +#define ORX_NSU_AOX_LOMDE_W__M 0xFFFF +#define ORX_NSU_AOX_LOMDE_W__PRE 0x0 + +#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__B 0 +#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__W 8 +#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__M 0xFF +#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__PRE 0x0 + +#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__B 13 +#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__W 1 +#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__M 0x2000 +#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__PRE 0x0 + +#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__B 14 +#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__W 2 +#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__M 0xC000 +#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__PRE 0x0 + +#define ORX_NSU_AOX_LOPOW_W__A 0x2060013 +#define ORX_NSU_AOX_LOPOW_W__W 2 +#define ORX_NSU_AOX_LOPOW_W__M 0x3 +#define ORX_NSU_AOX_LOPOW_W__PRE 0x0 +#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS0DB 0x0 +#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS5DB 0x1 +#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS10DB 0x2 +#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS15DB 0x3 + +#define ORX_NSU_AOX_STHR_W__A 0x2060014 +#define ORX_NSU_AOX_STHR_W__W 5 +#define ORX_NSU_AOX_STHR_W__M 0x1F +#define ORX_NSU_AOX_STHR_W__PRE 0x0 + +#define ORX_NSU_TUN_RFGAIN_W__A 0x2060015 +#define ORX_NSU_TUN_RFGAIN_W__W 15 +#define ORX_NSU_TUN_RFGAIN_W__M 0x7FFF +#define ORX_NSU_TUN_RFGAIN_W__PRE 0x0 + +#define ORX_NSU_TUN_IFGAIN_W__A 0x2060016 +#define ORX_NSU_TUN_IFGAIN_W__W 15 +#define ORX_NSU_TUN_IFGAIN_W__M 0x7FFF +#define ORX_NSU_TUN_IFGAIN_W__PRE 0x0 + +#define ORX_NSU_TUN_BPF_W__A 0x2060017 +#define ORX_NSU_TUN_BPF_W__W 15 +#define ORX_NSU_TUN_BPF_W__M 0x7FFF +#define ORX_NSU_TUN_BPF_W__PRE 0x1F9 +#define ORX_NSU_NSS_BITSWAP_W__A 0x2060018 +#define ORX_NSU_NSS_BITSWAP_W__W 3 +#define ORX_NSU_NSS_BITSWAP_W__M 0x7 +#define ORX_NSU_NSS_BITSWAP_W__PRE 0x0 + +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__B 0 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__W 1 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__M 0x1 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__PRE 0x0 + +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__B 1 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__W 1 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__M 0x2 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__PRE 0x0 + +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__B 2 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__W 1 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__M 0x4 +#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__PRE 0x0 + +#define ORX_TST_COMM_EXEC__A 0x23F0000 +#define ORX_TST_COMM_EXEC__W 2 +#define ORX_TST_COMM_EXEC__M 0x3 +#define ORX_TST_COMM_EXEC__PRE 0x0 +#define ORX_TST_COMM_EXEC_STOP 0x0 +#define ORX_TST_COMM_EXEC_ACTIVE 0x1 +#define ORX_TST_COMM_EXEC_HOLD 0x2 + +#define ORX_TST_AOX_TST_W__A 0x23F0010 +#define ORX_TST_AOX_TST_W__W 8 +#define ORX_TST_AOX_TST_W__M 0xFF +#define ORX_TST_AOX_TST_W__PRE 0x0 + +#define QAM_COMM_EXEC__A 0x1400000 +#define QAM_COMM_EXEC__W 2 +#define QAM_COMM_EXEC__M 0x3 +#define QAM_COMM_EXEC__PRE 0x0 +#define QAM_COMM_EXEC_STOP 0x0 +#define QAM_COMM_EXEC_ACTIVE 0x1 +#define QAM_COMM_EXEC_HOLD 0x2 + +#define QAM_COMM_MB__A 0x1400002 +#define QAM_COMM_MB__W 16 +#define QAM_COMM_MB__M 0xFFFF +#define QAM_COMM_MB__PRE 0x0 +#define QAM_COMM_INT_REQ__A 0x1400003 +#define QAM_COMM_INT_REQ__W 16 +#define QAM_COMM_INT_REQ__M 0xFFFF +#define QAM_COMM_INT_REQ__PRE 0x0 + +#define QAM_COMM_INT_REQ_SL_REQ__B 0 +#define QAM_COMM_INT_REQ_SL_REQ__W 1 +#define QAM_COMM_INT_REQ_SL_REQ__M 0x1 +#define QAM_COMM_INT_REQ_SL_REQ__PRE 0x0 + +#define QAM_COMM_INT_REQ_LC_REQ__B 1 +#define QAM_COMM_INT_REQ_LC_REQ__W 1 +#define QAM_COMM_INT_REQ_LC_REQ__M 0x2 +#define QAM_COMM_INT_REQ_LC_REQ__PRE 0x0 + +#define QAM_COMM_INT_REQ_VD_REQ__B 2 +#define QAM_COMM_INT_REQ_VD_REQ__W 1 +#define QAM_COMM_INT_REQ_VD_REQ__M 0x4 +#define QAM_COMM_INT_REQ_VD_REQ__PRE 0x0 + +#define QAM_COMM_INT_REQ_SY_REQ__B 3 +#define QAM_COMM_INT_REQ_SY_REQ__W 1 +#define QAM_COMM_INT_REQ_SY_REQ__M 0x8 +#define QAM_COMM_INT_REQ_SY_REQ__PRE 0x0 + +#define QAM_COMM_INT_STA__A 0x1400005 +#define QAM_COMM_INT_STA__W 16 +#define QAM_COMM_INT_STA__M 0xFFFF +#define QAM_COMM_INT_STA__PRE 0x0 +#define QAM_COMM_INT_MSK__A 0x1400006 +#define QAM_COMM_INT_MSK__W 16 +#define QAM_COMM_INT_MSK__M 0xFFFF +#define QAM_COMM_INT_MSK__PRE 0x0 +#define QAM_COMM_INT_STM__A 0x1400007 +#define QAM_COMM_INT_STM__W 16 +#define QAM_COMM_INT_STM__M 0xFFFF +#define QAM_COMM_INT_STM__PRE 0x0 + +#define QAM_TOP_COMM_EXEC__A 0x1410000 +#define QAM_TOP_COMM_EXEC__W 2 +#define QAM_TOP_COMM_EXEC__M 0x3 +#define QAM_TOP_COMM_EXEC__PRE 0x0 +#define QAM_TOP_COMM_EXEC_STOP 0x0 +#define QAM_TOP_COMM_EXEC_ACTIVE 0x1 +#define QAM_TOP_COMM_EXEC_HOLD 0x2 + +#define QAM_TOP_ANNEX__A 0x1410010 +#define QAM_TOP_ANNEX__W 2 +#define QAM_TOP_ANNEX__M 0x3 +#define QAM_TOP_ANNEX__PRE 0x1 +#define QAM_TOP_ANNEX_A 0x0 +#define QAM_TOP_ANNEX_B 0x1 +#define QAM_TOP_ANNEX_C 0x2 +#define QAM_TOP_ANNEX_D 0x3 + +#define QAM_TOP_CONSTELLATION__A 0x1410011 +#define QAM_TOP_CONSTELLATION__W 3 +#define QAM_TOP_CONSTELLATION__M 0x7 +#define QAM_TOP_CONSTELLATION__PRE 0x5 +#define QAM_TOP_CONSTELLATION_NONE 0x0 +#define QAM_TOP_CONSTELLATION_QPSK 0x1 +#define QAM_TOP_CONSTELLATION_QAM8 0x2 +#define QAM_TOP_CONSTELLATION_QAM16 0x3 +#define QAM_TOP_CONSTELLATION_QAM32 0x4 +#define QAM_TOP_CONSTELLATION_QAM64 0x5 +#define QAM_TOP_CONSTELLATION_QAM128 0x6 +#define QAM_TOP_CONSTELLATION_QAM256 0x7 + +#define QAM_FQ_COMM_EXEC__A 0x1420000 +#define QAM_FQ_COMM_EXEC__W 2 +#define QAM_FQ_COMM_EXEC__M 0x3 +#define QAM_FQ_COMM_EXEC__PRE 0x0 +#define QAM_FQ_COMM_EXEC_STOP 0x0 +#define QAM_FQ_COMM_EXEC_ACTIVE 0x1 +#define QAM_FQ_COMM_EXEC_HOLD 0x2 + +#define QAM_FQ_MODE__A 0x1420010 +#define QAM_FQ_MODE__W 3 +#define QAM_FQ_MODE__M 0x7 +#define QAM_FQ_MODE__PRE 0x0 + +#define QAM_FQ_MODE_TAPRESET__B 0 +#define QAM_FQ_MODE_TAPRESET__W 1 +#define QAM_FQ_MODE_TAPRESET__M 0x1 +#define QAM_FQ_MODE_TAPRESET__PRE 0x0 +#define QAM_FQ_MODE_TAPRESET_RST 0x1 + +#define QAM_FQ_MODE_TAPLMS__B 1 +#define QAM_FQ_MODE_TAPLMS__W 1 +#define QAM_FQ_MODE_TAPLMS__M 0x2 +#define QAM_FQ_MODE_TAPLMS__PRE 0x0 +#define QAM_FQ_MODE_TAPLMS_UPD 0x2 + +#define QAM_FQ_MODE_TAPDRAIN__B 2 +#define QAM_FQ_MODE_TAPDRAIN__W 1 +#define QAM_FQ_MODE_TAPDRAIN__M 0x4 +#define QAM_FQ_MODE_TAPDRAIN__PRE 0x0 +#define QAM_FQ_MODE_TAPDRAIN_DRAIN 0x4 + +#define QAM_FQ_MU_FACTOR__A 0x1420011 +#define QAM_FQ_MU_FACTOR__W 3 +#define QAM_FQ_MU_FACTOR__M 0x7 +#define QAM_FQ_MU_FACTOR__PRE 0x0 + +#define QAM_FQ_LA_FACTOR__A 0x1420012 +#define QAM_FQ_LA_FACTOR__W 4 +#define QAM_FQ_LA_FACTOR__M 0xF +#define QAM_FQ_LA_FACTOR__PRE 0xC +#define QAM_FQ_CENTTAP_IDX__A 0x1420016 +#define QAM_FQ_CENTTAP_IDX__W 5 +#define QAM_FQ_CENTTAP_IDX__M 0x1F +#define QAM_FQ_CENTTAP_IDX__PRE 0x13 + +#define QAM_FQ_CENTTAP_IDX_IDX__B 0 +#define QAM_FQ_CENTTAP_IDX_IDX__W 5 +#define QAM_FQ_CENTTAP_IDX_IDX__M 0x1F +#define QAM_FQ_CENTTAP_IDX_IDX__PRE 0x13 + +#define QAM_FQ_CENTTAP_VALUE__A 0x1420017 +#define QAM_FQ_CENTTAP_VALUE__W 12 +#define QAM_FQ_CENTTAP_VALUE__M 0xFFF +#define QAM_FQ_CENTTAP_VALUE__PRE 0x600 + +#define QAM_FQ_CENTTAP_VALUE_TAP__B 0 +#define QAM_FQ_CENTTAP_VALUE_TAP__W 12 +#define QAM_FQ_CENTTAP_VALUE_TAP__M 0xFFF +#define QAM_FQ_CENTTAP_VALUE_TAP__PRE 0x600 + +#define QAM_FQ_TAP_RE_EL0__A 0x1420020 +#define QAM_FQ_TAP_RE_EL0__W 12 +#define QAM_FQ_TAP_RE_EL0__M 0xFFF +#define QAM_FQ_TAP_RE_EL0__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL0_TAP__B 0 +#define QAM_FQ_TAP_RE_EL0_TAP__W 12 +#define QAM_FQ_TAP_RE_EL0_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL0_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL0__A 0x1420021 +#define QAM_FQ_TAP_IM_EL0__W 12 +#define QAM_FQ_TAP_IM_EL0__M 0xFFF +#define QAM_FQ_TAP_IM_EL0__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL0_TAP__B 0 +#define QAM_FQ_TAP_IM_EL0_TAP__W 12 +#define QAM_FQ_TAP_IM_EL0_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL0_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL1__A 0x1420022 +#define QAM_FQ_TAP_RE_EL1__W 12 +#define QAM_FQ_TAP_RE_EL1__M 0xFFF +#define QAM_FQ_TAP_RE_EL1__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL1_TAP__B 0 +#define QAM_FQ_TAP_RE_EL1_TAP__W 12 +#define QAM_FQ_TAP_RE_EL1_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL1_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL1__A 0x1420023 +#define QAM_FQ_TAP_IM_EL1__W 12 +#define QAM_FQ_TAP_IM_EL1__M 0xFFF +#define QAM_FQ_TAP_IM_EL1__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL1_TAP__B 0 +#define QAM_FQ_TAP_IM_EL1_TAP__W 12 +#define QAM_FQ_TAP_IM_EL1_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL1_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL2__A 0x1420024 +#define QAM_FQ_TAP_RE_EL2__W 12 +#define QAM_FQ_TAP_RE_EL2__M 0xFFF +#define QAM_FQ_TAP_RE_EL2__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL2_TAP__B 0 +#define QAM_FQ_TAP_RE_EL2_TAP__W 12 +#define QAM_FQ_TAP_RE_EL2_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL2_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL2__A 0x1420025 +#define QAM_FQ_TAP_IM_EL2__W 12 +#define QAM_FQ_TAP_IM_EL2__M 0xFFF +#define QAM_FQ_TAP_IM_EL2__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL2_TAP__B 0 +#define QAM_FQ_TAP_IM_EL2_TAP__W 12 +#define QAM_FQ_TAP_IM_EL2_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL2_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL3__A 0x1420026 +#define QAM_FQ_TAP_RE_EL3__W 12 +#define QAM_FQ_TAP_RE_EL3__M 0xFFF +#define QAM_FQ_TAP_RE_EL3__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL3_TAP__B 0 +#define QAM_FQ_TAP_RE_EL3_TAP__W 12 +#define QAM_FQ_TAP_RE_EL3_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL3_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL3__A 0x1420027 +#define QAM_FQ_TAP_IM_EL3__W 12 +#define QAM_FQ_TAP_IM_EL3__M 0xFFF +#define QAM_FQ_TAP_IM_EL3__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL3_TAP__B 0 +#define QAM_FQ_TAP_IM_EL3_TAP__W 12 +#define QAM_FQ_TAP_IM_EL3_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL3_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL4__A 0x1420028 +#define QAM_FQ_TAP_RE_EL4__W 12 +#define QAM_FQ_TAP_RE_EL4__M 0xFFF +#define QAM_FQ_TAP_RE_EL4__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL4_TAP__B 0 +#define QAM_FQ_TAP_RE_EL4_TAP__W 12 +#define QAM_FQ_TAP_RE_EL4_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL4_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL4__A 0x1420029 +#define QAM_FQ_TAP_IM_EL4__W 12 +#define QAM_FQ_TAP_IM_EL4__M 0xFFF +#define QAM_FQ_TAP_IM_EL4__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL4_TAP__B 0 +#define QAM_FQ_TAP_IM_EL4_TAP__W 12 +#define QAM_FQ_TAP_IM_EL4_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL4_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL5__A 0x142002A +#define QAM_FQ_TAP_RE_EL5__W 12 +#define QAM_FQ_TAP_RE_EL5__M 0xFFF +#define QAM_FQ_TAP_RE_EL5__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL5_TAP__B 0 +#define QAM_FQ_TAP_RE_EL5_TAP__W 12 +#define QAM_FQ_TAP_RE_EL5_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL5_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL5__A 0x142002B +#define QAM_FQ_TAP_IM_EL5__W 12 +#define QAM_FQ_TAP_IM_EL5__M 0xFFF +#define QAM_FQ_TAP_IM_EL5__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL5_TAP__B 0 +#define QAM_FQ_TAP_IM_EL5_TAP__W 12 +#define QAM_FQ_TAP_IM_EL5_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL5_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL6__A 0x142002C +#define QAM_FQ_TAP_RE_EL6__W 12 +#define QAM_FQ_TAP_RE_EL6__M 0xFFF +#define QAM_FQ_TAP_RE_EL6__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL6_TAP__B 0 +#define QAM_FQ_TAP_RE_EL6_TAP__W 12 +#define QAM_FQ_TAP_RE_EL6_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL6_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL6__A 0x142002D +#define QAM_FQ_TAP_IM_EL6__W 12 +#define QAM_FQ_TAP_IM_EL6__M 0xFFF +#define QAM_FQ_TAP_IM_EL6__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL6_TAP__B 0 +#define QAM_FQ_TAP_IM_EL6_TAP__W 12 +#define QAM_FQ_TAP_IM_EL6_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL6_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL7__A 0x142002E +#define QAM_FQ_TAP_RE_EL7__W 12 +#define QAM_FQ_TAP_RE_EL7__M 0xFFF +#define QAM_FQ_TAP_RE_EL7__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL7_TAP__B 0 +#define QAM_FQ_TAP_RE_EL7_TAP__W 12 +#define QAM_FQ_TAP_RE_EL7_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL7_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL7__A 0x142002F +#define QAM_FQ_TAP_IM_EL7__W 12 +#define QAM_FQ_TAP_IM_EL7__M 0xFFF +#define QAM_FQ_TAP_IM_EL7__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL7_TAP__B 0 +#define QAM_FQ_TAP_IM_EL7_TAP__W 12 +#define QAM_FQ_TAP_IM_EL7_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL7_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL8__A 0x1420030 +#define QAM_FQ_TAP_RE_EL8__W 12 +#define QAM_FQ_TAP_RE_EL8__M 0xFFF +#define QAM_FQ_TAP_RE_EL8__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL8_TAP__B 0 +#define QAM_FQ_TAP_RE_EL8_TAP__W 12 +#define QAM_FQ_TAP_RE_EL8_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL8_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL8__A 0x1420031 +#define QAM_FQ_TAP_IM_EL8__W 12 +#define QAM_FQ_TAP_IM_EL8__M 0xFFF +#define QAM_FQ_TAP_IM_EL8__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL8_TAP__B 0 +#define QAM_FQ_TAP_IM_EL8_TAP__W 12 +#define QAM_FQ_TAP_IM_EL8_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL8_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL9__A 0x1420032 +#define QAM_FQ_TAP_RE_EL9__W 12 +#define QAM_FQ_TAP_RE_EL9__M 0xFFF +#define QAM_FQ_TAP_RE_EL9__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL9_TAP__B 0 +#define QAM_FQ_TAP_RE_EL9_TAP__W 12 +#define QAM_FQ_TAP_RE_EL9_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL9_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL9__A 0x1420033 +#define QAM_FQ_TAP_IM_EL9__W 12 +#define QAM_FQ_TAP_IM_EL9__M 0xFFF +#define QAM_FQ_TAP_IM_EL9__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL9_TAP__B 0 +#define QAM_FQ_TAP_IM_EL9_TAP__W 12 +#define QAM_FQ_TAP_IM_EL9_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL9_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL10__A 0x1420034 +#define QAM_FQ_TAP_RE_EL10__W 12 +#define QAM_FQ_TAP_RE_EL10__M 0xFFF +#define QAM_FQ_TAP_RE_EL10__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL10_TAP__B 0 +#define QAM_FQ_TAP_RE_EL10_TAP__W 12 +#define QAM_FQ_TAP_RE_EL10_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL10_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL10__A 0x1420035 +#define QAM_FQ_TAP_IM_EL10__W 12 +#define QAM_FQ_TAP_IM_EL10__M 0xFFF +#define QAM_FQ_TAP_IM_EL10__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL10_TAP__B 0 +#define QAM_FQ_TAP_IM_EL10_TAP__W 12 +#define QAM_FQ_TAP_IM_EL10_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL10_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL11__A 0x1420036 +#define QAM_FQ_TAP_RE_EL11__W 12 +#define QAM_FQ_TAP_RE_EL11__M 0xFFF +#define QAM_FQ_TAP_RE_EL11__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL11_TAP__B 0 +#define QAM_FQ_TAP_RE_EL11_TAP__W 12 +#define QAM_FQ_TAP_RE_EL11_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL11_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL11__A 0x1420037 +#define QAM_FQ_TAP_IM_EL11__W 12 +#define QAM_FQ_TAP_IM_EL11__M 0xFFF +#define QAM_FQ_TAP_IM_EL11__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL11_TAP__B 0 +#define QAM_FQ_TAP_IM_EL11_TAP__W 12 +#define QAM_FQ_TAP_IM_EL11_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL11_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL12__A 0x1420038 +#define QAM_FQ_TAP_RE_EL12__W 12 +#define QAM_FQ_TAP_RE_EL12__M 0xFFF +#define QAM_FQ_TAP_RE_EL12__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL12_TAP__B 0 +#define QAM_FQ_TAP_RE_EL12_TAP__W 12 +#define QAM_FQ_TAP_RE_EL12_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL12_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL12__A 0x1420039 +#define QAM_FQ_TAP_IM_EL12__W 12 +#define QAM_FQ_TAP_IM_EL12__M 0xFFF +#define QAM_FQ_TAP_IM_EL12__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL12_TAP__B 0 +#define QAM_FQ_TAP_IM_EL12_TAP__W 12 +#define QAM_FQ_TAP_IM_EL12_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL12_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL13__A 0x142003A +#define QAM_FQ_TAP_RE_EL13__W 12 +#define QAM_FQ_TAP_RE_EL13__M 0xFFF +#define QAM_FQ_TAP_RE_EL13__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL13_TAP__B 0 +#define QAM_FQ_TAP_RE_EL13_TAP__W 12 +#define QAM_FQ_TAP_RE_EL13_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL13_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL13__A 0x142003B +#define QAM_FQ_TAP_IM_EL13__W 12 +#define QAM_FQ_TAP_IM_EL13__M 0xFFF +#define QAM_FQ_TAP_IM_EL13__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL13_TAP__B 0 +#define QAM_FQ_TAP_IM_EL13_TAP__W 12 +#define QAM_FQ_TAP_IM_EL13_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL13_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL14__A 0x142003C +#define QAM_FQ_TAP_RE_EL14__W 12 +#define QAM_FQ_TAP_RE_EL14__M 0xFFF +#define QAM_FQ_TAP_RE_EL14__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL14_TAP__B 0 +#define QAM_FQ_TAP_RE_EL14_TAP__W 12 +#define QAM_FQ_TAP_RE_EL14_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL14_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL14__A 0x142003D +#define QAM_FQ_TAP_IM_EL14__W 12 +#define QAM_FQ_TAP_IM_EL14__M 0xFFF +#define QAM_FQ_TAP_IM_EL14__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL14_TAP__B 0 +#define QAM_FQ_TAP_IM_EL14_TAP__W 12 +#define QAM_FQ_TAP_IM_EL14_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL14_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL15__A 0x142003E +#define QAM_FQ_TAP_RE_EL15__W 12 +#define QAM_FQ_TAP_RE_EL15__M 0xFFF +#define QAM_FQ_TAP_RE_EL15__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL15_TAP__B 0 +#define QAM_FQ_TAP_RE_EL15_TAP__W 12 +#define QAM_FQ_TAP_RE_EL15_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL15_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL15__A 0x142003F +#define QAM_FQ_TAP_IM_EL15__W 12 +#define QAM_FQ_TAP_IM_EL15__M 0xFFF +#define QAM_FQ_TAP_IM_EL15__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL15_TAP__B 0 +#define QAM_FQ_TAP_IM_EL15_TAP__W 12 +#define QAM_FQ_TAP_IM_EL15_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL15_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL16__A 0x1420040 +#define QAM_FQ_TAP_RE_EL16__W 12 +#define QAM_FQ_TAP_RE_EL16__M 0xFFF +#define QAM_FQ_TAP_RE_EL16__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL16_TAP__B 0 +#define QAM_FQ_TAP_RE_EL16_TAP__W 12 +#define QAM_FQ_TAP_RE_EL16_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL16_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL16__A 0x1420041 +#define QAM_FQ_TAP_IM_EL16__W 12 +#define QAM_FQ_TAP_IM_EL16__M 0xFFF +#define QAM_FQ_TAP_IM_EL16__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL16_TAP__B 0 +#define QAM_FQ_TAP_IM_EL16_TAP__W 12 +#define QAM_FQ_TAP_IM_EL16_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL16_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL17__A 0x1420042 +#define QAM_FQ_TAP_RE_EL17__W 12 +#define QAM_FQ_TAP_RE_EL17__M 0xFFF +#define QAM_FQ_TAP_RE_EL17__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL17_TAP__B 0 +#define QAM_FQ_TAP_RE_EL17_TAP__W 12 +#define QAM_FQ_TAP_RE_EL17_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL17_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL17__A 0x1420043 +#define QAM_FQ_TAP_IM_EL17__W 12 +#define QAM_FQ_TAP_IM_EL17__M 0xFFF +#define QAM_FQ_TAP_IM_EL17__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL17_TAP__B 0 +#define QAM_FQ_TAP_IM_EL17_TAP__W 12 +#define QAM_FQ_TAP_IM_EL17_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL17_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL18__A 0x1420044 +#define QAM_FQ_TAP_RE_EL18__W 12 +#define QAM_FQ_TAP_RE_EL18__M 0xFFF +#define QAM_FQ_TAP_RE_EL18__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL18_TAP__B 0 +#define QAM_FQ_TAP_RE_EL18_TAP__W 12 +#define QAM_FQ_TAP_RE_EL18_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL18_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL18__A 0x1420045 +#define QAM_FQ_TAP_IM_EL18__W 12 +#define QAM_FQ_TAP_IM_EL18__M 0xFFF +#define QAM_FQ_TAP_IM_EL18__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL18_TAP__B 0 +#define QAM_FQ_TAP_IM_EL18_TAP__W 12 +#define QAM_FQ_TAP_IM_EL18_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL18_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL19__A 0x1420046 +#define QAM_FQ_TAP_RE_EL19__W 12 +#define QAM_FQ_TAP_RE_EL19__M 0xFFF +#define QAM_FQ_TAP_RE_EL19__PRE 0x600 + +#define QAM_FQ_TAP_RE_EL19_TAP__B 0 +#define QAM_FQ_TAP_RE_EL19_TAP__W 12 +#define QAM_FQ_TAP_RE_EL19_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL19_TAP__PRE 0x600 + +#define QAM_FQ_TAP_IM_EL19__A 0x1420047 +#define QAM_FQ_TAP_IM_EL19__W 12 +#define QAM_FQ_TAP_IM_EL19__M 0xFFF +#define QAM_FQ_TAP_IM_EL19__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL19_TAP__B 0 +#define QAM_FQ_TAP_IM_EL19_TAP__W 12 +#define QAM_FQ_TAP_IM_EL19_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL19_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL20__A 0x1420048 +#define QAM_FQ_TAP_RE_EL20__W 12 +#define QAM_FQ_TAP_RE_EL20__M 0xFFF +#define QAM_FQ_TAP_RE_EL20__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL20_TAP__B 0 +#define QAM_FQ_TAP_RE_EL20_TAP__W 12 +#define QAM_FQ_TAP_RE_EL20_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL20_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL20__A 0x1420049 +#define QAM_FQ_TAP_IM_EL20__W 12 +#define QAM_FQ_TAP_IM_EL20__M 0xFFF +#define QAM_FQ_TAP_IM_EL20__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL20_TAP__B 0 +#define QAM_FQ_TAP_IM_EL20_TAP__W 12 +#define QAM_FQ_TAP_IM_EL20_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL20_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL21__A 0x142004A +#define QAM_FQ_TAP_RE_EL21__W 12 +#define QAM_FQ_TAP_RE_EL21__M 0xFFF +#define QAM_FQ_TAP_RE_EL21__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL21_TAP__B 0 +#define QAM_FQ_TAP_RE_EL21_TAP__W 12 +#define QAM_FQ_TAP_RE_EL21_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL21_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL21__A 0x142004B +#define QAM_FQ_TAP_IM_EL21__W 12 +#define QAM_FQ_TAP_IM_EL21__M 0xFFF +#define QAM_FQ_TAP_IM_EL21__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL21_TAP__B 0 +#define QAM_FQ_TAP_IM_EL21_TAP__W 12 +#define QAM_FQ_TAP_IM_EL21_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL21_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL22__A 0x142004C +#define QAM_FQ_TAP_RE_EL22__W 12 +#define QAM_FQ_TAP_RE_EL22__M 0xFFF +#define QAM_FQ_TAP_RE_EL22__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL22_TAP__B 0 +#define QAM_FQ_TAP_RE_EL22_TAP__W 12 +#define QAM_FQ_TAP_RE_EL22_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL22_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL22__A 0x142004D +#define QAM_FQ_TAP_IM_EL22__W 12 +#define QAM_FQ_TAP_IM_EL22__M 0xFFF +#define QAM_FQ_TAP_IM_EL22__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL22_TAP__B 0 +#define QAM_FQ_TAP_IM_EL22_TAP__W 12 +#define QAM_FQ_TAP_IM_EL22_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL22_TAP__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL23__A 0x142004E +#define QAM_FQ_TAP_RE_EL23__W 12 +#define QAM_FQ_TAP_RE_EL23__M 0xFFF +#define QAM_FQ_TAP_RE_EL23__PRE 0x2 + +#define QAM_FQ_TAP_RE_EL23_TAP__B 0 +#define QAM_FQ_TAP_RE_EL23_TAP__W 12 +#define QAM_FQ_TAP_RE_EL23_TAP__M 0xFFF +#define QAM_FQ_TAP_RE_EL23_TAP__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL23__A 0x142004F +#define QAM_FQ_TAP_IM_EL23__W 12 +#define QAM_FQ_TAP_IM_EL23__M 0xFFF +#define QAM_FQ_TAP_IM_EL23__PRE 0x2 + +#define QAM_FQ_TAP_IM_EL23_TAP__B 0 +#define QAM_FQ_TAP_IM_EL23_TAP__W 12 +#define QAM_FQ_TAP_IM_EL23_TAP__M 0xFFF +#define QAM_FQ_TAP_IM_EL23_TAP__PRE 0x2 + +#define QAM_SL_COMM_EXEC__A 0x1430000 +#define QAM_SL_COMM_EXEC__W 2 +#define QAM_SL_COMM_EXEC__M 0x3 +#define QAM_SL_COMM_EXEC__PRE 0x0 +#define QAM_SL_COMM_EXEC_STOP 0x0 +#define QAM_SL_COMM_EXEC_ACTIVE 0x1 +#define QAM_SL_COMM_EXEC_HOLD 0x2 + +#define QAM_SL_COMM_MB__A 0x1430002 +#define QAM_SL_COMM_MB__W 4 +#define QAM_SL_COMM_MB__M 0xF +#define QAM_SL_COMM_MB__PRE 0x0 +#define QAM_SL_COMM_MB_CTL__B 0 +#define QAM_SL_COMM_MB_CTL__W 1 +#define QAM_SL_COMM_MB_CTL__M 0x1 +#define QAM_SL_COMM_MB_CTL__PRE 0x0 +#define QAM_SL_COMM_MB_CTL_OFF 0x0 +#define QAM_SL_COMM_MB_CTL_ON 0x1 +#define QAM_SL_COMM_MB_OBS__B 1 +#define QAM_SL_COMM_MB_OBS__W 1 +#define QAM_SL_COMM_MB_OBS__M 0x2 +#define QAM_SL_COMM_MB_OBS__PRE 0x0 +#define QAM_SL_COMM_MB_OBS_OFF 0x0 +#define QAM_SL_COMM_MB_OBS_ON 0x2 +#define QAM_SL_COMM_MB_MUX_OBS__B 2 +#define QAM_SL_COMM_MB_MUX_OBS__W 2 +#define QAM_SL_COMM_MB_MUX_OBS__M 0xC +#define QAM_SL_COMM_MB_MUX_OBS__PRE 0x0 +#define QAM_SL_COMM_MB_MUX_OBS_CONST_CORR 0x0 +#define QAM_SL_COMM_MB_MUX_OBS_CONST2LC_O 0x4 +#define QAM_SL_COMM_MB_MUX_OBS_CONST2DQ_O 0x8 +#define QAM_SL_COMM_MB_MUX_OBS_VDEC_O 0xC + +#define QAM_SL_COMM_INT_REQ__A 0x1430003 +#define QAM_SL_COMM_INT_REQ__W 1 +#define QAM_SL_COMM_INT_REQ__M 0x1 +#define QAM_SL_COMM_INT_REQ__PRE 0x0 +#define QAM_SL_COMM_INT_STA__A 0x1430005 +#define QAM_SL_COMM_INT_STA__W 2 +#define QAM_SL_COMM_INT_STA__M 0x3 +#define QAM_SL_COMM_INT_STA__PRE 0x0 + +#define QAM_SL_COMM_INT_STA_MED_ERR_INT__B 0 +#define QAM_SL_COMM_INT_STA_MED_ERR_INT__W 1 +#define QAM_SL_COMM_INT_STA_MED_ERR_INT__M 0x1 +#define QAM_SL_COMM_INT_STA_MED_ERR_INT__PRE 0x0 + +#define QAM_SL_COMM_INT_STA_MER_INT__B 1 +#define QAM_SL_COMM_INT_STA_MER_INT__W 1 +#define QAM_SL_COMM_INT_STA_MER_INT__M 0x2 +#define QAM_SL_COMM_INT_STA_MER_INT__PRE 0x0 + +#define QAM_SL_COMM_INT_MSK__A 0x1430006 +#define QAM_SL_COMM_INT_MSK__W 2 +#define QAM_SL_COMM_INT_MSK__M 0x3 +#define QAM_SL_COMM_INT_MSK__PRE 0x0 +#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__B 0 +#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__W 1 +#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__M 0x1 +#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__PRE 0x0 +#define QAM_SL_COMM_INT_MSK_MER_MSK__B 1 +#define QAM_SL_COMM_INT_MSK_MER_MSK__W 1 +#define QAM_SL_COMM_INT_MSK_MER_MSK__M 0x2 +#define QAM_SL_COMM_INT_MSK_MER_MSK__PRE 0x0 + +#define QAM_SL_COMM_INT_STM__A 0x1430007 +#define QAM_SL_COMM_INT_STM__W 2 +#define QAM_SL_COMM_INT_STM__M 0x3 +#define QAM_SL_COMM_INT_STM__PRE 0x0 +#define QAM_SL_COMM_INT_STM_MED_ERR_STM__B 0 +#define QAM_SL_COMM_INT_STM_MED_ERR_STM__W 1 +#define QAM_SL_COMM_INT_STM_MED_ERR_STM__M 0x1 +#define QAM_SL_COMM_INT_STM_MED_ERR_STM__PRE 0x0 +#define QAM_SL_COMM_INT_STM_MER_STM__B 1 +#define QAM_SL_COMM_INT_STM_MER_STM__W 1 +#define QAM_SL_COMM_INT_STM_MER_STM__M 0x2 +#define QAM_SL_COMM_INT_STM_MER_STM__PRE 0x0 + +#define QAM_SL_MODE__A 0x1430010 +#define QAM_SL_MODE__W 11 +#define QAM_SL_MODE__M 0x7FF +#define QAM_SL_MODE__PRE 0x0 + +#define QAM_SL_MODE_SLICER4LC__B 0 +#define QAM_SL_MODE_SLICER4LC__W 2 +#define QAM_SL_MODE_SLICER4LC__M 0x3 +#define QAM_SL_MODE_SLICER4LC__PRE 0x0 +#define QAM_SL_MODE_SLICER4LC_RECT 0x0 +#define QAM_SL_MODE_SLICER4LC_ONET 0x1 +#define QAM_SL_MODE_SLICER4LC_RAD 0x2 + +#define QAM_SL_MODE_SLICER4DQ__B 2 +#define QAM_SL_MODE_SLICER4DQ__W 2 +#define QAM_SL_MODE_SLICER4DQ__M 0xC +#define QAM_SL_MODE_SLICER4DQ__PRE 0x0 +#define QAM_SL_MODE_SLICER4DQ_RECT 0x0 +#define QAM_SL_MODE_SLICER4DQ_ONET 0x4 +#define QAM_SL_MODE_SLICER4DQ_RAD 0x8 + +#define QAM_SL_MODE_SLICER4VD__B 4 +#define QAM_SL_MODE_SLICER4VD__W 2 +#define QAM_SL_MODE_SLICER4VD__M 0x30 +#define QAM_SL_MODE_SLICER4VD__PRE 0x0 +#define QAM_SL_MODE_SLICER4VD_RECT 0x0 +#define QAM_SL_MODE_SLICER4VD_ONET 0x10 +#define QAM_SL_MODE_SLICER4VD_RAD 0x20 + +#define QAM_SL_MODE_ROT_DIS__B 6 +#define QAM_SL_MODE_ROT_DIS__W 1 +#define QAM_SL_MODE_ROT_DIS__M 0x40 +#define QAM_SL_MODE_ROT_DIS__PRE 0x0 + +#define QAM_SL_MODE_DQROT_DIS__B 7 +#define QAM_SL_MODE_DQROT_DIS__W 1 +#define QAM_SL_MODE_DQROT_DIS__M 0x80 +#define QAM_SL_MODE_DQROT_DIS__PRE 0x0 + +#define QAM_SL_MODE_DFE_DIS__B 8 +#define QAM_SL_MODE_DFE_DIS__W 1 +#define QAM_SL_MODE_DFE_DIS__M 0x100 +#define QAM_SL_MODE_DFE_DIS__PRE 0x0 + +#define QAM_SL_MODE_RADIUS_MIX__B 9 +#define QAM_SL_MODE_RADIUS_MIX__W 1 +#define QAM_SL_MODE_RADIUS_MIX__M 0x200 +#define QAM_SL_MODE_RADIUS_MIX__PRE 0x0 + +#define QAM_SL_MODE_TILT_COMP__B 10 +#define QAM_SL_MODE_TILT_COMP__W 1 +#define QAM_SL_MODE_TILT_COMP__M 0x400 +#define QAM_SL_MODE_TILT_COMP__PRE 0x0 + +#define QAM_SL_K_FACTOR__A 0x1430011 +#define QAM_SL_K_FACTOR__W 4 +#define QAM_SL_K_FACTOR__M 0xF +#define QAM_SL_K_FACTOR__PRE 0x0 +#define QAM_SL_MEDIAN__A 0x1430012 +#define QAM_SL_MEDIAN__W 14 +#define QAM_SL_MEDIAN__M 0x3FFF +#define QAM_SL_MEDIAN__PRE 0x0 + +#define QAM_SL_MEDIAN_LENGTH__B 0 +#define QAM_SL_MEDIAN_LENGTH__W 2 +#define QAM_SL_MEDIAN_LENGTH__M 0x3 +#define QAM_SL_MEDIAN_LENGTH__PRE 0x0 + +#define QAM_SL_MEDIAN_CORRECT__B 2 +#define QAM_SL_MEDIAN_CORRECT__W 4 +#define QAM_SL_MEDIAN_CORRECT__M 0x3C +#define QAM_SL_MEDIAN_CORRECT__PRE 0x0 + +#define QAM_SL_MEDIAN_TOLERANCE__B 6 +#define QAM_SL_MEDIAN_TOLERANCE__W 7 +#define QAM_SL_MEDIAN_TOLERANCE__M 0x1FC0 +#define QAM_SL_MEDIAN_TOLERANCE__PRE 0x0 + +#define QAM_SL_MEDIAN_FAST__B 13 +#define QAM_SL_MEDIAN_FAST__W 1 +#define QAM_SL_MEDIAN_FAST__M 0x2000 +#define QAM_SL_MEDIAN_FAST__PRE 0x0 + +#define QAM_SL_ALPHA__A 0x1430013 +#define QAM_SL_ALPHA__W 3 +#define QAM_SL_ALPHA__M 0x7 +#define QAM_SL_ALPHA__PRE 0x0 + +#define QAM_SL_PHASELIMIT__A 0x1430014 +#define QAM_SL_PHASELIMIT__W 9 +#define QAM_SL_PHASELIMIT__M 0x1FF +#define QAM_SL_PHASELIMIT__PRE 0x0 +#define QAM_SL_MTA_LENGTH__A 0x1430015 +#define QAM_SL_MTA_LENGTH__W 2 +#define QAM_SL_MTA_LENGTH__M 0x3 +#define QAM_SL_MTA_LENGTH__PRE 0x1 + +#define QAM_SL_MTA_LENGTH_LENGTH__B 0 +#define QAM_SL_MTA_LENGTH_LENGTH__W 2 +#define QAM_SL_MTA_LENGTH_LENGTH__M 0x3 +#define QAM_SL_MTA_LENGTH_LENGTH__PRE 0x1 + +#define QAM_SL_MEDIAN_ERROR__A 0x1430016 +#define QAM_SL_MEDIAN_ERROR__W 10 +#define QAM_SL_MEDIAN_ERROR__M 0x3FF +#define QAM_SL_MEDIAN_ERROR__PRE 0x0 + +#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__B 0 +#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__W 10 +#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__M 0x3FF +#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__PRE 0x0 + +#define QAM_SL_ERR_POWER__A 0x1430017 +#define QAM_SL_ERR_POWER__W 16 +#define QAM_SL_ERR_POWER__M 0xFFFF +#define QAM_SL_ERR_POWER__PRE 0x0 + +#define QAM_DQ_COMM_EXEC__A 0x1440000 +#define QAM_DQ_COMM_EXEC__W 2 +#define QAM_DQ_COMM_EXEC__M 0x3 +#define QAM_DQ_COMM_EXEC__PRE 0x0 +#define QAM_DQ_COMM_EXEC_STOP 0x0 +#define QAM_DQ_COMM_EXEC_ACTIVE 0x1 +#define QAM_DQ_COMM_EXEC_HOLD 0x2 + +#define QAM_DQ_MODE__A 0x1440010 +#define QAM_DQ_MODE__W 5 +#define QAM_DQ_MODE__M 0x1F +#define QAM_DQ_MODE__PRE 0x0 + +#define QAM_DQ_MODE_TAPRESET__B 0 +#define QAM_DQ_MODE_TAPRESET__W 1 +#define QAM_DQ_MODE_TAPRESET__M 0x1 +#define QAM_DQ_MODE_TAPRESET__PRE 0x0 +#define QAM_DQ_MODE_TAPRESET_RST 0x1 + +#define QAM_DQ_MODE_TAPLMS__B 1 +#define QAM_DQ_MODE_TAPLMS__W 1 +#define QAM_DQ_MODE_TAPLMS__M 0x2 +#define QAM_DQ_MODE_TAPLMS__PRE 0x0 +#define QAM_DQ_MODE_TAPLMS_UPD 0x2 + +#define QAM_DQ_MODE_TAPDRAIN__B 2 +#define QAM_DQ_MODE_TAPDRAIN__W 1 +#define QAM_DQ_MODE_TAPDRAIN__M 0x4 +#define QAM_DQ_MODE_TAPDRAIN__PRE 0x0 +#define QAM_DQ_MODE_TAPDRAIN_DRAIN 0x4 + +#define QAM_DQ_MODE_FB__B 3 +#define QAM_DQ_MODE_FB__W 2 +#define QAM_DQ_MODE_FB__M 0x18 +#define QAM_DQ_MODE_FB__PRE 0x0 +#define QAM_DQ_MODE_FB_CMA 0x0 +#define QAM_DQ_MODE_FB_RADIUS 0x8 +#define QAM_DQ_MODE_FB_DFB 0x10 +#define QAM_DQ_MODE_FB_TRELLIS 0x18 + +#define QAM_DQ_MU_FACTOR__A 0x1440011 +#define QAM_DQ_MU_FACTOR__W 3 +#define QAM_DQ_MU_FACTOR__M 0x7 +#define QAM_DQ_MU_FACTOR__PRE 0x0 + +#define QAM_DQ_LA_FACTOR__A 0x1440012 +#define QAM_DQ_LA_FACTOR__W 4 +#define QAM_DQ_LA_FACTOR__M 0xF +#define QAM_DQ_LA_FACTOR__PRE 0xC + +#define QAM_DQ_CMA_RATIO__A 0x1440013 +#define QAM_DQ_CMA_RATIO__W 14 +#define QAM_DQ_CMA_RATIO__M 0x3FFF +#define QAM_DQ_CMA_RATIO__PRE 0x3CF9 +#define QAM_DQ_CMA_RATIO_QPSK 0x2000 +#define QAM_DQ_CMA_RATIO_QAM16 0x34CD +#define QAM_DQ_CMA_RATIO_QAM64 0x3A00 +#define QAM_DQ_CMA_RATIO_QAM256 0x3B4D +#define QAM_DQ_CMA_RATIO_QAM1024 0x3BA0 + +#define QAM_DQ_QUAL_RADSEL__A 0x1440014 +#define QAM_DQ_QUAL_RADSEL__W 3 +#define QAM_DQ_QUAL_RADSEL__M 0x7 +#define QAM_DQ_QUAL_RADSEL__PRE 0x0 + +#define QAM_DQ_QUAL_RADSEL_BIT__B 0 +#define QAM_DQ_QUAL_RADSEL_BIT__W 3 +#define QAM_DQ_QUAL_RADSEL_BIT__M 0x7 +#define QAM_DQ_QUAL_RADSEL_BIT__PRE 0x0 +#define QAM_DQ_QUAL_RADSEL_BIT_PURE_RADIUS 0x0 +#define QAM_DQ_QUAL_RADSEL_BIT_PURE_CMA 0x6 + +#define QAM_DQ_QUAL_ENA__A 0x1440015 +#define QAM_DQ_QUAL_ENA__W 1 +#define QAM_DQ_QUAL_ENA__M 0x1 +#define QAM_DQ_QUAL_ENA__PRE 0x0 + +#define QAM_DQ_QUAL_ENA_ENA__B 0 +#define QAM_DQ_QUAL_ENA_ENA__W 1 +#define QAM_DQ_QUAL_ENA_ENA__M 0x1 +#define QAM_DQ_QUAL_ENA_ENA__PRE 0x0 +#define QAM_DQ_QUAL_ENA_ENA_QUAL_WEIGHTING 0x1 + +#define QAM_DQ_QUAL_FUN0__A 0x1440018 +#define QAM_DQ_QUAL_FUN0__W 6 +#define QAM_DQ_QUAL_FUN0__M 0x3F +#define QAM_DQ_QUAL_FUN0__PRE 0x4 + +#define QAM_DQ_QUAL_FUN0_BIT__B 0 +#define QAM_DQ_QUAL_FUN0_BIT__W 6 +#define QAM_DQ_QUAL_FUN0_BIT__M 0x3F +#define QAM_DQ_QUAL_FUN0_BIT__PRE 0x4 + +#define QAM_DQ_QUAL_FUN1__A 0x1440019 +#define QAM_DQ_QUAL_FUN1__W 6 +#define QAM_DQ_QUAL_FUN1__M 0x3F +#define QAM_DQ_QUAL_FUN1__PRE 0x4 + +#define QAM_DQ_QUAL_FUN1_BIT__B 0 +#define QAM_DQ_QUAL_FUN1_BIT__W 6 +#define QAM_DQ_QUAL_FUN1_BIT__M 0x3F +#define QAM_DQ_QUAL_FUN1_BIT__PRE 0x4 + +#define QAM_DQ_QUAL_FUN2__A 0x144001A +#define QAM_DQ_QUAL_FUN2__W 6 +#define QAM_DQ_QUAL_FUN2__M 0x3F +#define QAM_DQ_QUAL_FUN2__PRE 0x4 + +#define QAM_DQ_QUAL_FUN2_BIT__B 0 +#define QAM_DQ_QUAL_FUN2_BIT__W 6 +#define QAM_DQ_QUAL_FUN2_BIT__M 0x3F +#define QAM_DQ_QUAL_FUN2_BIT__PRE 0x4 + +#define QAM_DQ_QUAL_FUN3__A 0x144001B +#define QAM_DQ_QUAL_FUN3__W 6 +#define QAM_DQ_QUAL_FUN3__M 0x3F +#define QAM_DQ_QUAL_FUN3__PRE 0x4 + +#define QAM_DQ_QUAL_FUN3_BIT__B 0 +#define QAM_DQ_QUAL_FUN3_BIT__W 6 +#define QAM_DQ_QUAL_FUN3_BIT__M 0x3F +#define QAM_DQ_QUAL_FUN3_BIT__PRE 0x4 + +#define QAM_DQ_QUAL_FUN4__A 0x144001C +#define QAM_DQ_QUAL_FUN4__W 6 +#define QAM_DQ_QUAL_FUN4__M 0x3F +#define QAM_DQ_QUAL_FUN4__PRE 0x6 + +#define QAM_DQ_QUAL_FUN4_BIT__B 0 +#define QAM_DQ_QUAL_FUN4_BIT__W 6 +#define QAM_DQ_QUAL_FUN4_BIT__M 0x3F +#define QAM_DQ_QUAL_FUN4_BIT__PRE 0x6 + +#define QAM_DQ_QUAL_FUN5__A 0x144001D +#define QAM_DQ_QUAL_FUN5__W 6 +#define QAM_DQ_QUAL_FUN5__M 0x3F +#define QAM_DQ_QUAL_FUN5__PRE 0x6 + +#define QAM_DQ_QUAL_FUN5_BIT__B 0 +#define QAM_DQ_QUAL_FUN5_BIT__W 6 +#define QAM_DQ_QUAL_FUN5_BIT__M 0x3F +#define QAM_DQ_QUAL_FUN5_BIT__PRE 0x6 + +#define QAM_DQ_RAW_LIM__A 0x144001E +#define QAM_DQ_RAW_LIM__W 5 +#define QAM_DQ_RAW_LIM__M 0x1F +#define QAM_DQ_RAW_LIM__PRE 0x1F + +#define QAM_DQ_RAW_LIM_BIT__B 0 +#define QAM_DQ_RAW_LIM_BIT__W 5 +#define QAM_DQ_RAW_LIM_BIT__M 0x1F +#define QAM_DQ_RAW_LIM_BIT__PRE 0x1F + +#define QAM_DQ_TAP_RE_EL0__A 0x1440020 +#define QAM_DQ_TAP_RE_EL0__W 12 +#define QAM_DQ_TAP_RE_EL0__M 0xFFF +#define QAM_DQ_TAP_RE_EL0__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL0_TAP__B 0 +#define QAM_DQ_TAP_RE_EL0_TAP__W 12 +#define QAM_DQ_TAP_RE_EL0_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL0_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL0__A 0x1440021 +#define QAM_DQ_TAP_IM_EL0__W 12 +#define QAM_DQ_TAP_IM_EL0__M 0xFFF +#define QAM_DQ_TAP_IM_EL0__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL0_TAP__B 0 +#define QAM_DQ_TAP_IM_EL0_TAP__W 12 +#define QAM_DQ_TAP_IM_EL0_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL0_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL1__A 0x1440022 +#define QAM_DQ_TAP_RE_EL1__W 12 +#define QAM_DQ_TAP_RE_EL1__M 0xFFF +#define QAM_DQ_TAP_RE_EL1__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL1_TAP__B 0 +#define QAM_DQ_TAP_RE_EL1_TAP__W 12 +#define QAM_DQ_TAP_RE_EL1_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL1_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL1__A 0x1440023 +#define QAM_DQ_TAP_IM_EL1__W 12 +#define QAM_DQ_TAP_IM_EL1__M 0xFFF +#define QAM_DQ_TAP_IM_EL1__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL1_TAP__B 0 +#define QAM_DQ_TAP_IM_EL1_TAP__W 12 +#define QAM_DQ_TAP_IM_EL1_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL1_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL2__A 0x1440024 +#define QAM_DQ_TAP_RE_EL2__W 12 +#define QAM_DQ_TAP_RE_EL2__M 0xFFF +#define QAM_DQ_TAP_RE_EL2__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL2_TAP__B 0 +#define QAM_DQ_TAP_RE_EL2_TAP__W 12 +#define QAM_DQ_TAP_RE_EL2_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL2_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL2__A 0x1440025 +#define QAM_DQ_TAP_IM_EL2__W 12 +#define QAM_DQ_TAP_IM_EL2__M 0xFFF +#define QAM_DQ_TAP_IM_EL2__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL2_TAP__B 0 +#define QAM_DQ_TAP_IM_EL2_TAP__W 12 +#define QAM_DQ_TAP_IM_EL2_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL2_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL3__A 0x1440026 +#define QAM_DQ_TAP_RE_EL3__W 12 +#define QAM_DQ_TAP_RE_EL3__M 0xFFF +#define QAM_DQ_TAP_RE_EL3__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL3_TAP__B 0 +#define QAM_DQ_TAP_RE_EL3_TAP__W 12 +#define QAM_DQ_TAP_RE_EL3_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL3_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL3__A 0x1440027 +#define QAM_DQ_TAP_IM_EL3__W 12 +#define QAM_DQ_TAP_IM_EL3__M 0xFFF +#define QAM_DQ_TAP_IM_EL3__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL3_TAP__B 0 +#define QAM_DQ_TAP_IM_EL3_TAP__W 12 +#define QAM_DQ_TAP_IM_EL3_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL3_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL4__A 0x1440028 +#define QAM_DQ_TAP_RE_EL4__W 12 +#define QAM_DQ_TAP_RE_EL4__M 0xFFF +#define QAM_DQ_TAP_RE_EL4__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL4_TAP__B 0 +#define QAM_DQ_TAP_RE_EL4_TAP__W 12 +#define QAM_DQ_TAP_RE_EL4_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL4_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL4__A 0x1440029 +#define QAM_DQ_TAP_IM_EL4__W 12 +#define QAM_DQ_TAP_IM_EL4__M 0xFFF +#define QAM_DQ_TAP_IM_EL4__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL4_TAP__B 0 +#define QAM_DQ_TAP_IM_EL4_TAP__W 12 +#define QAM_DQ_TAP_IM_EL4_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL4_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL5__A 0x144002A +#define QAM_DQ_TAP_RE_EL5__W 12 +#define QAM_DQ_TAP_RE_EL5__M 0xFFF +#define QAM_DQ_TAP_RE_EL5__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL5_TAP__B 0 +#define QAM_DQ_TAP_RE_EL5_TAP__W 12 +#define QAM_DQ_TAP_RE_EL5_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL5_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL5__A 0x144002B +#define QAM_DQ_TAP_IM_EL5__W 12 +#define QAM_DQ_TAP_IM_EL5__M 0xFFF +#define QAM_DQ_TAP_IM_EL5__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL5_TAP__B 0 +#define QAM_DQ_TAP_IM_EL5_TAP__W 12 +#define QAM_DQ_TAP_IM_EL5_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL5_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL6__A 0x144002C +#define QAM_DQ_TAP_RE_EL6__W 12 +#define QAM_DQ_TAP_RE_EL6__M 0xFFF +#define QAM_DQ_TAP_RE_EL6__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL6_TAP__B 0 +#define QAM_DQ_TAP_RE_EL6_TAP__W 12 +#define QAM_DQ_TAP_RE_EL6_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL6_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL6__A 0x144002D +#define QAM_DQ_TAP_IM_EL6__W 12 +#define QAM_DQ_TAP_IM_EL6__M 0xFFF +#define QAM_DQ_TAP_IM_EL6__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL6_TAP__B 0 +#define QAM_DQ_TAP_IM_EL6_TAP__W 12 +#define QAM_DQ_TAP_IM_EL6_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL6_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL7__A 0x144002E +#define QAM_DQ_TAP_RE_EL7__W 12 +#define QAM_DQ_TAP_RE_EL7__M 0xFFF +#define QAM_DQ_TAP_RE_EL7__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL7_TAP__B 0 +#define QAM_DQ_TAP_RE_EL7_TAP__W 12 +#define QAM_DQ_TAP_RE_EL7_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL7_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL7__A 0x144002F +#define QAM_DQ_TAP_IM_EL7__W 12 +#define QAM_DQ_TAP_IM_EL7__M 0xFFF +#define QAM_DQ_TAP_IM_EL7__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL7_TAP__B 0 +#define QAM_DQ_TAP_IM_EL7_TAP__W 12 +#define QAM_DQ_TAP_IM_EL7_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL7_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL8__A 0x1440030 +#define QAM_DQ_TAP_RE_EL8__W 12 +#define QAM_DQ_TAP_RE_EL8__M 0xFFF +#define QAM_DQ_TAP_RE_EL8__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL8_TAP__B 0 +#define QAM_DQ_TAP_RE_EL8_TAP__W 12 +#define QAM_DQ_TAP_RE_EL8_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL8_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL8__A 0x1440031 +#define QAM_DQ_TAP_IM_EL8__W 12 +#define QAM_DQ_TAP_IM_EL8__M 0xFFF +#define QAM_DQ_TAP_IM_EL8__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL8_TAP__B 0 +#define QAM_DQ_TAP_IM_EL8_TAP__W 12 +#define QAM_DQ_TAP_IM_EL8_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL8_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL9__A 0x1440032 +#define QAM_DQ_TAP_RE_EL9__W 12 +#define QAM_DQ_TAP_RE_EL9__M 0xFFF +#define QAM_DQ_TAP_RE_EL9__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL9_TAP__B 0 +#define QAM_DQ_TAP_RE_EL9_TAP__W 12 +#define QAM_DQ_TAP_RE_EL9_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL9_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL9__A 0x1440033 +#define QAM_DQ_TAP_IM_EL9__W 12 +#define QAM_DQ_TAP_IM_EL9__M 0xFFF +#define QAM_DQ_TAP_IM_EL9__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL9_TAP__B 0 +#define QAM_DQ_TAP_IM_EL9_TAP__W 12 +#define QAM_DQ_TAP_IM_EL9_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL9_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL10__A 0x1440034 +#define QAM_DQ_TAP_RE_EL10__W 12 +#define QAM_DQ_TAP_RE_EL10__M 0xFFF +#define QAM_DQ_TAP_RE_EL10__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL10_TAP__B 0 +#define QAM_DQ_TAP_RE_EL10_TAP__W 12 +#define QAM_DQ_TAP_RE_EL10_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL10_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL10__A 0x1440035 +#define QAM_DQ_TAP_IM_EL10__W 12 +#define QAM_DQ_TAP_IM_EL10__M 0xFFF +#define QAM_DQ_TAP_IM_EL10__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL10_TAP__B 0 +#define QAM_DQ_TAP_IM_EL10_TAP__W 12 +#define QAM_DQ_TAP_IM_EL10_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL10_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL11__A 0x1440036 +#define QAM_DQ_TAP_RE_EL11__W 12 +#define QAM_DQ_TAP_RE_EL11__M 0xFFF +#define QAM_DQ_TAP_RE_EL11__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL11_TAP__B 0 +#define QAM_DQ_TAP_RE_EL11_TAP__W 12 +#define QAM_DQ_TAP_RE_EL11_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL11_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL11__A 0x1440037 +#define QAM_DQ_TAP_IM_EL11__W 12 +#define QAM_DQ_TAP_IM_EL11__M 0xFFF +#define QAM_DQ_TAP_IM_EL11__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL11_TAP__B 0 +#define QAM_DQ_TAP_IM_EL11_TAP__W 12 +#define QAM_DQ_TAP_IM_EL11_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL11_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL12__A 0x1440038 +#define QAM_DQ_TAP_RE_EL12__W 12 +#define QAM_DQ_TAP_RE_EL12__M 0xFFF +#define QAM_DQ_TAP_RE_EL12__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL12_TAP__B 0 +#define QAM_DQ_TAP_RE_EL12_TAP__W 12 +#define QAM_DQ_TAP_RE_EL12_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL12_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL12__A 0x1440039 +#define QAM_DQ_TAP_IM_EL12__W 12 +#define QAM_DQ_TAP_IM_EL12__M 0xFFF +#define QAM_DQ_TAP_IM_EL12__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL12_TAP__B 0 +#define QAM_DQ_TAP_IM_EL12_TAP__W 12 +#define QAM_DQ_TAP_IM_EL12_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL12_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL13__A 0x144003A +#define QAM_DQ_TAP_RE_EL13__W 12 +#define QAM_DQ_TAP_RE_EL13__M 0xFFF +#define QAM_DQ_TAP_RE_EL13__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL13_TAP__B 0 +#define QAM_DQ_TAP_RE_EL13_TAP__W 12 +#define QAM_DQ_TAP_RE_EL13_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL13_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL13__A 0x144003B +#define QAM_DQ_TAP_IM_EL13__W 12 +#define QAM_DQ_TAP_IM_EL13__M 0xFFF +#define QAM_DQ_TAP_IM_EL13__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL13_TAP__B 0 +#define QAM_DQ_TAP_IM_EL13_TAP__W 12 +#define QAM_DQ_TAP_IM_EL13_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL13_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL14__A 0x144003C +#define QAM_DQ_TAP_RE_EL14__W 12 +#define QAM_DQ_TAP_RE_EL14__M 0xFFF +#define QAM_DQ_TAP_RE_EL14__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL14_TAP__B 0 +#define QAM_DQ_TAP_RE_EL14_TAP__W 12 +#define QAM_DQ_TAP_RE_EL14_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL14_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL14__A 0x144003D +#define QAM_DQ_TAP_IM_EL14__W 12 +#define QAM_DQ_TAP_IM_EL14__M 0xFFF +#define QAM_DQ_TAP_IM_EL14__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL14_TAP__B 0 +#define QAM_DQ_TAP_IM_EL14_TAP__W 12 +#define QAM_DQ_TAP_IM_EL14_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL14_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL15__A 0x144003E +#define QAM_DQ_TAP_RE_EL15__W 12 +#define QAM_DQ_TAP_RE_EL15__M 0xFFF +#define QAM_DQ_TAP_RE_EL15__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL15_TAP__B 0 +#define QAM_DQ_TAP_RE_EL15_TAP__W 12 +#define QAM_DQ_TAP_RE_EL15_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL15_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL15__A 0x144003F +#define QAM_DQ_TAP_IM_EL15__W 12 +#define QAM_DQ_TAP_IM_EL15__M 0xFFF +#define QAM_DQ_TAP_IM_EL15__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL15_TAP__B 0 +#define QAM_DQ_TAP_IM_EL15_TAP__W 12 +#define QAM_DQ_TAP_IM_EL15_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL15_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL16__A 0x1440040 +#define QAM_DQ_TAP_RE_EL16__W 12 +#define QAM_DQ_TAP_RE_EL16__M 0xFFF +#define QAM_DQ_TAP_RE_EL16__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL16_TAP__B 0 +#define QAM_DQ_TAP_RE_EL16_TAP__W 12 +#define QAM_DQ_TAP_RE_EL16_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL16_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL16__A 0x1440041 +#define QAM_DQ_TAP_IM_EL16__W 12 +#define QAM_DQ_TAP_IM_EL16__M 0xFFF +#define QAM_DQ_TAP_IM_EL16__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL16_TAP__B 0 +#define QAM_DQ_TAP_IM_EL16_TAP__W 12 +#define QAM_DQ_TAP_IM_EL16_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL16_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL17__A 0x1440042 +#define QAM_DQ_TAP_RE_EL17__W 12 +#define QAM_DQ_TAP_RE_EL17__M 0xFFF +#define QAM_DQ_TAP_RE_EL17__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL17_TAP__B 0 +#define QAM_DQ_TAP_RE_EL17_TAP__W 12 +#define QAM_DQ_TAP_RE_EL17_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL17_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL17__A 0x1440043 +#define QAM_DQ_TAP_IM_EL17__W 12 +#define QAM_DQ_TAP_IM_EL17__M 0xFFF +#define QAM_DQ_TAP_IM_EL17__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL17_TAP__B 0 +#define QAM_DQ_TAP_IM_EL17_TAP__W 12 +#define QAM_DQ_TAP_IM_EL17_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL17_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL18__A 0x1440044 +#define QAM_DQ_TAP_RE_EL18__W 12 +#define QAM_DQ_TAP_RE_EL18__M 0xFFF +#define QAM_DQ_TAP_RE_EL18__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL18_TAP__B 0 +#define QAM_DQ_TAP_RE_EL18_TAP__W 12 +#define QAM_DQ_TAP_RE_EL18_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL18_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL18__A 0x1440045 +#define QAM_DQ_TAP_IM_EL18__W 12 +#define QAM_DQ_TAP_IM_EL18__M 0xFFF +#define QAM_DQ_TAP_IM_EL18__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL18_TAP__B 0 +#define QAM_DQ_TAP_IM_EL18_TAP__W 12 +#define QAM_DQ_TAP_IM_EL18_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL18_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL19__A 0x1440046 +#define QAM_DQ_TAP_RE_EL19__W 12 +#define QAM_DQ_TAP_RE_EL19__M 0xFFF +#define QAM_DQ_TAP_RE_EL19__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL19_TAP__B 0 +#define QAM_DQ_TAP_RE_EL19_TAP__W 12 +#define QAM_DQ_TAP_RE_EL19_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL19_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL19__A 0x1440047 +#define QAM_DQ_TAP_IM_EL19__W 12 +#define QAM_DQ_TAP_IM_EL19__M 0xFFF +#define QAM_DQ_TAP_IM_EL19__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL19_TAP__B 0 +#define QAM_DQ_TAP_IM_EL19_TAP__W 12 +#define QAM_DQ_TAP_IM_EL19_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL19_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL20__A 0x1440048 +#define QAM_DQ_TAP_RE_EL20__W 12 +#define QAM_DQ_TAP_RE_EL20__M 0xFFF +#define QAM_DQ_TAP_RE_EL20__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL20_TAP__B 0 +#define QAM_DQ_TAP_RE_EL20_TAP__W 12 +#define QAM_DQ_TAP_RE_EL20_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL20_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL20__A 0x1440049 +#define QAM_DQ_TAP_IM_EL20__W 12 +#define QAM_DQ_TAP_IM_EL20__M 0xFFF +#define QAM_DQ_TAP_IM_EL20__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL20_TAP__B 0 +#define QAM_DQ_TAP_IM_EL20_TAP__W 12 +#define QAM_DQ_TAP_IM_EL20_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL20_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL21__A 0x144004A +#define QAM_DQ_TAP_RE_EL21__W 12 +#define QAM_DQ_TAP_RE_EL21__M 0xFFF +#define QAM_DQ_TAP_RE_EL21__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL21_TAP__B 0 +#define QAM_DQ_TAP_RE_EL21_TAP__W 12 +#define QAM_DQ_TAP_RE_EL21_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL21_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL21__A 0x144004B +#define QAM_DQ_TAP_IM_EL21__W 12 +#define QAM_DQ_TAP_IM_EL21__M 0xFFF +#define QAM_DQ_TAP_IM_EL21__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL21_TAP__B 0 +#define QAM_DQ_TAP_IM_EL21_TAP__W 12 +#define QAM_DQ_TAP_IM_EL21_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL21_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL22__A 0x144004C +#define QAM_DQ_TAP_RE_EL22__W 12 +#define QAM_DQ_TAP_RE_EL22__M 0xFFF +#define QAM_DQ_TAP_RE_EL22__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL22_TAP__B 0 +#define QAM_DQ_TAP_RE_EL22_TAP__W 12 +#define QAM_DQ_TAP_RE_EL22_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL22_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL22__A 0x144004D +#define QAM_DQ_TAP_IM_EL22__W 12 +#define QAM_DQ_TAP_IM_EL22__M 0xFFF +#define QAM_DQ_TAP_IM_EL22__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL22_TAP__B 0 +#define QAM_DQ_TAP_IM_EL22_TAP__W 12 +#define QAM_DQ_TAP_IM_EL22_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL22_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL23__A 0x144004E +#define QAM_DQ_TAP_RE_EL23__W 12 +#define QAM_DQ_TAP_RE_EL23__M 0xFFF +#define QAM_DQ_TAP_RE_EL23__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL23_TAP__B 0 +#define QAM_DQ_TAP_RE_EL23_TAP__W 12 +#define QAM_DQ_TAP_RE_EL23_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL23_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL23__A 0x144004F +#define QAM_DQ_TAP_IM_EL23__W 12 +#define QAM_DQ_TAP_IM_EL23__M 0xFFF +#define QAM_DQ_TAP_IM_EL23__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL23_TAP__B 0 +#define QAM_DQ_TAP_IM_EL23_TAP__W 12 +#define QAM_DQ_TAP_IM_EL23_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL23_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL24__A 0x1440050 +#define QAM_DQ_TAP_RE_EL24__W 12 +#define QAM_DQ_TAP_RE_EL24__M 0xFFF +#define QAM_DQ_TAP_RE_EL24__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL24_TAP__B 0 +#define QAM_DQ_TAP_RE_EL24_TAP__W 12 +#define QAM_DQ_TAP_RE_EL24_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL24_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL24__A 0x1440051 +#define QAM_DQ_TAP_IM_EL24__W 12 +#define QAM_DQ_TAP_IM_EL24__M 0xFFF +#define QAM_DQ_TAP_IM_EL24__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL24_TAP__B 0 +#define QAM_DQ_TAP_IM_EL24_TAP__W 12 +#define QAM_DQ_TAP_IM_EL24_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL24_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL25__A 0x1440052 +#define QAM_DQ_TAP_RE_EL25__W 12 +#define QAM_DQ_TAP_RE_EL25__M 0xFFF +#define QAM_DQ_TAP_RE_EL25__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL25_TAP__B 0 +#define QAM_DQ_TAP_RE_EL25_TAP__W 12 +#define QAM_DQ_TAP_RE_EL25_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL25_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL25__A 0x1440053 +#define QAM_DQ_TAP_IM_EL25__W 12 +#define QAM_DQ_TAP_IM_EL25__M 0xFFF +#define QAM_DQ_TAP_IM_EL25__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL25_TAP__B 0 +#define QAM_DQ_TAP_IM_EL25_TAP__W 12 +#define QAM_DQ_TAP_IM_EL25_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL25_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL26__A 0x1440054 +#define QAM_DQ_TAP_RE_EL26__W 12 +#define QAM_DQ_TAP_RE_EL26__M 0xFFF +#define QAM_DQ_TAP_RE_EL26__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL26_TAP__B 0 +#define QAM_DQ_TAP_RE_EL26_TAP__W 12 +#define QAM_DQ_TAP_RE_EL26_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL26_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL26__A 0x1440055 +#define QAM_DQ_TAP_IM_EL26__W 12 +#define QAM_DQ_TAP_IM_EL26__M 0xFFF +#define QAM_DQ_TAP_IM_EL26__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL26_TAP__B 0 +#define QAM_DQ_TAP_IM_EL26_TAP__W 12 +#define QAM_DQ_TAP_IM_EL26_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL26_TAP__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL27__A 0x1440056 +#define QAM_DQ_TAP_RE_EL27__W 12 +#define QAM_DQ_TAP_RE_EL27__M 0xFFF +#define QAM_DQ_TAP_RE_EL27__PRE 0x2 + +#define QAM_DQ_TAP_RE_EL27_TAP__B 0 +#define QAM_DQ_TAP_RE_EL27_TAP__W 12 +#define QAM_DQ_TAP_RE_EL27_TAP__M 0xFFF +#define QAM_DQ_TAP_RE_EL27_TAP__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL27__A 0x1440057 +#define QAM_DQ_TAP_IM_EL27__W 12 +#define QAM_DQ_TAP_IM_EL27__M 0xFFF +#define QAM_DQ_TAP_IM_EL27__PRE 0x2 + +#define QAM_DQ_TAP_IM_EL27_TAP__B 0 +#define QAM_DQ_TAP_IM_EL27_TAP__W 12 +#define QAM_DQ_TAP_IM_EL27_TAP__M 0xFFF +#define QAM_DQ_TAP_IM_EL27_TAP__PRE 0x2 + +#define QAM_LC_COMM_EXEC__A 0x1450000 +#define QAM_LC_COMM_EXEC__W 2 +#define QAM_LC_COMM_EXEC__M 0x3 +#define QAM_LC_COMM_EXEC__PRE 0x0 +#define QAM_LC_COMM_EXEC_STOP 0x0 +#define QAM_LC_COMM_EXEC_ACTIVE 0x1 +#define QAM_LC_COMM_EXEC_HOLD 0x2 + +#define QAM_LC_COMM_MB__A 0x1450002 +#define QAM_LC_COMM_MB__W 2 +#define QAM_LC_COMM_MB__M 0x3 +#define QAM_LC_COMM_MB__PRE 0x0 +#define QAM_LC_COMM_MB_CTL__B 0 +#define QAM_LC_COMM_MB_CTL__W 1 +#define QAM_LC_COMM_MB_CTL__M 0x1 +#define QAM_LC_COMM_MB_CTL__PRE 0x0 +#define QAM_LC_COMM_MB_CTL_OFF 0x0 +#define QAM_LC_COMM_MB_CTL_ON 0x1 +#define QAM_LC_COMM_MB_OBS__B 1 +#define QAM_LC_COMM_MB_OBS__W 1 +#define QAM_LC_COMM_MB_OBS__M 0x2 +#define QAM_LC_COMM_MB_OBS__PRE 0x0 +#define QAM_LC_COMM_MB_OBS_OFF 0x0 +#define QAM_LC_COMM_MB_OBS_ON 0x2 + +#define QAM_LC_COMM_INT_REQ__A 0x1450003 +#define QAM_LC_COMM_INT_REQ__W 1 +#define QAM_LC_COMM_INT_REQ__M 0x1 +#define QAM_LC_COMM_INT_REQ__PRE 0x0 +#define QAM_LC_COMM_INT_STA__A 0x1450005 +#define QAM_LC_COMM_INT_STA__W 3 +#define QAM_LC_COMM_INT_STA__M 0x7 +#define QAM_LC_COMM_INT_STA__PRE 0x0 + +#define QAM_LC_COMM_INT_STA_READY__B 0 +#define QAM_LC_COMM_INT_STA_READY__W 1 +#define QAM_LC_COMM_INT_STA_READY__M 0x1 +#define QAM_LC_COMM_INT_STA_READY__PRE 0x0 + +#define QAM_LC_COMM_INT_STA_OVERFLOW__B 1 +#define QAM_LC_COMM_INT_STA_OVERFLOW__W 1 +#define QAM_LC_COMM_INT_STA_OVERFLOW__M 0x2 +#define QAM_LC_COMM_INT_STA_OVERFLOW__PRE 0x0 + +#define QAM_LC_COMM_INT_STA_FREQ_WRAP__B 2 +#define QAM_LC_COMM_INT_STA_FREQ_WRAP__W 1 +#define QAM_LC_COMM_INT_STA_FREQ_WRAP__M 0x4 +#define QAM_LC_COMM_INT_STA_FREQ_WRAP__PRE 0x0 + +#define QAM_LC_COMM_INT_MSK__A 0x1450006 +#define QAM_LC_COMM_INT_MSK__W 3 +#define QAM_LC_COMM_INT_MSK__M 0x7 +#define QAM_LC_COMM_INT_MSK__PRE 0x0 +#define QAM_LC_COMM_INT_MSK_READY__B 0 +#define QAM_LC_COMM_INT_MSK_READY__W 1 +#define QAM_LC_COMM_INT_MSK_READY__M 0x1 +#define QAM_LC_COMM_INT_MSK_READY__PRE 0x0 +#define QAM_LC_COMM_INT_MSK_OVERFLOW__B 1 +#define QAM_LC_COMM_INT_MSK_OVERFLOW__W 1 +#define QAM_LC_COMM_INT_MSK_OVERFLOW__M 0x2 +#define QAM_LC_COMM_INT_MSK_OVERFLOW__PRE 0x0 +#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__B 2 +#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__W 1 +#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__M 0x4 +#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__PRE 0x0 + +#define QAM_LC_COMM_INT_STM__A 0x1450007 +#define QAM_LC_COMM_INT_STM__W 3 +#define QAM_LC_COMM_INT_STM__M 0x7 +#define QAM_LC_COMM_INT_STM__PRE 0x0 +#define QAM_LC_COMM_INT_STM_READY__B 0 +#define QAM_LC_COMM_INT_STM_READY__W 1 +#define QAM_LC_COMM_INT_STM_READY__M 0x1 +#define QAM_LC_COMM_INT_STM_READY__PRE 0x0 +#define QAM_LC_COMM_INT_STM_OVERFLOW__B 1 +#define QAM_LC_COMM_INT_STM_OVERFLOW__W 1 +#define QAM_LC_COMM_INT_STM_OVERFLOW__M 0x2 +#define QAM_LC_COMM_INT_STM_OVERFLOW__PRE 0x0 +#define QAM_LC_COMM_INT_STM_FREQ_WRAP__B 2 +#define QAM_LC_COMM_INT_STM_FREQ_WRAP__W 1 +#define QAM_LC_COMM_INT_STM_FREQ_WRAP__M 0x4 +#define QAM_LC_COMM_INT_STM_FREQ_WRAP__PRE 0x0 + +#define QAM_LC_MODE__A 0x1450010 +#define QAM_LC_MODE__W 3 +#define QAM_LC_MODE__M 0x7 +#define QAM_LC_MODE__PRE 0x7 + +#define QAM_LC_MODE_ENABLE_A__B 0 +#define QAM_LC_MODE_ENABLE_A__W 1 +#define QAM_LC_MODE_ENABLE_A__M 0x1 +#define QAM_LC_MODE_ENABLE_A__PRE 0x1 + +#define QAM_LC_MODE_ENABLE_F__B 1 +#define QAM_LC_MODE_ENABLE_F__W 1 +#define QAM_LC_MODE_ENABLE_F__M 0x2 +#define QAM_LC_MODE_ENABLE_F__PRE 0x2 + +#define QAM_LC_MODE_ENABLE_R__B 2 +#define QAM_LC_MODE_ENABLE_R__W 1 +#define QAM_LC_MODE_ENABLE_R__M 0x4 +#define QAM_LC_MODE_ENABLE_R__PRE 0x4 + +#define QAM_LC_CA__A 0x1450011 +#define QAM_LC_CA__W 6 +#define QAM_LC_CA__M 0x3F +#define QAM_LC_CA__PRE 0x28 + +#define QAM_LC_CA_COEF__B 0 +#define QAM_LC_CA_COEF__W 6 +#define QAM_LC_CA_COEF__M 0x3F +#define QAM_LC_CA_COEF__PRE 0x28 + +#define QAM_LC_CF__A 0x1450012 +#define QAM_LC_CF__W 8 +#define QAM_LC_CF__M 0xFF +#define QAM_LC_CF__PRE 0x8C + +#define QAM_LC_CF_COEF__B 0 +#define QAM_LC_CF_COEF__W 8 +#define QAM_LC_CF_COEF__M 0xFF +#define QAM_LC_CF_COEF__PRE 0x8C + +#define QAM_LC_CF1__A 0x1450013 +#define QAM_LC_CF1__W 8 +#define QAM_LC_CF1__M 0xFF +#define QAM_LC_CF1__PRE 0x1E + +#define QAM_LC_CF1_COEF__B 0 +#define QAM_LC_CF1_COEF__W 8 +#define QAM_LC_CF1_COEF__M 0xFF +#define QAM_LC_CF1_COEF__PRE 0x1E + +#define QAM_LC_CP__A 0x1450014 +#define QAM_LC_CP__W 8 +#define QAM_LC_CP__M 0xFF +#define QAM_LC_CP__PRE 0x78 + +#define QAM_LC_CP_COEF__B 0 +#define QAM_LC_CP_COEF__W 8 +#define QAM_LC_CP_COEF__M 0xFF +#define QAM_LC_CP_COEF__PRE 0x78 + +#define QAM_LC_CI__A 0x1450015 +#define QAM_LC_CI__W 8 +#define QAM_LC_CI__M 0xFF +#define QAM_LC_CI__PRE 0x46 + +#define QAM_LC_CI_COEF__B 0 +#define QAM_LC_CI_COEF__W 8 +#define QAM_LC_CI_COEF__M 0xFF +#define QAM_LC_CI_COEF__PRE 0x46 + +#define QAM_LC_EP__A 0x1450016 +#define QAM_LC_EP__W 6 +#define QAM_LC_EP__M 0x3F +#define QAM_LC_EP__PRE 0x0 + +#define QAM_LC_EP_COEF__B 0 +#define QAM_LC_EP_COEF__W 6 +#define QAM_LC_EP_COEF__M 0x3F +#define QAM_LC_EP_COEF__PRE 0x0 + +#define QAM_LC_EI__A 0x1450017 +#define QAM_LC_EI__W 6 +#define QAM_LC_EI__M 0x3F +#define QAM_LC_EI__PRE 0x0 + +#define QAM_LC_EI_COEF__B 0 +#define QAM_LC_EI_COEF__W 6 +#define QAM_LC_EI_COEF__M 0x3F +#define QAM_LC_EI_COEF__PRE 0x0 + +#define QAM_LC_QUAL_TAB0__A 0x1450018 +#define QAM_LC_QUAL_TAB0__W 5 +#define QAM_LC_QUAL_TAB0__M 0x1F +#define QAM_LC_QUAL_TAB0__PRE 0x1 + +#define QAM_LC_QUAL_TAB0_VALUE__B 0 +#define QAM_LC_QUAL_TAB0_VALUE__W 5 +#define QAM_LC_QUAL_TAB0_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB0_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB1__A 0x1450019 +#define QAM_LC_QUAL_TAB1__W 5 +#define QAM_LC_QUAL_TAB1__M 0x1F +#define QAM_LC_QUAL_TAB1__PRE 0x1 + +#define QAM_LC_QUAL_TAB1_VALUE__B 0 +#define QAM_LC_QUAL_TAB1_VALUE__W 5 +#define QAM_LC_QUAL_TAB1_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB1_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB2__A 0x145001A +#define QAM_LC_QUAL_TAB2__W 5 +#define QAM_LC_QUAL_TAB2__M 0x1F +#define QAM_LC_QUAL_TAB2__PRE 0x1 + +#define QAM_LC_QUAL_TAB2_VALUE__B 0 +#define QAM_LC_QUAL_TAB2_VALUE__W 5 +#define QAM_LC_QUAL_TAB2_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB2_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB3__A 0x145001B +#define QAM_LC_QUAL_TAB3__W 5 +#define QAM_LC_QUAL_TAB3__M 0x1F +#define QAM_LC_QUAL_TAB3__PRE 0x1 + +#define QAM_LC_QUAL_TAB3_VALUE__B 0 +#define QAM_LC_QUAL_TAB3_VALUE__W 5 +#define QAM_LC_QUAL_TAB3_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB3_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB4__A 0x145001C +#define QAM_LC_QUAL_TAB4__W 5 +#define QAM_LC_QUAL_TAB4__M 0x1F +#define QAM_LC_QUAL_TAB4__PRE 0x1 + +#define QAM_LC_QUAL_TAB4_VALUE__B 0 +#define QAM_LC_QUAL_TAB4_VALUE__W 5 +#define QAM_LC_QUAL_TAB4_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB4_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB5__A 0x145001D +#define QAM_LC_QUAL_TAB5__W 5 +#define QAM_LC_QUAL_TAB5__M 0x1F +#define QAM_LC_QUAL_TAB5__PRE 0x1 + +#define QAM_LC_QUAL_TAB5_VALUE__B 0 +#define QAM_LC_QUAL_TAB5_VALUE__W 5 +#define QAM_LC_QUAL_TAB5_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB5_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB6__A 0x145001E +#define QAM_LC_QUAL_TAB6__W 5 +#define QAM_LC_QUAL_TAB6__M 0x1F +#define QAM_LC_QUAL_TAB6__PRE 0x1 + +#define QAM_LC_QUAL_TAB6_VALUE__B 0 +#define QAM_LC_QUAL_TAB6_VALUE__W 5 +#define QAM_LC_QUAL_TAB6_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB6_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB8__A 0x145001F +#define QAM_LC_QUAL_TAB8__W 5 +#define QAM_LC_QUAL_TAB8__M 0x1F +#define QAM_LC_QUAL_TAB8__PRE 0x1 + +#define QAM_LC_QUAL_TAB8_VALUE__B 0 +#define QAM_LC_QUAL_TAB8_VALUE__W 5 +#define QAM_LC_QUAL_TAB8_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB8_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB9__A 0x1450020 +#define QAM_LC_QUAL_TAB9__W 5 +#define QAM_LC_QUAL_TAB9__M 0x1F +#define QAM_LC_QUAL_TAB9__PRE 0x1 + +#define QAM_LC_QUAL_TAB9_VALUE__B 0 +#define QAM_LC_QUAL_TAB9_VALUE__W 5 +#define QAM_LC_QUAL_TAB9_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB9_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB10__A 0x1450021 +#define QAM_LC_QUAL_TAB10__W 5 +#define QAM_LC_QUAL_TAB10__M 0x1F +#define QAM_LC_QUAL_TAB10__PRE 0x1 + +#define QAM_LC_QUAL_TAB10_VALUE__B 0 +#define QAM_LC_QUAL_TAB10_VALUE__W 5 +#define QAM_LC_QUAL_TAB10_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB10_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB12__A 0x1450022 +#define QAM_LC_QUAL_TAB12__W 5 +#define QAM_LC_QUAL_TAB12__M 0x1F +#define QAM_LC_QUAL_TAB12__PRE 0x1 + +#define QAM_LC_QUAL_TAB12_VALUE__B 0 +#define QAM_LC_QUAL_TAB12_VALUE__W 5 +#define QAM_LC_QUAL_TAB12_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB12_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB15__A 0x1450023 +#define QAM_LC_QUAL_TAB15__W 5 +#define QAM_LC_QUAL_TAB15__M 0x1F +#define QAM_LC_QUAL_TAB15__PRE 0x1 + +#define QAM_LC_QUAL_TAB15_VALUE__B 0 +#define QAM_LC_QUAL_TAB15_VALUE__W 5 +#define QAM_LC_QUAL_TAB15_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB15_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB16__A 0x1450024 +#define QAM_LC_QUAL_TAB16__W 5 +#define QAM_LC_QUAL_TAB16__M 0x1F +#define QAM_LC_QUAL_TAB16__PRE 0x1 + +#define QAM_LC_QUAL_TAB16_VALUE__B 0 +#define QAM_LC_QUAL_TAB16_VALUE__W 5 +#define QAM_LC_QUAL_TAB16_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB16_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB20__A 0x1450025 +#define QAM_LC_QUAL_TAB20__W 5 +#define QAM_LC_QUAL_TAB20__M 0x1F +#define QAM_LC_QUAL_TAB20__PRE 0x1 + +#define QAM_LC_QUAL_TAB20_VALUE__B 0 +#define QAM_LC_QUAL_TAB20_VALUE__W 5 +#define QAM_LC_QUAL_TAB20_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB20_VALUE__PRE 0x1 + +#define QAM_LC_QUAL_TAB25__A 0x1450026 +#define QAM_LC_QUAL_TAB25__W 5 +#define QAM_LC_QUAL_TAB25__M 0x1F +#define QAM_LC_QUAL_TAB25__PRE 0x1 + +#define QAM_LC_QUAL_TAB25_VALUE__B 0 +#define QAM_LC_QUAL_TAB25_VALUE__W 5 +#define QAM_LC_QUAL_TAB25_VALUE__M 0x1F +#define QAM_LC_QUAL_TAB25_VALUE__PRE 0x1 + +#define QAM_LC_EQ_TIMING__A 0x1450027 +#define QAM_LC_EQ_TIMING__W 10 +#define QAM_LC_EQ_TIMING__M 0x3FF +#define QAM_LC_EQ_TIMING__PRE 0x0 + +#define QAM_LC_EQ_TIMING_OFFS__B 0 +#define QAM_LC_EQ_TIMING_OFFS__W 10 +#define QAM_LC_EQ_TIMING_OFFS__M 0x3FF +#define QAM_LC_EQ_TIMING_OFFS__PRE 0x0 + +#define QAM_LC_LPF_FACTORP__A 0x1450028 +#define QAM_LC_LPF_FACTORP__W 3 +#define QAM_LC_LPF_FACTORP__M 0x7 +#define QAM_LC_LPF_FACTORP__PRE 0x3 + +#define QAM_LC_LPF_FACTORP_FACTOR__B 0 +#define QAM_LC_LPF_FACTORP_FACTOR__W 3 +#define QAM_LC_LPF_FACTORP_FACTOR__M 0x7 +#define QAM_LC_LPF_FACTORP_FACTOR__PRE 0x3 + +#define QAM_LC_LPF_FACTORI__A 0x1450029 +#define QAM_LC_LPF_FACTORI__W 3 +#define QAM_LC_LPF_FACTORI__M 0x7 +#define QAM_LC_LPF_FACTORI__PRE 0x3 + +#define QAM_LC_LPF_FACTORI_FACTOR__B 0 +#define QAM_LC_LPF_FACTORI_FACTOR__W 3 +#define QAM_LC_LPF_FACTORI_FACTOR__M 0x7 +#define QAM_LC_LPF_FACTORI_FACTOR__PRE 0x3 + +#define QAM_LC_RATE_LIMIT__A 0x145002A +#define QAM_LC_RATE_LIMIT__W 2 +#define QAM_LC_RATE_LIMIT__M 0x3 +#define QAM_LC_RATE_LIMIT__PRE 0x3 + +#define QAM_LC_RATE_LIMIT_LIMIT__B 0 +#define QAM_LC_RATE_LIMIT_LIMIT__W 2 +#define QAM_LC_RATE_LIMIT_LIMIT__M 0x3 +#define QAM_LC_RATE_LIMIT_LIMIT__PRE 0x3 + +#define QAM_LC_SYMBOL_FREQ__A 0x145002B +#define QAM_LC_SYMBOL_FREQ__W 10 +#define QAM_LC_SYMBOL_FREQ__M 0x3FF +#define QAM_LC_SYMBOL_FREQ__PRE 0x199 + +#define QAM_LC_SYMBOL_FREQ_FREQ__B 0 +#define QAM_LC_SYMBOL_FREQ_FREQ__W 10 +#define QAM_LC_SYMBOL_FREQ_FREQ__M 0x3FF +#define QAM_LC_SYMBOL_FREQ_FREQ__PRE 0x199 +#define QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_64 0x197 +#define QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_256 0x1B2 + +#define QAM_LC_MTA_LENGTH__A 0x145002C +#define QAM_LC_MTA_LENGTH__W 2 +#define QAM_LC_MTA_LENGTH__M 0x3 +#define QAM_LC_MTA_LENGTH__PRE 0x2 + +#define QAM_LC_MTA_LENGTH_LENGTH__B 0 +#define QAM_LC_MTA_LENGTH_LENGTH__W 2 +#define QAM_LC_MTA_LENGTH_LENGTH__M 0x3 +#define QAM_LC_MTA_LENGTH_LENGTH__PRE 0x2 + +#define QAM_LC_AMP_ACCU__A 0x145002D +#define QAM_LC_AMP_ACCU__W 14 +#define QAM_LC_AMP_ACCU__M 0x3FFF +#define QAM_LC_AMP_ACCU__PRE 0x600 + +#define QAM_LC_AMP_ACCU_ACCU__B 0 +#define QAM_LC_AMP_ACCU_ACCU__W 14 +#define QAM_LC_AMP_ACCU_ACCU__M 0x3FFF +#define QAM_LC_AMP_ACCU_ACCU__PRE 0x600 + +#define QAM_LC_FREQ_ACCU__A 0x145002E +#define QAM_LC_FREQ_ACCU__W 10 +#define QAM_LC_FREQ_ACCU__M 0x3FF +#define QAM_LC_FREQ_ACCU__PRE 0x0 + +#define QAM_LC_FREQ_ACCU_ACCU__B 0 +#define QAM_LC_FREQ_ACCU_ACCU__W 10 +#define QAM_LC_FREQ_ACCU_ACCU__M 0x3FF +#define QAM_LC_FREQ_ACCU_ACCU__PRE 0x0 + +#define QAM_LC_RATE_ACCU__A 0x145002F +#define QAM_LC_RATE_ACCU__W 10 +#define QAM_LC_RATE_ACCU__M 0x3FF +#define QAM_LC_RATE_ACCU__PRE 0x0 + +#define QAM_LC_RATE_ACCU_ACCU__B 0 +#define QAM_LC_RATE_ACCU_ACCU__W 10 +#define QAM_LC_RATE_ACCU_ACCU__M 0x3FF +#define QAM_LC_RATE_ACCU_ACCU__PRE 0x0 + +#define QAM_LC_AMPLITUDE__A 0x1450030 +#define QAM_LC_AMPLITUDE__W 10 +#define QAM_LC_AMPLITUDE__M 0x3FF +#define QAM_LC_AMPLITUDE__PRE 0x0 + +#define QAM_LC_AMPLITUDE_SIZE__B 0 +#define QAM_LC_AMPLITUDE_SIZE__W 10 +#define QAM_LC_AMPLITUDE_SIZE__M 0x3FF +#define QAM_LC_AMPLITUDE_SIZE__PRE 0x0 + +#define QAM_LC_RAD_ERROR__A 0x1450031 +#define QAM_LC_RAD_ERROR__W 10 +#define QAM_LC_RAD_ERROR__M 0x3FF +#define QAM_LC_RAD_ERROR__PRE 0x0 + +#define QAM_LC_RAD_ERROR_SIZE__B 0 +#define QAM_LC_RAD_ERROR_SIZE__W 10 +#define QAM_LC_RAD_ERROR_SIZE__M 0x3FF +#define QAM_LC_RAD_ERROR_SIZE__PRE 0x0 + +#define QAM_LC_FREQ_OFFS__A 0x1450032 +#define QAM_LC_FREQ_OFFS__W 10 +#define QAM_LC_FREQ_OFFS__M 0x3FF +#define QAM_LC_FREQ_OFFS__PRE 0x0 + +#define QAM_LC_FREQ_OFFS_OFFS__B 0 +#define QAM_LC_FREQ_OFFS_OFFS__W 10 +#define QAM_LC_FREQ_OFFS_OFFS__M 0x3FF +#define QAM_LC_FREQ_OFFS_OFFS__PRE 0x0 + +#define QAM_LC_PHASE_ERROR__A 0x1450033 +#define QAM_LC_PHASE_ERROR__W 10 +#define QAM_LC_PHASE_ERROR__M 0x3FF +#define QAM_LC_PHASE_ERROR__PRE 0x0 + +#define QAM_LC_PHASE_ERROR_SIZE__B 0 +#define QAM_LC_PHASE_ERROR_SIZE__W 10 +#define QAM_LC_PHASE_ERROR_SIZE__M 0x3FF +#define QAM_LC_PHASE_ERROR_SIZE__PRE 0x0 + +#define QAM_VD_COMM_EXEC__A 0x1460000 +#define QAM_VD_COMM_EXEC__W 2 +#define QAM_VD_COMM_EXEC__M 0x3 +#define QAM_VD_COMM_EXEC__PRE 0x0 +#define QAM_VD_COMM_EXEC_STOP 0x0 +#define QAM_VD_COMM_EXEC_ACTIVE 0x1 +#define QAM_VD_COMM_EXEC_HOLD 0x2 + +#define QAM_VD_COMM_MB__A 0x1460002 +#define QAM_VD_COMM_MB__W 2 +#define QAM_VD_COMM_MB__M 0x3 +#define QAM_VD_COMM_MB__PRE 0x0 +#define QAM_VD_COMM_MB_CTL__B 0 +#define QAM_VD_COMM_MB_CTL__W 1 +#define QAM_VD_COMM_MB_CTL__M 0x1 +#define QAM_VD_COMM_MB_CTL__PRE 0x0 +#define QAM_VD_COMM_MB_CTL_OFF 0x0 +#define QAM_VD_COMM_MB_CTL_ON 0x1 +#define QAM_VD_COMM_MB_OBS__B 1 +#define QAM_VD_COMM_MB_OBS__W 1 +#define QAM_VD_COMM_MB_OBS__M 0x2 +#define QAM_VD_COMM_MB_OBS__PRE 0x0 +#define QAM_VD_COMM_MB_OBS_OFF 0x0 +#define QAM_VD_COMM_MB_OBS_ON 0x2 + +#define QAM_VD_COMM_INT_REQ__A 0x1460003 +#define QAM_VD_COMM_INT_REQ__W 1 +#define QAM_VD_COMM_INT_REQ__M 0x1 +#define QAM_VD_COMM_INT_REQ__PRE 0x0 +#define QAM_VD_COMM_INT_STA__A 0x1460005 +#define QAM_VD_COMM_INT_STA__W 2 +#define QAM_VD_COMM_INT_STA__M 0x3 +#define QAM_VD_COMM_INT_STA__PRE 0x0 + +#define QAM_VD_COMM_INT_STA_LOCK_INT__B 0 +#define QAM_VD_COMM_INT_STA_LOCK_INT__W 1 +#define QAM_VD_COMM_INT_STA_LOCK_INT__M 0x1 +#define QAM_VD_COMM_INT_STA_LOCK_INT__PRE 0x0 + +#define QAM_VD_COMM_INT_STA_PERIOD_INT__B 1 +#define QAM_VD_COMM_INT_STA_PERIOD_INT__W 1 +#define QAM_VD_COMM_INT_STA_PERIOD_INT__M 0x2 +#define QAM_VD_COMM_INT_STA_PERIOD_INT__PRE 0x0 + +#define QAM_VD_COMM_INT_MSK__A 0x1460006 +#define QAM_VD_COMM_INT_MSK__W 2 +#define QAM_VD_COMM_INT_MSK__M 0x3 +#define QAM_VD_COMM_INT_MSK__PRE 0x0 +#define QAM_VD_COMM_INT_MSK_LOCK_INT__B 0 +#define QAM_VD_COMM_INT_MSK_LOCK_INT__W 1 +#define QAM_VD_COMM_INT_MSK_LOCK_INT__M 0x1 +#define QAM_VD_COMM_INT_MSK_LOCK_INT__PRE 0x0 +#define QAM_VD_COMM_INT_MSK_PERIOD_INT__B 1 +#define QAM_VD_COMM_INT_MSK_PERIOD_INT__W 1 +#define QAM_VD_COMM_INT_MSK_PERIOD_INT__M 0x2 +#define QAM_VD_COMM_INT_MSK_PERIOD_INT__PRE 0x0 + +#define QAM_VD_COMM_INT_STM__A 0x1460007 +#define QAM_VD_COMM_INT_STM__W 2 +#define QAM_VD_COMM_INT_STM__M 0x3 +#define QAM_VD_COMM_INT_STM__PRE 0x0 +#define QAM_VD_COMM_INT_STM_LOCK_INT__B 0 +#define QAM_VD_COMM_INT_STM_LOCK_INT__W 1 +#define QAM_VD_COMM_INT_STM_LOCK_INT__M 0x1 +#define QAM_VD_COMM_INT_STM_LOCK_INT__PRE 0x0 +#define QAM_VD_COMM_INT_STM_PERIOD_INT__B 1 +#define QAM_VD_COMM_INT_STM_PERIOD_INT__W 1 +#define QAM_VD_COMM_INT_STM_PERIOD_INT__M 0x2 +#define QAM_VD_COMM_INT_STM_PERIOD_INT__PRE 0x0 + +#define QAM_VD_STATUS__A 0x1460010 +#define QAM_VD_STATUS__W 1 +#define QAM_VD_STATUS__M 0x1 +#define QAM_VD_STATUS__PRE 0x0 + +#define QAM_VD_STATUS_LOCK__B 0 +#define QAM_VD_STATUS_LOCK__W 1 +#define QAM_VD_STATUS_LOCK__M 0x1 +#define QAM_VD_STATUS_LOCK__PRE 0x0 + +#define QAM_VD_UNLOCK_CONTROL__A 0x1460011 +#define QAM_VD_UNLOCK_CONTROL__W 1 +#define QAM_VD_UNLOCK_CONTROL__M 0x1 +#define QAM_VD_UNLOCK_CONTROL__PRE 0x0 + +#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__B 0 +#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__W 1 +#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__M 0x1 +#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__PRE 0x0 + +#define QAM_VD_MIN_VOTING_ROUNDS__A 0x1460012 +#define QAM_VD_MIN_VOTING_ROUNDS__W 6 +#define QAM_VD_MIN_VOTING_ROUNDS__M 0x3F +#define QAM_VD_MIN_VOTING_ROUNDS__PRE 0x10 + +#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__B 0 +#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__W 6 +#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__M 0x3F +#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__PRE 0x10 + +#define QAM_VD_MAX_VOTING_ROUNDS__A 0x1460013 +#define QAM_VD_MAX_VOTING_ROUNDS__W 6 +#define QAM_VD_MAX_VOTING_ROUNDS__M 0x3F +#define QAM_VD_MAX_VOTING_ROUNDS__PRE 0x10 + +#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__B 0 +#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__W 6 +#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__M 0x3F +#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__PRE 0x10 + +#define QAM_VD_TRACEBACK_DEPTH__A 0x1460014 +#define QAM_VD_TRACEBACK_DEPTH__W 5 +#define QAM_VD_TRACEBACK_DEPTH__M 0x1F +#define QAM_VD_TRACEBACK_DEPTH__PRE 0x10 + +#define QAM_VD_TRACEBACK_DEPTH_LENGTH__B 0 +#define QAM_VD_TRACEBACK_DEPTH_LENGTH__W 5 +#define QAM_VD_TRACEBACK_DEPTH_LENGTH__M 0x1F +#define QAM_VD_TRACEBACK_DEPTH_LENGTH__PRE 0x10 + +#define QAM_VD_UNLOCK__A 0x1460015 +#define QAM_VD_UNLOCK__W 1 +#define QAM_VD_UNLOCK__M 0x1 +#define QAM_VD_UNLOCK__PRE 0x0 +#define QAM_VD_MEASUREMENT_PERIOD__A 0x1460016 +#define QAM_VD_MEASUREMENT_PERIOD__W 16 +#define QAM_VD_MEASUREMENT_PERIOD__M 0xFFFF +#define QAM_VD_MEASUREMENT_PERIOD__PRE 0x8236 + +#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__B 0 +#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__W 16 +#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__M 0xFFFF +#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__PRE 0x8236 + +#define QAM_VD_MEASUREMENT_PRESCALE__A 0x1460017 +#define QAM_VD_MEASUREMENT_PRESCALE__W 16 +#define QAM_VD_MEASUREMENT_PRESCALE__M 0xFFFF +#define QAM_VD_MEASUREMENT_PRESCALE__PRE 0x4 + +#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__B 0 +#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__W 16 +#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__M 0xFFFF +#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__PRE 0x4 + +#define QAM_VD_DELTA_PATH_METRIC__A 0x1460018 +#define QAM_VD_DELTA_PATH_METRIC__W 16 +#define QAM_VD_DELTA_PATH_METRIC__M 0xFFFF +#define QAM_VD_DELTA_PATH_METRIC__PRE 0xFFFF + +#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__B 0 +#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__W 12 +#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__M 0xFFF +#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__PRE 0xFFF + +#define QAM_VD_DELTA_PATH_METRIC_EXP__B 12 +#define QAM_VD_DELTA_PATH_METRIC_EXP__W 4 +#define QAM_VD_DELTA_PATH_METRIC_EXP__M 0xF000 +#define QAM_VD_DELTA_PATH_METRIC_EXP__PRE 0xF000 + +#define QAM_VD_NR_QSYM_ERRORS__A 0x1460019 +#define QAM_VD_NR_QSYM_ERRORS__W 16 +#define QAM_VD_NR_QSYM_ERRORS__M 0xFFFF +#define QAM_VD_NR_QSYM_ERRORS__PRE 0xFFFF + +#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__B 0 +#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__W 12 +#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__M 0xFFF +#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__PRE 0xFFF + +#define QAM_VD_NR_QSYM_ERRORS_EXP__B 12 +#define QAM_VD_NR_QSYM_ERRORS_EXP__W 4 +#define QAM_VD_NR_QSYM_ERRORS_EXP__M 0xF000 +#define QAM_VD_NR_QSYM_ERRORS_EXP__PRE 0xF000 + +#define QAM_VD_NR_SYMBOL_ERRORS__A 0x146001A +#define QAM_VD_NR_SYMBOL_ERRORS__W 16 +#define QAM_VD_NR_SYMBOL_ERRORS__M 0xFFFF +#define QAM_VD_NR_SYMBOL_ERRORS__PRE 0xFFFF + +#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__B 0 +#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__W 12 +#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__M 0xFFF +#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__PRE 0xFFF + +#define QAM_VD_NR_SYMBOL_ERRORS_EXP__B 12 +#define QAM_VD_NR_SYMBOL_ERRORS_EXP__W 4 +#define QAM_VD_NR_SYMBOL_ERRORS_EXP__M 0xF000 +#define QAM_VD_NR_SYMBOL_ERRORS_EXP__PRE 0xF000 + +#define QAM_VD_RELOCK_COUNT__A 0x146001B +#define QAM_VD_RELOCK_COUNT__W 16 +#define QAM_VD_RELOCK_COUNT__M 0xFFFF +#define QAM_VD_RELOCK_COUNT__PRE 0x0 + +#define QAM_VD_RELOCK_COUNT_COUNT__B 0 +#define QAM_VD_RELOCK_COUNT_COUNT__W 8 +#define QAM_VD_RELOCK_COUNT_COUNT__M 0xFF +#define QAM_VD_RELOCK_COUNT_COUNT__PRE 0x0 + +#define QAM_SY_COMM_EXEC__A 0x1470000 +#define QAM_SY_COMM_EXEC__W 2 +#define QAM_SY_COMM_EXEC__M 0x3 +#define QAM_SY_COMM_EXEC__PRE 0x0 +#define QAM_SY_COMM_EXEC_STOP 0x0 +#define QAM_SY_COMM_EXEC_ACTIVE 0x1 +#define QAM_SY_COMM_EXEC_HOLD 0x2 + +#define QAM_SY_COMM_MB__A 0x1470002 +#define QAM_SY_COMM_MB__W 2 +#define QAM_SY_COMM_MB__M 0x3 +#define QAM_SY_COMM_MB__PRE 0x0 +#define QAM_SY_COMM_MB_CTL__B 0 +#define QAM_SY_COMM_MB_CTL__W 1 +#define QAM_SY_COMM_MB_CTL__M 0x1 +#define QAM_SY_COMM_MB_CTL__PRE 0x0 +#define QAM_SY_COMM_MB_CTL_OFF 0x0 +#define QAM_SY_COMM_MB_CTL_ON 0x1 +#define QAM_SY_COMM_MB_OBS__B 1 +#define QAM_SY_COMM_MB_OBS__W 1 +#define QAM_SY_COMM_MB_OBS__M 0x2 +#define QAM_SY_COMM_MB_OBS__PRE 0x0 +#define QAM_SY_COMM_MB_OBS_OFF 0x0 +#define QAM_SY_COMM_MB_OBS_ON 0x2 + +#define QAM_SY_COMM_INT_REQ__A 0x1470003 +#define QAM_SY_COMM_INT_REQ__W 1 +#define QAM_SY_COMM_INT_REQ__M 0x1 +#define QAM_SY_COMM_INT_REQ__PRE 0x0 +#define QAM_SY_COMM_INT_STA__A 0x1470005 +#define QAM_SY_COMM_INT_STA__W 4 +#define QAM_SY_COMM_INT_STA__M 0xF +#define QAM_SY_COMM_INT_STA__PRE 0x0 + +#define QAM_SY_COMM_INT_STA_LOCK_INT__B 0 +#define QAM_SY_COMM_INT_STA_LOCK_INT__W 1 +#define QAM_SY_COMM_INT_STA_LOCK_INT__M 0x1 +#define QAM_SY_COMM_INT_STA_LOCK_INT__PRE 0x0 + +#define QAM_SY_COMM_INT_STA_UNLOCK_INT__B 1 +#define QAM_SY_COMM_INT_STA_UNLOCK_INT__W 1 +#define QAM_SY_COMM_INT_STA_UNLOCK_INT__M 0x2 +#define QAM_SY_COMM_INT_STA_UNLOCK_INT__PRE 0x0 + +#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__B 2 +#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__W 1 +#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__M 0x4 +#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__PRE 0x0 + +#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__B 3 +#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__W 1 +#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__M 0x8 +#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__PRE 0x0 + +#define QAM_SY_COMM_INT_MSK__A 0x1470006 +#define QAM_SY_COMM_INT_MSK__W 4 +#define QAM_SY_COMM_INT_MSK__M 0xF +#define QAM_SY_COMM_INT_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_MSK_LOCK_MSK__B 0 +#define QAM_SY_COMM_INT_MSK_LOCK_MSK__W 1 +#define QAM_SY_COMM_INT_MSK_LOCK_MSK__M 0x1 +#define QAM_SY_COMM_INT_MSK_LOCK_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__B 1 +#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__W 1 +#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__M 0x2 +#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__B 2 +#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__W 1 +#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__M 0x4 +#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__B 3 +#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__W 1 +#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__M 0x8 +#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__PRE 0x0 + +#define QAM_SY_COMM_INT_STM__A 0x1470007 +#define QAM_SY_COMM_INT_STM__W 4 +#define QAM_SY_COMM_INT_STM__M 0xF +#define QAM_SY_COMM_INT_STM__PRE 0x0 +#define QAM_SY_COMM_INT_STM_LOCK_MSK__B 0 +#define QAM_SY_COMM_INT_STM_LOCK_MSK__W 1 +#define QAM_SY_COMM_INT_STM_LOCK_MSK__M 0x1 +#define QAM_SY_COMM_INT_STM_LOCK_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__B 1 +#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__W 1 +#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__M 0x2 +#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__B 2 +#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__W 1 +#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__M 0x4 +#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__PRE 0x0 +#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__B 3 +#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__W 1 +#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__M 0x8 +#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__PRE 0x0 + +#define QAM_SY_STATUS__A 0x1470010 +#define QAM_SY_STATUS__W 2 +#define QAM_SY_STATUS__M 0x3 +#define QAM_SY_STATUS__PRE 0x0 + +#define QAM_SY_STATUS_SYNC_STATE__B 0 +#define QAM_SY_STATUS_SYNC_STATE__W 2 +#define QAM_SY_STATUS_SYNC_STATE__M 0x3 +#define QAM_SY_STATUS_SYNC_STATE__PRE 0x0 + +#define QAM_SY_TIMEOUT__A 0x1470011 +#define QAM_SY_TIMEOUT__W 16 +#define QAM_SY_TIMEOUT__M 0xFFFF +#define QAM_SY_TIMEOUT__PRE 0x3A98 + +#define QAM_SY_SYNC_LWM__A 0x1470012 +#define QAM_SY_SYNC_LWM__W 4 +#define QAM_SY_SYNC_LWM__M 0xF +#define QAM_SY_SYNC_LWM__PRE 0x2 + +#define QAM_SY_SYNC_AWM__A 0x1470013 +#define QAM_SY_SYNC_AWM__W 4 +#define QAM_SY_SYNC_AWM__M 0xF +#define QAM_SY_SYNC_AWM__PRE 0x3 + +#define QAM_SY_SYNC_HWM__A 0x1470014 +#define QAM_SY_SYNC_HWM__W 4 +#define QAM_SY_SYNC_HWM__M 0xF +#define QAM_SY_SYNC_HWM__PRE 0x5 + +#define QAM_SY_UNLOCK__A 0x1470015 +#define QAM_SY_UNLOCK__W 1 +#define QAM_SY_UNLOCK__M 0x1 +#define QAM_SY_UNLOCK__PRE 0x0 +#define QAM_SY_CONTROL_WORD__A 0x1470016 +#define QAM_SY_CONTROL_WORD__W 4 +#define QAM_SY_CONTROL_WORD__M 0xF +#define QAM_SY_CONTROL_WORD__PRE 0x0 + +#define QAM_SY_CONTROL_WORD_CTRL_WORD__B 0 +#define QAM_SY_CONTROL_WORD_CTRL_WORD__W 4 +#define QAM_SY_CONTROL_WORD_CTRL_WORD__M 0xF +#define QAM_SY_CONTROL_WORD_CTRL_WORD__PRE 0x0 + +#define QAM_VD_ISS_RAM__A 0x1480000 + +#define QAM_VD_QSS_RAM__A 0x1490000 + +#define QAM_VD_SYM_RAM__A 0x14A0000 + +#define SCU_COMM_EXEC__A 0x800000 +#define SCU_COMM_EXEC__W 2 +#define SCU_COMM_EXEC__M 0x3 +#define SCU_COMM_EXEC__PRE 0x0 +#define SCU_COMM_EXEC_STOP 0x0 +#define SCU_COMM_EXEC_ACTIVE 0x1 +#define SCU_COMM_EXEC_HOLD 0x2 + +#define SCU_COMM_STATE__A 0x800001 +#define SCU_COMM_STATE__W 16 +#define SCU_COMM_STATE__M 0xFFFF +#define SCU_COMM_STATE__PRE 0x0 + +#define SCU_COMM_STATE_COMM_STATE__B 0 +#define SCU_COMM_STATE_COMM_STATE__W 16 +#define SCU_COMM_STATE_COMM_STATE__M 0xFFFF +#define SCU_COMM_STATE_COMM_STATE__PRE 0x0 + +#define SCU_TOP_COMM_EXEC__A 0x810000 +#define SCU_TOP_COMM_EXEC__W 2 +#define SCU_TOP_COMM_EXEC__M 0x3 +#define SCU_TOP_COMM_EXEC__PRE 0x0 +#define SCU_TOP_COMM_EXEC_STOP 0x0 +#define SCU_TOP_COMM_EXEC_ACTIVE 0x1 +#define SCU_TOP_COMM_EXEC_HOLD 0x2 + +#define SCU_TOP_COMM_STATE__A 0x810001 +#define SCU_TOP_COMM_STATE__W 16 +#define SCU_TOP_COMM_STATE__M 0xFFFF +#define SCU_TOP_COMM_STATE__PRE 0x0 +#define SCU_TOP_MWAIT_CTR__A 0x810010 +#define SCU_TOP_MWAIT_CTR__W 2 +#define SCU_TOP_MWAIT_CTR__M 0x3 +#define SCU_TOP_MWAIT_CTR__PRE 0x0 + +#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__B 0 +#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__W 1 +#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__M 0x1 +#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__PRE 0x0 +#define SCU_TOP_MWAIT_CTR_MWAIT_SEL_TR_MW_OFF 0x0 +#define SCU_TOP_MWAIT_CTR_MWAIT_SEL_TR_MW_ON 0x1 + +#define SCU_TOP_MWAIT_CTR_READY_DIS__B 1 +#define SCU_TOP_MWAIT_CTR_READY_DIS__W 1 +#define SCU_TOP_MWAIT_CTR_READY_DIS__M 0x2 +#define SCU_TOP_MWAIT_CTR_READY_DIS__PRE 0x0 +#define SCU_TOP_MWAIT_CTR_READY_DIS_NMI_ON 0x0 +#define SCU_TOP_MWAIT_CTR_READY_DIS_NMI_OFF 0x2 + +#define SCU_LOW_RAM__A 0x820000 + +#define SCU_LOW_RAM_LOW__B 0 +#define SCU_LOW_RAM_LOW__W 16 +#define SCU_LOW_RAM_LOW__M 0xFFFF +#define SCU_LOW_RAM_LOW__PRE 0x0 + +#define SCU_HIGH_RAM__A 0x830000 + +#define SCU_HIGH_RAM_HIGH__B 0 +#define SCU_HIGH_RAM_HIGH__W 16 +#define SCU_HIGH_RAM_HIGH__M 0xFFFF +#define SCU_HIGH_RAM_HIGH__PRE 0x0 + +#define SCU_RAM_AGC_RF_MAX__A 0x831E96 +#define SCU_RAM_AGC_RF_MAX__W 15 +#define SCU_RAM_AGC_RF_MAX__M 0x7FFF +#define SCU_RAM_AGC_RF_MAX__PRE 0x0 + +#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831E97 +#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__W 16 +#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__M 0xFFFF +#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__PRE 0x0 + +#define SCU_RAM_AGC_KI_CYCCNT__A 0x831E98 +#define SCU_RAM_AGC_KI_CYCCNT__W 16 +#define SCU_RAM_AGC_KI_CYCCNT__M 0xFFFF +#define SCU_RAM_AGC_KI_CYCCNT__PRE 0x0 + +#define SCU_RAM_AGC_KI_CYCLEN__A 0x831E99 +#define SCU_RAM_AGC_KI_CYCLEN__W 16 +#define SCU_RAM_AGC_KI_CYCLEN__M 0xFFFF +#define SCU_RAM_AGC_KI_CYCLEN__PRE 0x0 + +#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831E9A +#define SCU_RAM_AGC_SNS_CYCLEN__W 16 +#define SCU_RAM_AGC_SNS_CYCLEN__M 0xFFFF +#define SCU_RAM_AGC_SNS_CYCLEN__PRE 0x0 + +#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831E9B +#define SCU_RAM_AGC_RF_SNS_DEV_MAX__W 16 +#define SCU_RAM_AGC_RF_SNS_DEV_MAX__M 0xFFFF +#define SCU_RAM_AGC_RF_SNS_DEV_MAX__PRE 0x0 + +#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831E9C +#define SCU_RAM_AGC_RF_SNS_DEV_MIN__W 16 +#define SCU_RAM_AGC_RF_SNS_DEV_MIN__M 0xFFFF +#define SCU_RAM_AGC_RF_SNS_DEV_MIN__PRE 0x0 +#define SCU_RAM_AGC_KI__A 0x831E9D +#define SCU_RAM_AGC_KI__W 15 +#define SCU_RAM_AGC_KI__M 0x7FFF +#define SCU_RAM_AGC_KI__PRE 0x0 + +#define SCU_RAM_AGC_KI_DGAIN__B 0 +#define SCU_RAM_AGC_KI_DGAIN__W 4 +#define SCU_RAM_AGC_KI_DGAIN__M 0xF +#define SCU_RAM_AGC_KI_DGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_RF__B 4 +#define SCU_RAM_AGC_KI_RF__W 4 +#define SCU_RAM_AGC_KI_RF__M 0xF0 +#define SCU_RAM_AGC_KI_RF__PRE 0x0 + +#define SCU_RAM_AGC_KI_IF__B 8 +#define SCU_RAM_AGC_KI_IF__W 4 +#define SCU_RAM_AGC_KI_IF__M 0xF00 +#define SCU_RAM_AGC_KI_IF__PRE 0x0 + +#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__B 12 +#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__W 1 +#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__M 0x1000 +#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__PRE 0x0 + +#define SCU_RAM_AGC_KI_INV_IF_POL__B 13 +#define SCU_RAM_AGC_KI_INV_IF_POL__W 1 +#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000 +#define SCU_RAM_AGC_KI_INV_IF_POL__PRE 0x0 + +#define SCU_RAM_AGC_KI_INV_RF_POL__B 14 +#define SCU_RAM_AGC_KI_INV_RF_POL__W 1 +#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000 +#define SCU_RAM_AGC_KI_INV_RF_POL__PRE 0x0 + +#define SCU_RAM_AGC_KI_RED__A 0x831E9E +#define SCU_RAM_AGC_KI_RED__W 6 +#define SCU_RAM_AGC_KI_RED__M 0x3F +#define SCU_RAM_AGC_KI_RED__PRE 0x0 + +#define SCU_RAM_AGC_KI_RED_INNER_RED__B 0 +#define SCU_RAM_AGC_KI_RED_INNER_RED__W 2 +#define SCU_RAM_AGC_KI_RED_INNER_RED__M 0x3 +#define SCU_RAM_AGC_KI_RED_INNER_RED__PRE 0x0 + +#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2 +#define SCU_RAM_AGC_KI_RED_RAGC_RED__W 2 +#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC +#define SCU_RAM_AGC_KI_RED_RAGC_RED__PRE 0x0 + +#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4 +#define SCU_RAM_AGC_KI_RED_IAGC_RED__W 2 +#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30 +#define SCU_RAM_AGC_KI_RED_IAGC_RED__PRE 0x0 + +#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831E9F +#define SCU_RAM_AGC_KI_INNERGAIN_MIN__W 16 +#define SCU_RAM_AGC_KI_INNERGAIN_MIN__M 0xFFFF +#define SCU_RAM_AGC_KI_INNERGAIN_MIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MINGAIN__A 0x831EA0 +#define SCU_RAM_AGC_KI_MINGAIN__W 16 +#define SCU_RAM_AGC_KI_MINGAIN__M 0xFFFF +#define SCU_RAM_AGC_KI_MINGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831EA1 +#define SCU_RAM_AGC_KI_MAXGAIN__W 16 +#define SCU_RAM_AGC_KI_MAXGAIN__M 0xFFFF +#define SCU_RAM_AGC_KI_MAXGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831EA2 +#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__W 16 +#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__M 0xFFFF +#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__PRE 0x0 +#define SCU_RAM_AGC_KI_MIN__A 0x831EA3 +#define SCU_RAM_AGC_KI_MIN__W 12 +#define SCU_RAM_AGC_KI_MIN__M 0xFFF +#define SCU_RAM_AGC_KI_MIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MIN_DGAIN__B 0 +#define SCU_RAM_AGC_KI_MIN_DGAIN__W 4 +#define SCU_RAM_AGC_KI_MIN_DGAIN__M 0xF +#define SCU_RAM_AGC_KI_MIN_DGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MIN_RF__B 4 +#define SCU_RAM_AGC_KI_MIN_RF__W 4 +#define SCU_RAM_AGC_KI_MIN_RF__M 0xF0 +#define SCU_RAM_AGC_KI_MIN_RF__PRE 0x0 + +#define SCU_RAM_AGC_KI_MIN_IF__B 8 +#define SCU_RAM_AGC_KI_MIN_IF__W 4 +#define SCU_RAM_AGC_KI_MIN_IF__M 0xF00 +#define SCU_RAM_AGC_KI_MIN_IF__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAX__A 0x831EA4 +#define SCU_RAM_AGC_KI_MAX__W 12 +#define SCU_RAM_AGC_KI_MAX__M 0xFFF +#define SCU_RAM_AGC_KI_MAX__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAX_DGAIN__B 0 +#define SCU_RAM_AGC_KI_MAX_DGAIN__W 4 +#define SCU_RAM_AGC_KI_MAX_DGAIN__M 0xF +#define SCU_RAM_AGC_KI_MAX_DGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAX_RF__B 4 +#define SCU_RAM_AGC_KI_MAX_RF__W 4 +#define SCU_RAM_AGC_KI_MAX_RF__M 0xF0 +#define SCU_RAM_AGC_KI_MAX_RF__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAX_IF__B 8 +#define SCU_RAM_AGC_KI_MAX_IF__W 4 +#define SCU_RAM_AGC_KI_MAX_IF__M 0xF00 +#define SCU_RAM_AGC_KI_MAX_IF__PRE 0x0 + +#define SCU_RAM_AGC_CLP_SUM__A 0x831EA5 +#define SCU_RAM_AGC_CLP_SUM__W 16 +#define SCU_RAM_AGC_CLP_SUM__M 0xFFFF +#define SCU_RAM_AGC_CLP_SUM__PRE 0x0 + +#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831EA6 +#define SCU_RAM_AGC_CLP_SUM_MIN__W 16 +#define SCU_RAM_AGC_CLP_SUM_MIN__M 0xFFFF +#define SCU_RAM_AGC_CLP_SUM_MIN__PRE 0x0 + +#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831EA7 +#define SCU_RAM_AGC_CLP_SUM_MAX__W 16 +#define SCU_RAM_AGC_CLP_SUM_MAX__M 0xFFFF +#define SCU_RAM_AGC_CLP_SUM_MAX__PRE 0x0 + +#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831EA8 +#define SCU_RAM_AGC_CLP_CYCLEN__W 16 +#define SCU_RAM_AGC_CLP_CYCLEN__M 0xFFFF +#define SCU_RAM_AGC_CLP_CYCLEN__PRE 0x0 + +#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831EA9 +#define SCU_RAM_AGC_CLP_CYCCNT__W 16 +#define SCU_RAM_AGC_CLP_CYCCNT__M 0xFFFF +#define SCU_RAM_AGC_CLP_CYCCNT__PRE 0x0 + +#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831EAA +#define SCU_RAM_AGC_CLP_DIR_TO__W 8 +#define SCU_RAM_AGC_CLP_DIR_TO__M 0xFF +#define SCU_RAM_AGC_CLP_DIR_TO__PRE 0x0 + +#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831EAB +#define SCU_RAM_AGC_CLP_DIR_WD__W 8 +#define SCU_RAM_AGC_CLP_DIR_WD__M 0xFF +#define SCU_RAM_AGC_CLP_DIR_WD__PRE 0x0 + +#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831EAC +#define SCU_RAM_AGC_CLP_DIR_STP__W 16 +#define SCU_RAM_AGC_CLP_DIR_STP__M 0xFFFF +#define SCU_RAM_AGC_CLP_DIR_STP__PRE 0x0 + +#define SCU_RAM_AGC_SNS_SUM__A 0x831EAD +#define SCU_RAM_AGC_SNS_SUM__W 16 +#define SCU_RAM_AGC_SNS_SUM__M 0xFFFF +#define SCU_RAM_AGC_SNS_SUM__PRE 0x0 + +#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831EAE +#define SCU_RAM_AGC_SNS_SUM_MIN__W 16 +#define SCU_RAM_AGC_SNS_SUM_MIN__M 0xFFFF +#define SCU_RAM_AGC_SNS_SUM_MIN__PRE 0x0 + +#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831EAF +#define SCU_RAM_AGC_SNS_SUM_MAX__W 16 +#define SCU_RAM_AGC_SNS_SUM_MAX__M 0xFFFF +#define SCU_RAM_AGC_SNS_SUM_MAX__PRE 0x0 + +#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831EB0 +#define SCU_RAM_AGC_SNS_CYCCNT__W 16 +#define SCU_RAM_AGC_SNS_CYCCNT__M 0xFFFF +#define SCU_RAM_AGC_SNS_CYCCNT__PRE 0x0 + +#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831EB1 +#define SCU_RAM_AGC_SNS_DIR_TO__W 8 +#define SCU_RAM_AGC_SNS_DIR_TO__M 0xFF +#define SCU_RAM_AGC_SNS_DIR_TO__PRE 0x0 + +#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831EB2 +#define SCU_RAM_AGC_SNS_DIR_WD__W 8 +#define SCU_RAM_AGC_SNS_DIR_WD__M 0xFF +#define SCU_RAM_AGC_SNS_DIR_WD__PRE 0x0 + +#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831EB3 +#define SCU_RAM_AGC_SNS_DIR_STP__W 16 +#define SCU_RAM_AGC_SNS_DIR_STP__M 0xFFFF +#define SCU_RAM_AGC_SNS_DIR_STP__PRE 0x0 + +#define SCU_RAM_AGC_INGAIN__A 0x831EB4 +#define SCU_RAM_AGC_INGAIN__W 16 +#define SCU_RAM_AGC_INGAIN__M 0xFFFF +#define SCU_RAM_AGC_INGAIN__PRE 0x0 + +#define SCU_RAM_AGC_INGAIN_TGT__A 0x831EB5 +#define SCU_RAM_AGC_INGAIN_TGT__W 15 +#define SCU_RAM_AGC_INGAIN_TGT__M 0x7FFF +#define SCU_RAM_AGC_INGAIN_TGT__PRE 0x0 + +#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831EB6 +#define SCU_RAM_AGC_INGAIN_TGT_MIN__W 15 +#define SCU_RAM_AGC_INGAIN_TGT_MIN__M 0x7FFF +#define SCU_RAM_AGC_INGAIN_TGT_MIN__PRE 0x0 + +#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831EB7 +#define SCU_RAM_AGC_INGAIN_TGT_MAX__W 15 +#define SCU_RAM_AGC_INGAIN_TGT_MAX__M 0x7FFF +#define SCU_RAM_AGC_INGAIN_TGT_MAX__PRE 0x0 + +#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831EB8 +#define SCU_RAM_AGC_IF_IACCU_HI__W 16 +#define SCU_RAM_AGC_IF_IACCU_HI__M 0xFFFF +#define SCU_RAM_AGC_IF_IACCU_HI__PRE 0x0 + +#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831EB9 +#define SCU_RAM_AGC_IF_IACCU_LO__W 8 +#define SCU_RAM_AGC_IF_IACCU_LO__M 0xFF +#define SCU_RAM_AGC_IF_IACCU_LO__PRE 0x0 + +#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831EBA +#define SCU_RAM_AGC_IF_IACCU_HI_TGT__W 15 +#define SCU_RAM_AGC_IF_IACCU_HI_TGT__M 0x7FFF +#define SCU_RAM_AGC_IF_IACCU_HI_TGT__PRE 0x0 + +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831EBB +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__W 15 +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__M 0x7FFF +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__PRE 0x0 + +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831EBC +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__W 15 +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__M 0x7FFF +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__PRE 0x0 + +#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831EBD +#define SCU_RAM_AGC_RF_IACCU_HI__W 16 +#define SCU_RAM_AGC_RF_IACCU_HI__M 0xFFFF +#define SCU_RAM_AGC_RF_IACCU_HI__PRE 0x0 + +#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831EBE +#define SCU_RAM_AGC_RF_IACCU_LO__W 8 +#define SCU_RAM_AGC_RF_IACCU_LO__M 0xFF +#define SCU_RAM_AGC_RF_IACCU_LO__PRE 0x0 + +#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831EBF +#define SCU_RAM_AGC_RF_IACCU_HI_CO__W 16 +#define SCU_RAM_AGC_RF_IACCU_HI_CO__M 0xFFFF +#define SCU_RAM_AGC_RF_IACCU_HI_CO__PRE 0x0 + +#define SCU_RAM_SP__A 0x831EC0 +#define SCU_RAM_SP__W 16 +#define SCU_RAM_SP__M 0xFFFF +#define SCU_RAM_SP__PRE 0x0 + +#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831EC1 +#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__W 16 +#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__M 0xFFFF +#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__PRE 0x0 + +#define SCU_RAM_AGC_KI_MIN_IFGAIN__A 0x831EC2 +#define SCU_RAM_AGC_KI_MIN_IFGAIN__W 16 +#define SCU_RAM_AGC_KI_MIN_IFGAIN__M 0xFFFF +#define SCU_RAM_AGC_KI_MIN_IFGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAX_IFGAIN__A 0x831EC3 +#define SCU_RAM_AGC_KI_MAX_IFGAIN__W 16 +#define SCU_RAM_AGC_KI_MAX_IFGAIN__M 0xFFFF +#define SCU_RAM_AGC_KI_MAX_IFGAIN__PRE 0x0 + +#define SCU_RAM_FEC_MEAS_COUNT__A 0x831EC4 +#define SCU_RAM_FEC_MEAS_COUNT__W 16 +#define SCU_RAM_FEC_MEAS_COUNT__M 0xFFFF +#define SCU_RAM_FEC_MEAS_COUNT__PRE 0x0 + +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A 0x831EC5 +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__W 16 +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__M 0xFFFF +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__PRE 0x0 + +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__A 0x831EC6 +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__W 16 +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__M 0xFFFF +#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__PRE 0x0 +#define SCU_RAM_GPIO__A 0x831EC7 +#define SCU_RAM_GPIO__W 1 +#define SCU_RAM_GPIO__M 0x1 +#define SCU_RAM_GPIO__PRE 0x0 + +#define SCU_RAM_GPIO_HW_LOCK_IND__B 0 +#define SCU_RAM_GPIO_HW_LOCK_IND__W 1 +#define SCU_RAM_GPIO_HW_LOCK_IND__M 0x1 +#define SCU_RAM_GPIO_HW_LOCK_IND__PRE 0x0 +#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0 +#define SCU_RAM_GPIO_HW_LOCK_IND_ENABLE 0x1 + +#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8 +#define SCU_RAM_AGC_CLP_CTRL_MODE__W 8 +#define SCU_RAM_AGC_CLP_CTRL_MODE__M 0xFF +#define SCU_RAM_AGC_CLP_CTRL_MODE__PRE 0x0 + +#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__B 0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__W 1 +#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__M 0x1 +#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__PRE 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW_false 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW_true 0x1 + +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__B 1 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__W 1 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__M 0x2 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__PRE 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP_FCC_ENABLE 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP_FCC_DISABLE 0x2 + +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__B 2 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__W 1 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__M 0x4 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__PRE 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC_DEC_DISABLE 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC_DEC_ENABLE 0x4 + +#define SCU_RAM_AGC_KI_MIN_RFGAIN__A 0x831EC9 +#define SCU_RAM_AGC_KI_MIN_RFGAIN__W 16 +#define SCU_RAM_AGC_KI_MIN_RFGAIN__M 0xFFFF +#define SCU_RAM_AGC_KI_MIN_RFGAIN__PRE 0x0 + +#define SCU_RAM_AGC_KI_MAX_RFGAIN__A 0x831ECA +#define SCU_RAM_AGC_KI_MAX_RFGAIN__W 16 +#define SCU_RAM_AGC_KI_MAX_RFGAIN__M 0xFFFF +#define SCU_RAM_AGC_KI_MAX_RFGAIN__PRE 0x0 + +#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB +#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__W 16 +#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__M 0xFFFF +#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__PRE 0x0 + +#define SCU_RAM_INHIBIT_1__A 0x831ECC +#define SCU_RAM_INHIBIT_1__W 16 +#define SCU_RAM_INHIBIT_1__M 0xFFFF +#define SCU_RAM_INHIBIT_1__PRE 0x0 + +#define SCU_RAM_HTOL_BUF_0__A 0x831ECD +#define SCU_RAM_HTOL_BUF_0__W 16 +#define SCU_RAM_HTOL_BUF_0__M 0xFFFF +#define SCU_RAM_HTOL_BUF_0__PRE 0x0 + +#define SCU_RAM_HTOL_BUF_1__A 0x831ECE +#define SCU_RAM_HTOL_BUF_1__W 16 +#define SCU_RAM_HTOL_BUF_1__M 0xFFFF +#define SCU_RAM_HTOL_BUF_1__PRE 0x0 + +#define SCU_RAM_INHIBIT_2__A 0x831ECF +#define SCU_RAM_INHIBIT_2__W 16 +#define SCU_RAM_INHIBIT_2__M 0xFFFF +#define SCU_RAM_INHIBIT_2__PRE 0x0 + +#define SCU_RAM_TR_SHORT_BUF_0__A 0x831ED0 +#define SCU_RAM_TR_SHORT_BUF_0__W 16 +#define SCU_RAM_TR_SHORT_BUF_0__M 0xFFFF +#define SCU_RAM_TR_SHORT_BUF_0__PRE 0x0 + +#define SCU_RAM_TR_SHORT_BUF_1__A 0x831ED1 +#define SCU_RAM_TR_SHORT_BUF_1__W 16 +#define SCU_RAM_TR_SHORT_BUF_1__M 0xFFFF +#define SCU_RAM_TR_SHORT_BUF_1__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_0__A 0x831ED2 +#define SCU_RAM_TR_LONG_BUF_0__W 16 +#define SCU_RAM_TR_LONG_BUF_0__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_0__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_1__A 0x831ED3 +#define SCU_RAM_TR_LONG_BUF_1__W 16 +#define SCU_RAM_TR_LONG_BUF_1__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_1__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_2__A 0x831ED4 +#define SCU_RAM_TR_LONG_BUF_2__W 16 +#define SCU_RAM_TR_LONG_BUF_2__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_2__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_3__A 0x831ED5 +#define SCU_RAM_TR_LONG_BUF_3__W 16 +#define SCU_RAM_TR_LONG_BUF_3__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_3__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_4__A 0x831ED6 +#define SCU_RAM_TR_LONG_BUF_4__W 16 +#define SCU_RAM_TR_LONG_BUF_4__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_4__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_5__A 0x831ED7 +#define SCU_RAM_TR_LONG_BUF_5__W 16 +#define SCU_RAM_TR_LONG_BUF_5__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_5__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_6__A 0x831ED8 +#define SCU_RAM_TR_LONG_BUF_6__W 16 +#define SCU_RAM_TR_LONG_BUF_6__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_6__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_7__A 0x831ED9 +#define SCU_RAM_TR_LONG_BUF_7__W 16 +#define SCU_RAM_TR_LONG_BUF_7__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_7__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_8__A 0x831EDA +#define SCU_RAM_TR_LONG_BUF_8__W 16 +#define SCU_RAM_TR_LONG_BUF_8__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_8__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_9__A 0x831EDB +#define SCU_RAM_TR_LONG_BUF_9__W 16 +#define SCU_RAM_TR_LONG_BUF_9__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_9__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_10__A 0x831EDC +#define SCU_RAM_TR_LONG_BUF_10__W 16 +#define SCU_RAM_TR_LONG_BUF_10__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_10__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_11__A 0x831EDD +#define SCU_RAM_TR_LONG_BUF_11__W 16 +#define SCU_RAM_TR_LONG_BUF_11__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_11__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_12__A 0x831EDE +#define SCU_RAM_TR_LONG_BUF_12__W 16 +#define SCU_RAM_TR_LONG_BUF_12__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_12__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_13__A 0x831EDF +#define SCU_RAM_TR_LONG_BUF_13__W 16 +#define SCU_RAM_TR_LONG_BUF_13__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_13__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_14__A 0x831EE0 +#define SCU_RAM_TR_LONG_BUF_14__W 16 +#define SCU_RAM_TR_LONG_BUF_14__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_14__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_15__A 0x831EE1 +#define SCU_RAM_TR_LONG_BUF_15__W 16 +#define SCU_RAM_TR_LONG_BUF_15__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_15__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_16__A 0x831EE2 +#define SCU_RAM_TR_LONG_BUF_16__W 16 +#define SCU_RAM_TR_LONG_BUF_16__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_16__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_17__A 0x831EE3 +#define SCU_RAM_TR_LONG_BUF_17__W 16 +#define SCU_RAM_TR_LONG_BUF_17__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_17__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_18__A 0x831EE4 +#define SCU_RAM_TR_LONG_BUF_18__W 16 +#define SCU_RAM_TR_LONG_BUF_18__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_18__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_19__A 0x831EE5 +#define SCU_RAM_TR_LONG_BUF_19__W 16 +#define SCU_RAM_TR_LONG_BUF_19__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_19__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_20__A 0x831EE6 +#define SCU_RAM_TR_LONG_BUF_20__W 16 +#define SCU_RAM_TR_LONG_BUF_20__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_20__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_21__A 0x831EE7 +#define SCU_RAM_TR_LONG_BUF_21__W 16 +#define SCU_RAM_TR_LONG_BUF_21__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_21__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_22__A 0x831EE8 +#define SCU_RAM_TR_LONG_BUF_22__W 16 +#define SCU_RAM_TR_LONG_BUF_22__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_22__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_23__A 0x831EE9 +#define SCU_RAM_TR_LONG_BUF_23__W 16 +#define SCU_RAM_TR_LONG_BUF_23__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_23__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_24__A 0x831EEA +#define SCU_RAM_TR_LONG_BUF_24__W 16 +#define SCU_RAM_TR_LONG_BUF_24__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_24__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_25__A 0x831EEB +#define SCU_RAM_TR_LONG_BUF_25__W 16 +#define SCU_RAM_TR_LONG_BUF_25__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_25__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_26__A 0x831EEC +#define SCU_RAM_TR_LONG_BUF_26__W 16 +#define SCU_RAM_TR_LONG_BUF_26__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_26__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_27__A 0x831EED +#define SCU_RAM_TR_LONG_BUF_27__W 16 +#define SCU_RAM_TR_LONG_BUF_27__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_27__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_28__A 0x831EEE +#define SCU_RAM_TR_LONG_BUF_28__W 16 +#define SCU_RAM_TR_LONG_BUF_28__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_28__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_29__A 0x831EEF +#define SCU_RAM_TR_LONG_BUF_29__W 16 +#define SCU_RAM_TR_LONG_BUF_29__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_29__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_30__A 0x831EF0 +#define SCU_RAM_TR_LONG_BUF_30__W 16 +#define SCU_RAM_TR_LONG_BUF_30__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_30__PRE 0x0 + +#define SCU_RAM_TR_LONG_BUF_31__A 0x831EF1 +#define SCU_RAM_TR_LONG_BUF_31__W 16 +#define SCU_RAM_TR_LONG_BUF_31__M 0xFFFF +#define SCU_RAM_TR_LONG_BUF_31__PRE 0x0 +#define SCU_RAM_ATV_AMS_MAX__A 0x831EF2 +#define SCU_RAM_ATV_AMS_MAX__W 11 +#define SCU_RAM_ATV_AMS_MAX__M 0x7FF +#define SCU_RAM_ATV_AMS_MAX__PRE 0x0 + +#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__B 0 +#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__W 11 +#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__M 0x7FF +#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__PRE 0x0 + +#define SCU_RAM_ATV_AMS_MIN__A 0x831EF3 +#define SCU_RAM_ATV_AMS_MIN__W 11 +#define SCU_RAM_ATV_AMS_MIN__M 0x7FF +#define SCU_RAM_ATV_AMS_MIN__PRE 0x0 + +#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__B 0 +#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__W 11 +#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__M 0x7FF +#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__PRE 0x0 + +#define SCU_RAM_ATV_FIELD_CNT__A 0x831EF4 +#define SCU_RAM_ATV_FIELD_CNT__W 9 +#define SCU_RAM_ATV_FIELD_CNT__M 0x1FF +#define SCU_RAM_ATV_FIELD_CNT__PRE 0x0 + +#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__B 0 +#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__W 9 +#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__M 0x1FF +#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__PRE 0x0 + +#define SCU_RAM_ATV_AAGC_FAST__A 0x831EF5 +#define SCU_RAM_ATV_AAGC_FAST__W 1 +#define SCU_RAM_ATV_AAGC_FAST__M 0x1 +#define SCU_RAM_ATV_AAGC_FAST__PRE 0x0 + +#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__B 0 +#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__W 1 +#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__M 0x1 +#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__PRE 0x0 +#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST_OFF 0x0 +#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST_ON 0x1 + +#define SCU_RAM_ATV_AAGC_LP2__A 0x831EF6 +#define SCU_RAM_ATV_AAGC_LP2__W 16 +#define SCU_RAM_ATV_AAGC_LP2__M 0xFFFF +#define SCU_RAM_ATV_AAGC_LP2__PRE 0x0 + +#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__B 0 +#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__W 16 +#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__M 0xFFFF +#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__PRE 0x0 + +#define SCU_RAM_ATV_BP_LVL__A 0x831EF7 +#define SCU_RAM_ATV_BP_LVL__W 11 +#define SCU_RAM_ATV_BP_LVL__M 0x7FF +#define SCU_RAM_ATV_BP_LVL__PRE 0x0 + +#define SCU_RAM_ATV_BP_LVL_BP_LVL__B 0 +#define SCU_RAM_ATV_BP_LVL_BP_LVL__W 11 +#define SCU_RAM_ATV_BP_LVL_BP_LVL__M 0x7FF +#define SCU_RAM_ATV_BP_LVL_BP_LVL__PRE 0x0 + +#define SCU_RAM_ATV_BP_RELY__A 0x831EF8 +#define SCU_RAM_ATV_BP_RELY__W 8 +#define SCU_RAM_ATV_BP_RELY__M 0xFF +#define SCU_RAM_ATV_BP_RELY__PRE 0x0 + +#define SCU_RAM_ATV_BP_RELY_BP_RELY__B 0 +#define SCU_RAM_ATV_BP_RELY_BP_RELY__W 8 +#define SCU_RAM_ATV_BP_RELY_BP_RELY__M 0xFF +#define SCU_RAM_ATV_BP_RELY_BP_RELY__PRE 0x0 + +#define SCU_RAM_ATV_BP_MTA__A 0x831EF9 +#define SCU_RAM_ATV_BP_MTA__W 14 +#define SCU_RAM_ATV_BP_MTA__M 0x3FFF +#define SCU_RAM_ATV_BP_MTA__PRE 0x0 + +#define SCU_RAM_ATV_BP_MTA_BP_MTA__B 0 +#define SCU_RAM_ATV_BP_MTA_BP_MTA__W 14 +#define SCU_RAM_ATV_BP_MTA_BP_MTA__M 0x3FFF +#define SCU_RAM_ATV_BP_MTA_BP_MTA__PRE 0x0 + +#define SCU_RAM_ATV_BP_REF__A 0x831EFA +#define SCU_RAM_ATV_BP_REF__W 11 +#define SCU_RAM_ATV_BP_REF__M 0x7FF +#define SCU_RAM_ATV_BP_REF__PRE 0x0 + +#define SCU_RAM_ATV_BP_REF_BP_REF__B 0 +#define SCU_RAM_ATV_BP_REF_BP_REF__W 11 +#define SCU_RAM_ATV_BP_REF_BP_REF__M 0x7FF +#define SCU_RAM_ATV_BP_REF_BP_REF__PRE 0x0 + +#define SCU_RAM_ATV_BP_REF_MIN__A 0x831EFB +#define SCU_RAM_ATV_BP_REF_MIN__W 11 +#define SCU_RAM_ATV_BP_REF_MIN__M 0x7FF +#define SCU_RAM_ATV_BP_REF_MIN__PRE 0x0 + +#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__B 0 +#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__W 11 +#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__M 0x7FF +#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__PRE 0x0 + +#define SCU_RAM_ATV_BP_REF_MAX__A 0x831EFC +#define SCU_RAM_ATV_BP_REF_MAX__W 11 +#define SCU_RAM_ATV_BP_REF_MAX__M 0x7FF +#define SCU_RAM_ATV_BP_REF_MAX__PRE 0x0 + +#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__B 0 +#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__W 11 +#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__M 0x7FF +#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__PRE 0x0 + +#define SCU_RAM_ATV_BP_CNT__A 0x831EFD +#define SCU_RAM_ATV_BP_CNT__W 8 +#define SCU_RAM_ATV_BP_CNT__M 0xFF +#define SCU_RAM_ATV_BP_CNT__PRE 0x0 + +#define SCU_RAM_ATV_BP_CNT_BP_CNT__B 0 +#define SCU_RAM_ATV_BP_CNT_BP_CNT__W 8 +#define SCU_RAM_ATV_BP_CNT_BP_CNT__M 0xFF +#define SCU_RAM_ATV_BP_CNT_BP_CNT__PRE 0x0 + +#define SCU_RAM_ATV_BP_XD_CNT__A 0x831EFE +#define SCU_RAM_ATV_BP_XD_CNT__W 12 +#define SCU_RAM_ATV_BP_XD_CNT__M 0xFFF +#define SCU_RAM_ATV_BP_XD_CNT__PRE 0x0 + +#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__B 0 +#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__W 12 +#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__M 0xFFF +#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__PRE 0x0 + +#define SCU_RAM_ATV_PAGC_KI_MIN__A 0x831EFF +#define SCU_RAM_ATV_PAGC_KI_MIN__W 12 +#define SCU_RAM_ATV_PAGC_KI_MIN__M 0xFFF +#define SCU_RAM_ATV_PAGC_KI_MIN__PRE 0x0 + +#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__B 0 +#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__W 12 +#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__M 0xFFF +#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__PRE 0x0 + +#define SCU_RAM_ATV_BPC_KI_MIN__A 0x831F00 +#define SCU_RAM_ATV_BPC_KI_MIN__W 12 +#define SCU_RAM_ATV_BPC_KI_MIN__M 0xFFF +#define SCU_RAM_ATV_BPC_KI_MIN__PRE 0x0 + +#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__B 0 +#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__W 12 +#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__M 0xFFF +#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__PRE 0x0 + +#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__A 0x831F01 +#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__W 16 +#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__M 0xFFFF +#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__PRE 0x0 + +#define SCU_RAM_ORX_RF_RX_DATA_RATE__A 0x831F02 +#define SCU_RAM_ORX_RF_RX_DATA_RATE__W 8 +#define SCU_RAM_ORX_RF_RX_DATA_RATE__M 0xFF +#define SCU_RAM_ORX_RF_RX_DATA_RATE__PRE 0x0 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC 0x0 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC 0x1 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC_ALT 0x40 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC_ALT 0x41 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_REGSPEC 0x80 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_INVSPEC 0x81 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_REGSPEC 0xC0 +#define SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_INVSPEC 0xC1 + +#define SCU_RAM_ORX_SCU_STATE__A 0x831F03 +#define SCU_RAM_ORX_SCU_STATE__W 8 +#define SCU_RAM_ORX_SCU_STATE__M 0xFF +#define SCU_RAM_ORX_SCU_STATE__PRE 0x0 +#define SCU_RAM_ORX_SCU_STATE_RESET 0x0 +#define SCU_RAM_ORX_SCU_STATE_AGN_HUNT 0x1 +#define SCU_RAM_ORX_SCU_STATE_DGN_HUNT 0x2 +#define SCU_RAM_ORX_SCU_STATE_AGC_HUNT 0x3 +#define SCU_RAM_ORX_SCU_STATE_FRQ_HUNT 0x4 +#define SCU_RAM_ORX_SCU_STATE_PHA_HUNT 0x8 +#define SCU_RAM_ORX_SCU_STATE_TIM_HUNT 0x10 +#define SCU_RAM_ORX_SCU_STATE_EQU_HUNT 0x20 +#define SCU_RAM_ORX_SCU_STATE_EQT_HUNT 0x30 +#define SCU_RAM_ORX_SCU_STATE_SYNC 0x40 + +#define SCU_RAM_ORX_SCU_LOCK__A 0x831F04 +#define SCU_RAM_ORX_SCU_LOCK__W 16 +#define SCU_RAM_ORX_SCU_LOCK__M 0xFFFF +#define SCU_RAM_ORX_SCU_LOCK__PRE 0x0 + +#define SCU_RAM_ORX_TARGET_MODE__A 0x831F05 +#define SCU_RAM_ORX_TARGET_MODE__W 2 +#define SCU_RAM_ORX_TARGET_MODE__M 0x3 +#define SCU_RAM_ORX_TARGET_MODE__PRE 0x0 +#define SCU_RAM_ORX_TARGET_MODE_1544KBPS 0x0 +#define SCU_RAM_ORX_TARGET_MODE_3088KBPS 0x1 +#define SCU_RAM_ORX_TARGET_MODE_2048KBPS_SQRT 0x2 +#define SCU_RAM_ORX_TARGET_MODE_2048KBPS_RO 0x3 + +#define SCU_RAM_ORX_MER_MIN_DB__A 0x831F06 +#define SCU_RAM_ORX_MER_MIN_DB__W 8 +#define SCU_RAM_ORX_MER_MIN_DB__M 0xFF +#define SCU_RAM_ORX_MER_MIN_DB__PRE 0x0 + +#define SCU_RAM_ORX_RF_GAIN__A 0x831F07 +#define SCU_RAM_ORX_RF_GAIN__W 16 +#define SCU_RAM_ORX_RF_GAIN__M 0xFFFF +#define SCU_RAM_ORX_RF_GAIN__PRE 0x0 + +#define SCU_RAM_ORX_RF_GAIN_MIN__A 0x831F08 +#define SCU_RAM_ORX_RF_GAIN_MIN__W 16 +#define SCU_RAM_ORX_RF_GAIN_MIN__M 0xFFFF +#define SCU_RAM_ORX_RF_GAIN_MIN__PRE 0x0 + +#define SCU_RAM_ORX_RF_GAIN_MAX__A 0x831F09 +#define SCU_RAM_ORX_RF_GAIN_MAX__W 16 +#define SCU_RAM_ORX_RF_GAIN_MAX__M 0xFFFF +#define SCU_RAM_ORX_RF_GAIN_MAX__PRE 0x0 + +#define SCU_RAM_ORX_IF_GAIN__A 0x831F0A +#define SCU_RAM_ORX_IF_GAIN__W 16 +#define SCU_RAM_ORX_IF_GAIN__M 0xFFFF +#define SCU_RAM_ORX_IF_GAIN__PRE 0x0 + +#define SCU_RAM_ORX_IF_GAIN_MIN__A 0x831F0B +#define SCU_RAM_ORX_IF_GAIN_MIN__W 16 +#define SCU_RAM_ORX_IF_GAIN_MIN__M 0xFFFF +#define SCU_RAM_ORX_IF_GAIN_MIN__PRE 0x0 + +#define SCU_RAM_ORX_IF_GAIN_MAX__A 0x831F0C +#define SCU_RAM_ORX_IF_GAIN_MAX__W 16 +#define SCU_RAM_ORX_IF_GAIN_MAX__M 0xFFFF +#define SCU_RAM_ORX_IF_GAIN_MAX__PRE 0x0 + +#define SCU_RAM_ORX_AGN_HEADR__A 0x831F0D +#define SCU_RAM_ORX_AGN_HEADR__W 16 +#define SCU_RAM_ORX_AGN_HEADR__M 0xFFFF +#define SCU_RAM_ORX_AGN_HEADR__PRE 0x0 + +#define SCU_RAM_ORX_AGN_HEADR_STP__A 0x831F0E +#define SCU_RAM_ORX_AGN_HEADR_STP__W 8 +#define SCU_RAM_ORX_AGN_HEADR_STP__M 0xFF +#define SCU_RAM_ORX_AGN_HEADR_STP__PRE 0x0 + +#define SCU_RAM_ORX_AGN_KI__A 0x831F0F +#define SCU_RAM_ORX_AGN_KI__W 8 +#define SCU_RAM_ORX_AGN_KI__M 0xFF +#define SCU_RAM_ORX_AGN_KI__PRE 0x0 + +#define SCU_RAM_ORX_AGN_LOCK_TH__A 0x831F10 +#define SCU_RAM_ORX_AGN_LOCK_TH__W 16 +#define SCU_RAM_ORX_AGN_LOCK_TH__M 0xFFFF +#define SCU_RAM_ORX_AGN_LOCK_TH__PRE 0x0 + +#define SCU_RAM_ORX_AGN_LOCK_WD__A 0x831F11 +#define SCU_RAM_ORX_AGN_LOCK_WD__W 16 +#define SCU_RAM_ORX_AGN_LOCK_WD__M 0xFFFF +#define SCU_RAM_ORX_AGN_LOCK_WD__PRE 0x0 + +#define SCU_RAM_ORX_AGN_ONLOCK_TTH__A 0x831F12 +#define SCU_RAM_ORX_AGN_ONLOCK_TTH__W 16 +#define SCU_RAM_ORX_AGN_ONLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_AGN_ONLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_AGN_UNLOCK_TTH__A 0x831F13 +#define SCU_RAM_ORX_AGN_UNLOCK_TTH__W 16 +#define SCU_RAM_ORX_AGN_UNLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_AGN_UNLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_AGN_LOCK_TOTH__A 0x831F14 +#define SCU_RAM_ORX_AGN_LOCK_TOTH__W 16 +#define SCU_RAM_ORX_AGN_LOCK_TOTH__M 0xFFFF +#define SCU_RAM_ORX_AGN_LOCK_TOTH__PRE 0x0 + +#define SCU_RAM_ORX_AGN_LOCK_MASK__A 0x831F15 +#define SCU_RAM_ORX_AGN_LOCK_MASK__W 8 +#define SCU_RAM_ORX_AGN_LOCK_MASK__M 0xFF +#define SCU_RAM_ORX_AGN_LOCK_MASK__PRE 0x0 + +#define SCU_RAM_ORX_DGN__A 0x831F16 +#define SCU_RAM_ORX_DGN__W 16 +#define SCU_RAM_ORX_DGN__M 0xFFFF +#define SCU_RAM_ORX_DGN__PRE 0x0 + +#define SCU_RAM_ORX_DGN_MIN__A 0x831F17 +#define SCU_RAM_ORX_DGN_MIN__W 16 +#define SCU_RAM_ORX_DGN_MIN__M 0xFFFF +#define SCU_RAM_ORX_DGN_MIN__PRE 0x0 + +#define SCU_RAM_ORX_DGN_MAX__A 0x831F18 +#define SCU_RAM_ORX_DGN_MAX__W 16 +#define SCU_RAM_ORX_DGN_MAX__M 0xFFFF +#define SCU_RAM_ORX_DGN_MAX__PRE 0x0 + +#define SCU_RAM_ORX_DGN_AMP__A 0x831F19 +#define SCU_RAM_ORX_DGN_AMP__W 16 +#define SCU_RAM_ORX_DGN_AMP__M 0xFFFF +#define SCU_RAM_ORX_DGN_AMP__PRE 0x0 + +#define SCU_RAM_ORX_DGN_AMPTARGET__A 0x831F1A +#define SCU_RAM_ORX_DGN_AMPTARGET__W 16 +#define SCU_RAM_ORX_DGN_AMPTARGET__M 0xFFFF +#define SCU_RAM_ORX_DGN_AMPTARGET__PRE 0x0 + +#define SCU_RAM_ORX_DGN_KI__A 0x831F1B +#define SCU_RAM_ORX_DGN_KI__W 8 +#define SCU_RAM_ORX_DGN_KI__M 0xFF +#define SCU_RAM_ORX_DGN_KI__PRE 0x0 + +#define SCU_RAM_ORX_DGN_LOCK_TH__A 0x831F1C +#define SCU_RAM_ORX_DGN_LOCK_TH__W 16 +#define SCU_RAM_ORX_DGN_LOCK_TH__M 0xFFFF +#define SCU_RAM_ORX_DGN_LOCK_TH__PRE 0x0 + +#define SCU_RAM_ORX_DGN_LOCK_WD__A 0x831F1D +#define SCU_RAM_ORX_DGN_LOCK_WD__W 16 +#define SCU_RAM_ORX_DGN_LOCK_WD__M 0xFFFF +#define SCU_RAM_ORX_DGN_LOCK_WD__PRE 0x0 + +#define SCU_RAM_ORX_DGN_ONLOCK_TTH__A 0x831F1E +#define SCU_RAM_ORX_DGN_ONLOCK_TTH__W 16 +#define SCU_RAM_ORX_DGN_ONLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_DGN_ONLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_DGN_UNLOCK_TTH__A 0x831F1F +#define SCU_RAM_ORX_DGN_UNLOCK_TTH__W 16 +#define SCU_RAM_ORX_DGN_UNLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_DGN_UNLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_DGN_LOCK_TOTH__A 0x831F20 +#define SCU_RAM_ORX_DGN_LOCK_TOTH__W 16 +#define SCU_RAM_ORX_DGN_LOCK_TOTH__M 0xFFFF +#define SCU_RAM_ORX_DGN_LOCK_TOTH__PRE 0x0 + +#define SCU_RAM_ORX_DGN_LOCK_MASK__A 0x831F21 +#define SCU_RAM_ORX_DGN_LOCK_MASK__W 8 +#define SCU_RAM_ORX_DGN_LOCK_MASK__M 0xFF +#define SCU_RAM_ORX_DGN_LOCK_MASK__PRE 0x0 + +#define SCU_RAM_ORX_FREQ_GAIN_CORR__A 0x831F22 +#define SCU_RAM_ORX_FREQ_GAIN_CORR__W 8 +#define SCU_RAM_ORX_FREQ_GAIN_CORR__M 0xFF +#define SCU_RAM_ORX_FREQ_GAIN_CORR__PRE 0x0 +#define SCU_RAM_ORX_FREQ_GAIN_CORR_1544KBPS 0x60 +#define SCU_RAM_ORX_FREQ_GAIN_CORR_2048KBPS 0x80 +#define SCU_RAM_ORX_FREQ_GAIN_CORR_3088KBPS 0xC0 + +#define SCU_RAM_ORX_FRQ_OFFSET__A 0x831F23 +#define SCU_RAM_ORX_FRQ_OFFSET__W 16 +#define SCU_RAM_ORX_FRQ_OFFSET__M 0xFFFF +#define SCU_RAM_ORX_FRQ_OFFSET__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_OFFSET_MAX__A 0x831F24 +#define SCU_RAM_ORX_FRQ_OFFSET_MAX__W 15 +#define SCU_RAM_ORX_FRQ_OFFSET_MAX__M 0x7FFF +#define SCU_RAM_ORX_FRQ_OFFSET_MAX__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_KI__A 0x831F25 +#define SCU_RAM_ORX_FRQ_KI__W 8 +#define SCU_RAM_ORX_FRQ_KI__M 0xFF +#define SCU_RAM_ORX_FRQ_KI__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_DIFF__A 0x831F26 +#define SCU_RAM_ORX_FRQ_DIFF__W 16 +#define SCU_RAM_ORX_FRQ_DIFF__M 0xFFFF +#define SCU_RAM_ORX_FRQ_DIFF__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_LOCK_TH__A 0x831F27 +#define SCU_RAM_ORX_FRQ_LOCK_TH__W 16 +#define SCU_RAM_ORX_FRQ_LOCK_TH__M 0xFFFF +#define SCU_RAM_ORX_FRQ_LOCK_TH__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_LOCK_WD__A 0x831F28 +#define SCU_RAM_ORX_FRQ_LOCK_WD__W 16 +#define SCU_RAM_ORX_FRQ_LOCK_WD__M 0xFFFF +#define SCU_RAM_ORX_FRQ_LOCK_WD__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__A 0x831F29 +#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__W 16 +#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__A 0x831F2A +#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__W 16 +#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_LOCK_TOTH__A 0x831F2B +#define SCU_RAM_ORX_FRQ_LOCK_TOTH__W 16 +#define SCU_RAM_ORX_FRQ_LOCK_TOTH__M 0xFFFF +#define SCU_RAM_ORX_FRQ_LOCK_TOTH__PRE 0x0 + +#define SCU_RAM_ORX_FRQ_LOCK_MASK__A 0x831F2C +#define SCU_RAM_ORX_FRQ_LOCK_MASK__W 8 +#define SCU_RAM_ORX_FRQ_LOCK_MASK__M 0xFF +#define SCU_RAM_ORX_FRQ_LOCK_MASK__PRE 0x0 + +#define SCU_RAM_ORX_PHA_DIFF__A 0x831F2D +#define SCU_RAM_ORX_PHA_DIFF__W 16 +#define SCU_RAM_ORX_PHA_DIFF__M 0xFFFF +#define SCU_RAM_ORX_PHA_DIFF__PRE 0x0 + +#define SCU_RAM_ORX_PHA_LOCK_TH__A 0x831F2E +#define SCU_RAM_ORX_PHA_LOCK_TH__W 16 +#define SCU_RAM_ORX_PHA_LOCK_TH__M 0xFFFF +#define SCU_RAM_ORX_PHA_LOCK_TH__PRE 0x0 + +#define SCU_RAM_ORX_PHA_LOCK_WD__A 0x831F2F +#define SCU_RAM_ORX_PHA_LOCK_WD__W 16 +#define SCU_RAM_ORX_PHA_LOCK_WD__M 0xFFFF +#define SCU_RAM_ORX_PHA_LOCK_WD__PRE 0x0 + +#define SCU_RAM_ORX_PHA_ONLOCK_TTH__A 0x831F30 +#define SCU_RAM_ORX_PHA_ONLOCK_TTH__W 16 +#define SCU_RAM_ORX_PHA_ONLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_PHA_ONLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_PHA_UNLOCK_TTH__A 0x831F31 +#define SCU_RAM_ORX_PHA_UNLOCK_TTH__W 16 +#define SCU_RAM_ORX_PHA_UNLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_PHA_UNLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_PHA_LOCK_TOTH__A 0x831F32 +#define SCU_RAM_ORX_PHA_LOCK_TOTH__W 16 +#define SCU_RAM_ORX_PHA_LOCK_TOTH__M 0xFFFF +#define SCU_RAM_ORX_PHA_LOCK_TOTH__PRE 0x0 + +#define SCU_RAM_ORX_PHA_LOCK_MASK__A 0x831F33 +#define SCU_RAM_ORX_PHA_LOCK_MASK__W 8 +#define SCU_RAM_ORX_PHA_LOCK_MASK__M 0xFF +#define SCU_RAM_ORX_PHA_LOCK_MASK__PRE 0x0 + +#define SCU_RAM_ORX_TIM_OFFSET__A 0x831F34 +#define SCU_RAM_ORX_TIM_OFFSET__W 16 +#define SCU_RAM_ORX_TIM_OFFSET__M 0xFFFF +#define SCU_RAM_ORX_TIM_OFFSET__PRE 0x0 + +#define SCU_RAM_ORX_TIM_DIFF__A 0x831F35 +#define SCU_RAM_ORX_TIM_DIFF__W 16 +#define SCU_RAM_ORX_TIM_DIFF__M 0xFFFF +#define SCU_RAM_ORX_TIM_DIFF__PRE 0x0 + +#define SCU_RAM_ORX_TIM_LOCK_TH__A 0x831F36 +#define SCU_RAM_ORX_TIM_LOCK_TH__W 16 +#define SCU_RAM_ORX_TIM_LOCK_TH__M 0xFFFF +#define SCU_RAM_ORX_TIM_LOCK_TH__PRE 0x0 + +#define SCU_RAM_ORX_TIM_LOCK_WD__A 0x831F37 +#define SCU_RAM_ORX_TIM_LOCK_WD__W 16 +#define SCU_RAM_ORX_TIM_LOCK_WD__M 0xFFFF +#define SCU_RAM_ORX_TIM_LOCK_WD__PRE 0x0 + +#define SCU_RAM_ORX_TIM_ONLOCK_TTH__A 0x831F38 +#define SCU_RAM_ORX_TIM_ONLOCK_TTH__W 16 +#define SCU_RAM_ORX_TIM_ONLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_TIM_ONLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_TIM_UNLOCK_TTH__A 0x831F39 +#define SCU_RAM_ORX_TIM_UNLOCK_TTH__W 16 +#define SCU_RAM_ORX_TIM_UNLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_TIM_UNLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_TIM_LOCK_TOTH__A 0x831F3A +#define SCU_RAM_ORX_TIM_LOCK_TOTH__W 16 +#define SCU_RAM_ORX_TIM_LOCK_TOTH__M 0xFFFF +#define SCU_RAM_ORX_TIM_LOCK_TOTH__PRE 0x0 + +#define SCU_RAM_ORX_TIM_LOCK_MASK__A 0x831F3B +#define SCU_RAM_ORX_TIM_LOCK_MASK__W 8 +#define SCU_RAM_ORX_TIM_LOCK_MASK__M 0xFF +#define SCU_RAM_ORX_TIM_LOCK_MASK__PRE 0x0 + +#define SCU_RAM_ORX_EQU_DIFF__A 0x831F3C +#define SCU_RAM_ORX_EQU_DIFF__W 16 +#define SCU_RAM_ORX_EQU_DIFF__M 0xFFFF +#define SCU_RAM_ORX_EQU_DIFF__PRE 0x0 + +#define SCU_RAM_ORX_EQU_LOCK_TH__A 0x831F3D +#define SCU_RAM_ORX_EQU_LOCK_TH__W 16 +#define SCU_RAM_ORX_EQU_LOCK_TH__M 0xFFFF +#define SCU_RAM_ORX_EQU_LOCK_TH__PRE 0x0 + +#define SCU_RAM_ORX_EQU_LOCK_WD__A 0x831F3E +#define SCU_RAM_ORX_EQU_LOCK_WD__W 16 +#define SCU_RAM_ORX_EQU_LOCK_WD__M 0xFFFF +#define SCU_RAM_ORX_EQU_LOCK_WD__PRE 0x0 + +#define SCU_RAM_ORX_EQU_ONLOCK_TTH__A 0x831F3F +#define SCU_RAM_ORX_EQU_ONLOCK_TTH__W 16 +#define SCU_RAM_ORX_EQU_ONLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_EQU_ONLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_EQU_UNLOCK_TTH__A 0x831F40 +#define SCU_RAM_ORX_EQU_UNLOCK_TTH__W 16 +#define SCU_RAM_ORX_EQU_UNLOCK_TTH__M 0xFFFF +#define SCU_RAM_ORX_EQU_UNLOCK_TTH__PRE 0x0 + +#define SCU_RAM_ORX_EQU_LOCK_TOTH__A 0x831F41 +#define SCU_RAM_ORX_EQU_LOCK_TOTH__W 16 +#define SCU_RAM_ORX_EQU_LOCK_TOTH__M 0xFFFF +#define SCU_RAM_ORX_EQU_LOCK_TOTH__PRE 0x0 + +#define SCU_RAM_ORX_EQU_LOCK_MASK__A 0x831F42 +#define SCU_RAM_ORX_EQU_LOCK_MASK__W 8 +#define SCU_RAM_ORX_EQU_LOCK_MASK__M 0xFF +#define SCU_RAM_ORX_EQU_LOCK_MASK__PRE 0x0 + +#define SCU_RAM_ORX_FLT_FRQ__A 0x831F43 +#define SCU_RAM_ORX_FLT_FRQ__W 16 +#define SCU_RAM_ORX_FLT_FRQ__M 0xFFFF +#define SCU_RAM_ORX_FLT_FRQ__PRE 0x0 +#define SCU_RAM_ORX_RST_CPH__A 0x831F44 +#define SCU_RAM_ORX_RST_CPH__W 4 +#define SCU_RAM_ORX_RST_CPH__M 0xF +#define SCU_RAM_ORX_RST_CPH__PRE 0x0 + +#define SCU_RAM_ORX_RST_CPH_RST_CPH__B 0 +#define SCU_RAM_ORX_RST_CPH_RST_CPH__W 4 +#define SCU_RAM_ORX_RST_CPH_RST_CPH__M 0xF +#define SCU_RAM_ORX_RST_CPH_RST_CPH__PRE 0x0 + +#define SCU_RAM_ORX_RST_CTI__A 0x831F45 +#define SCU_RAM_ORX_RST_CTI__W 4 +#define SCU_RAM_ORX_RST_CTI__M 0xF +#define SCU_RAM_ORX_RST_CTI__PRE 0x0 + +#define SCU_RAM_ORX_RST_CTI_RST_CTI__B 0 +#define SCU_RAM_ORX_RST_CTI_RST_CTI__W 4 +#define SCU_RAM_ORX_RST_CTI_RST_CTI__M 0xF +#define SCU_RAM_ORX_RST_CTI_RST_CTI__PRE 0x0 + +#define SCU_RAM_ORX_RST_KRN__A 0x831F46 +#define SCU_RAM_ORX_RST_KRN__W 4 +#define SCU_RAM_ORX_RST_KRN__M 0xF +#define SCU_RAM_ORX_RST_KRN__PRE 0x0 + +#define SCU_RAM_ORX_RST_KRN_RST_KRN__B 0 +#define SCU_RAM_ORX_RST_KRN_RST_KRN__W 4 +#define SCU_RAM_ORX_RST_KRN_RST_KRN__M 0xF +#define SCU_RAM_ORX_RST_KRN_RST_KRN__PRE 0x0 + +#define SCU_RAM_ORX_RST_KRP__A 0x831F47 +#define SCU_RAM_ORX_RST_KRP__W 4 +#define SCU_RAM_ORX_RST_KRP__M 0xF +#define SCU_RAM_ORX_RST_KRP__PRE 0x0 + +#define SCU_RAM_ORX_RST_KRP_RST_KRP__B 0 +#define SCU_RAM_ORX_RST_KRP_RST_KRP__W 4 +#define SCU_RAM_ORX_RST_KRP_RST_KRP__M 0xF +#define SCU_RAM_ORX_RST_KRP_RST_KRP__PRE 0x0 + +#define SCU_RAM_ATV_STANDARD__A 0x831F48 +#define SCU_RAM_ATV_STANDARD__W 12 +#define SCU_RAM_ATV_STANDARD__M 0xFFF +#define SCU_RAM_ATV_STANDARD__PRE 0x0 + +#define SCU_RAM_ATV_STANDARD_STANDARD__B 0 +#define SCU_RAM_ATV_STANDARD_STANDARD__W 12 +#define SCU_RAM_ATV_STANDARD_STANDARD__M 0xFFF +#define SCU_RAM_ATV_STANDARD_STANDARD__PRE 0x0 +#define SCU_RAM_ATV_STANDARD_STANDARD_MN 0x2 +#define SCU_RAM_ATV_STANDARD_STANDARD_B 0x103 +#define SCU_RAM_ATV_STANDARD_STANDARD_G 0x3 +#define SCU_RAM_ATV_STANDARD_STANDARD_DK 0x4 +#define SCU_RAM_ATV_STANDARD_STANDARD_L 0x9 +#define SCU_RAM_ATV_STANDARD_STANDARD_LP 0x109 +#define SCU_RAM_ATV_STANDARD_STANDARD_I 0xA +#define SCU_RAM_ATV_STANDARD_STANDARD_FM 0x40 + +#define SCU_RAM_ATV_DETECT__A 0x831F49 +#define SCU_RAM_ATV_DETECT__W 1 +#define SCU_RAM_ATV_DETECT__M 0x1 +#define SCU_RAM_ATV_DETECT__PRE 0x0 + +#define SCU_RAM_ATV_DETECT_DETECT__B 0 +#define SCU_RAM_ATV_DETECT_DETECT__W 1 +#define SCU_RAM_ATV_DETECT_DETECT__M 0x1 +#define SCU_RAM_ATV_DETECT_DETECT__PRE 0x0 +#define SCU_RAM_ATV_DETECT_DETECT_false 0x0 +#define SCU_RAM_ATV_DETECT_DETECT_true 0x1 + +#define SCU_RAM_ATV_DETECT_TH__A 0x831F4A +#define SCU_RAM_ATV_DETECT_TH__W 8 +#define SCU_RAM_ATV_DETECT_TH__M 0xFF +#define SCU_RAM_ATV_DETECT_TH__PRE 0x0 + +#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__B 0 +#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__W 8 +#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__M 0xFF +#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__PRE 0x0 + +#define SCU_RAM_ATV_LOCK__A 0x831F4B +#define SCU_RAM_ATV_LOCK__W 2 +#define SCU_RAM_ATV_LOCK__M 0x3 +#define SCU_RAM_ATV_LOCK__PRE 0x0 + +#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__B 0 +#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__W 1 +#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__M 0x1 +#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__PRE 0x0 +#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT_NO_LOCK 0x0 +#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT_LOCK 0x1 + +#define SCU_RAM_ATV_LOCK_SYNC_FLAG__B 1 +#define SCU_RAM_ATV_LOCK_SYNC_FLAG__W 1 +#define SCU_RAM_ATV_LOCK_SYNC_FLAG__M 0x2 +#define SCU_RAM_ATV_LOCK_SYNC_FLAG__PRE 0x0 +#define SCU_RAM_ATV_LOCK_SYNC_FLAG_NO_SYNC 0x0 +#define SCU_RAM_ATV_LOCK_SYNC_FLAG_SYNC 0x2 + +#define SCU_RAM_ATV_CR_LOCK__A 0x831F4C +#define SCU_RAM_ATV_CR_LOCK__W 11 +#define SCU_RAM_ATV_CR_LOCK__M 0x7FF +#define SCU_RAM_ATV_CR_LOCK__PRE 0x0 + +#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__B 0 +#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__W 11 +#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__M 0x7FF +#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__PRE 0x0 + +#define SCU_RAM_ATV_AGC_MODE__A 0x831F4D +#define SCU_RAM_ATV_AGC_MODE__W 8 +#define SCU_RAM_ATV_AGC_MODE__M 0xFF +#define SCU_RAM_ATV_AGC_MODE__PRE 0x0 + +#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__B 2 +#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__W 1 +#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__M 0x4 +#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__PRE 0x0 +#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL_AGC_FAST 0x0 +#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL_AGC_SLOW 0x4 + +#define SCU_RAM_ATV_AGC_MODE_BP_EN__B 3 +#define SCU_RAM_ATV_AGC_MODE_BP_EN__W 1 +#define SCU_RAM_ATV_AGC_MODE_BP_EN__M 0x8 +#define SCU_RAM_ATV_AGC_MODE_BP_EN__PRE 0x0 +#define SCU_RAM_ATV_AGC_MODE_BP_EN_BPC_DISABLE 0x0 +#define SCU_RAM_ATV_AGC_MODE_BP_EN_BPC_ENABLE 0x8 + +#define SCU_RAM_ATV_AGC_MODE_SIF_STD__B 4 +#define SCU_RAM_ATV_AGC_MODE_SIF_STD__W 2 +#define SCU_RAM_ATV_AGC_MODE_SIF_STD__M 0x30 +#define SCU_RAM_ATV_AGC_MODE_SIF_STD__PRE 0x0 +#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_OFF 0x0 +#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_FM 0x10 +#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_AM 0x20 + +#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__B 6 +#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__W 1 +#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__M 0x40 +#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__PRE 0x0 +#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN_FAGC_DISABLE 0x0 +#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN_FAGC_ENABLE 0x40 + +#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__B 7 +#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__W 1 +#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__M 0x80 +#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__PRE 0x0 +#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP_MWA_ENABLE 0x0 +#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP_MWA_DISABLE 0x80 + +#define SCU_RAM_ATV_RSV_01__A 0x831F4E +#define SCU_RAM_ATV_RSV_01__W 16 +#define SCU_RAM_ATV_RSV_01__M 0xFFFF +#define SCU_RAM_ATV_RSV_01__PRE 0x0 + +#define SCU_RAM_ATV_RSV_02__A 0x831F4F +#define SCU_RAM_ATV_RSV_02__W 16 +#define SCU_RAM_ATV_RSV_02__M 0xFFFF +#define SCU_RAM_ATV_RSV_02__PRE 0x0 + +#define SCU_RAM_ATV_RSV_03__A 0x831F50 +#define SCU_RAM_ATV_RSV_03__W 16 +#define SCU_RAM_ATV_RSV_03__M 0xFFFF +#define SCU_RAM_ATV_RSV_03__PRE 0x0 + +#define SCU_RAM_ATV_RSV_04__A 0x831F51 +#define SCU_RAM_ATV_RSV_04__W 16 +#define SCU_RAM_ATV_RSV_04__M 0xFFFF +#define SCU_RAM_ATV_RSV_04__PRE 0x0 +#define SCU_RAM_ATV_FAGC_TH_RED__A 0x831F52 +#define SCU_RAM_ATV_FAGC_TH_RED__W 8 +#define SCU_RAM_ATV_FAGC_TH_RED__M 0xFF +#define SCU_RAM_ATV_FAGC_TH_RED__PRE 0x0 + +#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__B 0 +#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__W 8 +#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__M 0xFF +#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__PRE 0x0 + +#define SCU_RAM_ATV_AMS_MAX_REF__A 0x831F53 +#define SCU_RAM_ATV_AMS_MAX_REF__W 11 +#define SCU_RAM_ATV_AMS_MAX_REF__M 0x7FF +#define SCU_RAM_ATV_AMS_MAX_REF__PRE 0x0 + +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__B 0 +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__W 11 +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__M 0x7FF +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__PRE 0x0 +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_BG_MN 0x2BC +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_DK 0x2D0 +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_I 0x314 +#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_LLP 0x28A + +#define SCU_RAM_ATV_ACT_AMX__A 0x831F54 +#define SCU_RAM_ATV_ACT_AMX__W 11 +#define SCU_RAM_ATV_ACT_AMX__M 0x7FF +#define SCU_RAM_ATV_ACT_AMX__PRE 0x0 + +#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__B 0 +#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__W 11 +#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__M 0x7FF +#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__PRE 0x0 + +#define SCU_RAM_ATV_ACT_AMI__A 0x831F55 +#define SCU_RAM_ATV_ACT_AMI__W 11 +#define SCU_RAM_ATV_ACT_AMI__M 0x7FF +#define SCU_RAM_ATV_ACT_AMI__PRE 0x0 + +#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__B 0 +#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__W 11 +#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__M 0x7FF +#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__PRE 0x0 + +#define SCU_RAM_ATV_RSV_05__A 0x831F56 +#define SCU_RAM_ATV_RSV_05__W 16 +#define SCU_RAM_ATV_RSV_05__M 0xFFFF +#define SCU_RAM_ATV_RSV_05__PRE 0x0 + +#define SCU_RAM_ATV_RSV_06__A 0x831F57 +#define SCU_RAM_ATV_RSV_06__W 16 +#define SCU_RAM_ATV_RSV_06__M 0xFFFF +#define SCU_RAM_ATV_RSV_06__PRE 0x0 + +#define SCU_RAM_ATV_RSV_07__A 0x831F58 +#define SCU_RAM_ATV_RSV_07__W 16 +#define SCU_RAM_ATV_RSV_07__M 0xFFFF +#define SCU_RAM_ATV_RSV_07__PRE 0x0 + +#define SCU_RAM_ATV_RSV_08__A 0x831F59 +#define SCU_RAM_ATV_RSV_08__W 16 +#define SCU_RAM_ATV_RSV_08__M 0xFFFF +#define SCU_RAM_ATV_RSV_08__PRE 0x0 + +#define SCU_RAM_ATV_RSV_09__A 0x831F5A +#define SCU_RAM_ATV_RSV_09__W 16 +#define SCU_RAM_ATV_RSV_09__M 0xFFFF +#define SCU_RAM_ATV_RSV_09__PRE 0x0 + +#define SCU_RAM_ATV_RSV_10__A 0x831F5B +#define SCU_RAM_ATV_RSV_10__W 16 +#define SCU_RAM_ATV_RSV_10__M 0xFFFF +#define SCU_RAM_ATV_RSV_10__PRE 0x0 + +#define SCU_RAM_ATV_RSV_11__A 0x831F5C +#define SCU_RAM_ATV_RSV_11__W 16 +#define SCU_RAM_ATV_RSV_11__M 0xFFFF +#define SCU_RAM_ATV_RSV_11__PRE 0x0 + +#define SCU_RAM_ATV_RSV_12__A 0x831F5D +#define SCU_RAM_ATV_RSV_12__W 16 +#define SCU_RAM_ATV_RSV_12__M 0xFFFF +#define SCU_RAM_ATV_RSV_12__PRE 0x0 +#define SCU_RAM_ATV_VID_GAIN_HI__A 0x831F5E +#define SCU_RAM_ATV_VID_GAIN_HI__W 16 +#define SCU_RAM_ATV_VID_GAIN_HI__M 0xFFFF +#define SCU_RAM_ATV_VID_GAIN_HI__PRE 0x0 + +#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__B 0 +#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__W 16 +#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__M 0xFFFF +#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__PRE 0x0 + +#define SCU_RAM_ATV_VID_GAIN_LO__A 0x831F5F +#define SCU_RAM_ATV_VID_GAIN_LO__W 8 +#define SCU_RAM_ATV_VID_GAIN_LO__M 0xFF +#define SCU_RAM_ATV_VID_GAIN_LO__PRE 0x0 + +#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__B 0 +#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__W 8 +#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__M 0xFF +#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__PRE 0x0 + +#define SCU_RAM_ATV_RSV_13__A 0x831F60 +#define SCU_RAM_ATV_RSV_13__W 16 +#define SCU_RAM_ATV_RSV_13__M 0xFFFF +#define SCU_RAM_ATV_RSV_13__PRE 0x0 + +#define SCU_RAM_ATV_RSV_14__A 0x831F61 +#define SCU_RAM_ATV_RSV_14__W 16 +#define SCU_RAM_ATV_RSV_14__M 0xFFFF +#define SCU_RAM_ATV_RSV_14__PRE 0x0 + +#define SCU_RAM_ATV_RSV_15__A 0x831F62 +#define SCU_RAM_ATV_RSV_15__W 16 +#define SCU_RAM_ATV_RSV_15__M 0xFFFF +#define SCU_RAM_ATV_RSV_15__PRE 0x0 + +#define SCU_RAM_ATV_RSV_16__A 0x831F63 +#define SCU_RAM_ATV_RSV_16__W 16 +#define SCU_RAM_ATV_RSV_16__M 0xFFFF +#define SCU_RAM_ATV_RSV_16__PRE 0x0 +#define SCU_RAM_ATV_AAGC_CNT__A 0x831F64 +#define SCU_RAM_ATV_AAGC_CNT__W 8 +#define SCU_RAM_ATV_AAGC_CNT__M 0xFF +#define SCU_RAM_ATV_AAGC_CNT__PRE 0x0 + +#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__B 0 +#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__W 8 +#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__M 0xFF +#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__PRE 0x0 + +#define SCU_RAM_ATV_SIF_GAIN__A 0x831F65 +#define SCU_RAM_ATV_SIF_GAIN__W 11 +#define SCU_RAM_ATV_SIF_GAIN__M 0x7FF +#define SCU_RAM_ATV_SIF_GAIN__PRE 0x0 + +#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__B 0 +#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__W 11 +#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__M 0x7FF +#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__PRE 0x0 + +#define SCU_RAM_ATV_RSV_17__A 0x831F66 +#define SCU_RAM_ATV_RSV_17__W 16 +#define SCU_RAM_ATV_RSV_17__M 0xFFFF +#define SCU_RAM_ATV_RSV_17__PRE 0x0 + +#define SCU_RAM_ATV_RSV_18__A 0x831F67 +#define SCU_RAM_ATV_RSV_18__W 16 +#define SCU_RAM_ATV_RSV_18__M 0xFFFF +#define SCU_RAM_ATV_RSV_18__PRE 0x0 + +#define SCU_RAM_ATV_RATE_OFS__A 0x831F68 +#define SCU_RAM_ATV_RATE_OFS__W 12 +#define SCU_RAM_ATV_RATE_OFS__M 0xFFF +#define SCU_RAM_ATV_RATE_OFS__PRE 0x0 + +#define SCU_RAM_ATV_LO_INCR__A 0x831F69 +#define SCU_RAM_ATV_LO_INCR__W 12 +#define SCU_RAM_ATV_LO_INCR__M 0xFFF +#define SCU_RAM_ATV_LO_INCR__PRE 0x0 + +#define SCU_RAM_ATV_IIR_CRIT__A 0x831F6A +#define SCU_RAM_ATV_IIR_CRIT__W 12 +#define SCU_RAM_ATV_IIR_CRIT__M 0xFFF +#define SCU_RAM_ATV_IIR_CRIT__PRE 0x0 + +#define SCU_RAM_ATV_DEF_RATE_OFS__A 0x831F6B +#define SCU_RAM_ATV_DEF_RATE_OFS__W 12 +#define SCU_RAM_ATV_DEF_RATE_OFS__M 0xFFF +#define SCU_RAM_ATV_DEF_RATE_OFS__PRE 0x0 + +#define SCU_RAM_ATV_DEF_LO_INCR__A 0x831F6C +#define SCU_RAM_ATV_DEF_LO_INCR__W 12 +#define SCU_RAM_ATV_DEF_LO_INCR__M 0xFFF +#define SCU_RAM_ATV_DEF_LO_INCR__PRE 0x0 + +#define SCU_RAM_ATV_ENABLE_IIR_WA__A 0x831F6D +#define SCU_RAM_ATV_ENABLE_IIR_WA__W 1 +#define SCU_RAM_ATV_ENABLE_IIR_WA__M 0x1 +#define SCU_RAM_ATV_ENABLE_IIR_WA__PRE 0x0 + +#define SCU_RAM_ATV_MOD_CONTROL__A 0x831F6E +#define SCU_RAM_ATV_MOD_CONTROL__W 12 +#define SCU_RAM_ATV_MOD_CONTROL__M 0xFFF +#define SCU_RAM_ATV_MOD_CONTROL__PRE 0x0 + +#define SCU_RAM_ATV_PAGC_KI_MAX__A 0x831F6F +#define SCU_RAM_ATV_PAGC_KI_MAX__W 12 +#define SCU_RAM_ATV_PAGC_KI_MAX__M 0xFFF +#define SCU_RAM_ATV_PAGC_KI_MAX__PRE 0x0 + +#define SCU_RAM_ATV_BPC_KI_MAX__A 0x831F70 +#define SCU_RAM_ATV_BPC_KI_MAX__W 12 +#define SCU_RAM_ATV_BPC_KI_MAX__M 0xFFF +#define SCU_RAM_ATV_BPC_KI_MAX__PRE 0x0 + +#define SCU_RAM_ATV_NAGC_KI_MAX__A 0x831F71 +#define SCU_RAM_ATV_NAGC_KI_MAX__W 12 +#define SCU_RAM_ATV_NAGC_KI_MAX__M 0xFFF +#define SCU_RAM_ATV_NAGC_KI_MAX__PRE 0x0 +#define SCU_RAM_ATV_NAGC_KI_MIN__A 0x831F72 +#define SCU_RAM_ATV_NAGC_KI_MIN__W 12 +#define SCU_RAM_ATV_NAGC_KI_MIN__M 0xFFF +#define SCU_RAM_ATV_NAGC_KI_MIN__PRE 0x0 + +#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__B 0 +#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__W 12 +#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__M 0xFFF +#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__PRE 0x0 + +#define SCU_RAM_ATV_KI_CHANGE_TH__A 0x831F73 +#define SCU_RAM_ATV_KI_CHANGE_TH__W 8 +#define SCU_RAM_ATV_KI_CHANGE_TH__M 0xFF +#define SCU_RAM_ATV_KI_CHANGE_TH__PRE 0x0 + +#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__B 0 +#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__W 8 +#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__M 0xFF +#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__PRE 0x0 +#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH_NEG_MOD 0x14 +#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH_POS_MOD 0x28 + +#define SCU_RAM_QAM_PARAM_ANNEX__A 0x831F74 +#define SCU_RAM_QAM_PARAM_ANNEX__W 2 +#define SCU_RAM_QAM_PARAM_ANNEX__M 0x3 +#define SCU_RAM_QAM_PARAM_ANNEX__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_ANNEX_BIT__B 0 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT__W 2 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT__M 0x3 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT__PRE 0x0 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_A 0x0 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_B 0x1 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_C 0x2 +#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_D 0x3 + +#define SCU_RAM_QAM_PARAM_CONSTELLATION__A 0x831F75 +#define SCU_RAM_QAM_PARAM_CONSTELLATION__W 3 +#define SCU_RAM_QAM_PARAM_CONSTELLATION__M 0x7 +#define SCU_RAM_QAM_PARAM_CONSTELLATION__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__B 0 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__W 3 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__M 0x7 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__PRE 0x0 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_UNKNOWN 0x0 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_16 0x3 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_32 0x4 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_64 0x5 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_128 0x6 +#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_256 0x7 + +#define SCU_RAM_QAM_PARAM_INTERLEAVE__A 0x831F76 +#define SCU_RAM_QAM_PARAM_INTERLEAVE__W 8 +#define SCU_RAM_QAM_PARAM_INTERLEAVE__M 0xFF +#define SCU_RAM_QAM_PARAM_INTERLEAVE__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__B 0 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__W 8 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__M 0xFF +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__PRE 0x0 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J1 0x0 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J1_V2 0x1 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J2 0x2 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I64_J2 0x3 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J3 0x4 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I32_J4 0x5 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J4 0x6 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I16_J8 0x7 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J5 0x8 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I8_J16 0x9 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J6 0xA +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J7 0xC +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J8 0xE +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I12_J17 0x10 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I5_J4 0x11 +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_UNKNOWN 0xFE +#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_AUTO 0xFF + +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__A 0x831F77 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__W 16 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__M 0xFFFF +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__B 0 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__W 16 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__M 0xFFFF +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__A 0x831F78 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__W 16 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__M 0xFFFF +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__B 0 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__W 16 +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__M 0xFFFF +#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CENTERTAP__A 0x831F79 +#define SCU_RAM_QAM_EQ_CENTERTAP__W 16 +#define SCU_RAM_QAM_EQ_CENTERTAP__M 0xFFFF +#define SCU_RAM_QAM_EQ_CENTERTAP__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__B 0 +#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__W 8 +#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__M 0xFF +#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_0__A 0x831F7A +#define SCU_RAM_QAM_WR_RSV_0__W 16 +#define SCU_RAM_QAM_WR_RSV_0__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_0__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_0_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_0_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_0_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_0_BIT__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__A 0x831F7B +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__W 16 +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__M 0xFFFF +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__B 0 +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__W 16 +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__M 0xFFFF +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__A 0x831F7C +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__W 16 +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__M 0xFFFF +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__PRE 0x0 + +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__B 0 +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__W 16 +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__M 0xFFFF +#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_5__A 0x831F7D +#define SCU_RAM_QAM_WR_RSV_5__W 16 +#define SCU_RAM_QAM_WR_RSV_5__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_5__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_5_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_5_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_5_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_5_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_6__A 0x831F7E +#define SCU_RAM_QAM_WR_RSV_6__W 16 +#define SCU_RAM_QAM_WR_RSV_6__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_6__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_6_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_6_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_6_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_6_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_7__A 0x831F7F +#define SCU_RAM_QAM_WR_RSV_7__W 16 +#define SCU_RAM_QAM_WR_RSV_7__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_7__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_7_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_7_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_7_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_7_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_8__A 0x831F80 +#define SCU_RAM_QAM_WR_RSV_8__W 16 +#define SCU_RAM_QAM_WR_RSV_8__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_8__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_8_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_8_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_8_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_8_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_9__A 0x831F81 +#define SCU_RAM_QAM_WR_RSV_9__W 16 +#define SCU_RAM_QAM_WR_RSV_9__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_9__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_9_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_9_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_9_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_9_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_10__A 0x831F82 +#define SCU_RAM_QAM_WR_RSV_10__W 16 +#define SCU_RAM_QAM_WR_RSV_10__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_10__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_10_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_10_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_10_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_10_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_FMHUM_TO__A 0x831F83 +#define SCU_RAM_QAM_FSM_FMHUM_TO__W 16 +#define SCU_RAM_QAM_FSM_FMHUM_TO__M 0xFFFF +#define SCU_RAM_QAM_FSM_FMHUM_TO__PRE 0x0 + +#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__B 0 +#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__W 16 +#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT_NO_FMHUM_TO 0x0 + +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84 +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__W 16 +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__M 0xFFFF +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__B 0 +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__W 16 +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85 +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__W 16 +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__B 0 +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__W 16 +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__B 0 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__B 0 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__B 0 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__B 0 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__B 0 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__W 16 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_STATE_TGT__A 0x831F8B +#define SCU_RAM_QAM_FSM_STATE_TGT__W 4 +#define SCU_RAM_QAM_FSM_STATE_TGT__M 0xF +#define SCU_RAM_QAM_FSM_STATE_TGT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__B 0 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__W 4 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__M 0xF +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_AMP 0x0 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_RATE 0x1 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_FREQ 0x2 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_UPRIGHT 0x3 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_PHASE 0x4 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING_PHNOISE 0x5 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING 0x6 +#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING_BURST 0x7 + +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__A 0x831F8C +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__W 9 +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__M 0x1FF +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__B 0 +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__W 1 +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__M 0x1 +#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__PRE 0x0 + +#define SCU_RAM_QAM_FSM_ATH__A 0x831F8D +#define SCU_RAM_QAM_FSM_ATH__W 16 +#define SCU_RAM_QAM_FSM_ATH__M 0xFFFF +#define SCU_RAM_QAM_FSM_ATH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_ATH_BIT__B 0 +#define SCU_RAM_QAM_FSM_ATH_BIT__W 16 +#define SCU_RAM_QAM_FSM_ATH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_ATH_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E +#define SCU_RAM_QAM_FSM_RTH__W 16 +#define SCU_RAM_QAM_FSM_RTH__M 0xFFFF +#define SCU_RAM_QAM_FSM_RTH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RTH_BIT__B 0 +#define SCU_RAM_QAM_FSM_RTH_BIT__W 16 +#define SCU_RAM_QAM_FSM_RTH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_RTH_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_16 0x8C +#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_32 0x50 +#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_64 0x4E +#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_128 0x32 +#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_256 0x2D + +#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F +#define SCU_RAM_QAM_FSM_FTH__W 16 +#define SCU_RAM_QAM_FSM_FTH__M 0xFFFF +#define SCU_RAM_QAM_FSM_FTH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_FTH_BIT__B 0 +#define SCU_RAM_QAM_FSM_FTH_BIT__W 16 +#define SCU_RAM_QAM_FSM_FTH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_FTH_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_16 0x32 +#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_32 0x1E +#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_64 0x1E +#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_128 0x14 +#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_256 0x14 + +#define SCU_RAM_QAM_FSM_PTH__A 0x831F90 +#define SCU_RAM_QAM_FSM_PTH__W 16 +#define SCU_RAM_QAM_FSM_PTH__M 0xFFFF +#define SCU_RAM_QAM_FSM_PTH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_PTH_BIT__B 0 +#define SCU_RAM_QAM_FSM_PTH_BIT__W 16 +#define SCU_RAM_QAM_FSM_PTH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_PTH_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_16 0xC8 +#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_32 0x96 +#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_64 0x8C +#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_128 0x64 +#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_256 0x64 + +#define SCU_RAM_QAM_FSM_MTH__A 0x831F91 +#define SCU_RAM_QAM_FSM_MTH__W 16 +#define SCU_RAM_QAM_FSM_MTH__M 0xFFFF +#define SCU_RAM_QAM_FSM_MTH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_MTH_BIT__B 0 +#define SCU_RAM_QAM_FSM_MTH_BIT__W 16 +#define SCU_RAM_QAM_FSM_MTH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_MTH_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_16 0x5A +#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_32 0x50 +#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_64 0x46 +#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_128 0x3C +#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_256 0x50 + +#define SCU_RAM_QAM_FSM_CTH__A 0x831F92 +#define SCU_RAM_QAM_FSM_CTH__W 16 +#define SCU_RAM_QAM_FSM_CTH__M 0xFFFF +#define SCU_RAM_QAM_FSM_CTH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_CTH_BIT__B 0 +#define SCU_RAM_QAM_FSM_CTH_BIT__W 16 +#define SCU_RAM_QAM_FSM_CTH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_CTH_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_16 0xA0 +#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_32 0x8C +#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_64 0x8C +#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_128 0x8C +#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_256 0x8C + +#define SCU_RAM_QAM_FSM_QTH__A 0x831F93 +#define SCU_RAM_QAM_FSM_QTH__W 16 +#define SCU_RAM_QAM_FSM_QTH__M 0xFFFF +#define SCU_RAM_QAM_FSM_QTH__PRE 0x0 + +#define SCU_RAM_QAM_FSM_QTH_BIT__B 0 +#define SCU_RAM_QAM_FSM_QTH_BIT__W 16 +#define SCU_RAM_QAM_FSM_QTH_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_QTH_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_16 0xE6 +#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_32 0xAA +#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_64 0xC3 +#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_128 0x8C +#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_256 0x96 + +#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94 +#define SCU_RAM_QAM_FSM_RATE_LIM__W 16 +#define SCU_RAM_QAM_FSM_RATE_LIM__M 0xFFFF +#define SCU_RAM_QAM_FSM_RATE_LIM__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__B 0 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__W 16 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_16 0x46 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_32 0x46 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_64 0x46 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_128 0x46 +#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_256 0x46 + +#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95 +#define SCU_RAM_QAM_FSM_FREQ_LIM__W 16 +#define SCU_RAM_QAM_FSM_FREQ_LIM__M 0xFFFF +#define SCU_RAM_QAM_FSM_FREQ_LIM__PRE 0x0 + +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__B 0 +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__W 16 +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_16 0x1E +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_32 0x14 +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_64 0x28 +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_128 0x8 +#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_256 0x28 + +#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96 +#define SCU_RAM_QAM_FSM_COUNT_LIM__W 16 +#define SCU_RAM_QAM_FSM_COUNT_LIM__M 0xFFFF +#define SCU_RAM_QAM_FSM_COUNT_LIM__PRE 0x0 + +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__B 0 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__W 16 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_16 0x4 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_32 0x6 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_64 0x6 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_128 0x7 +#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_256 0x6 + +#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97 +#define SCU_RAM_QAM_LC_CA_COARSE__W 16 +#define SCU_RAM_QAM_LC_CA_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_CA_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CA_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_CA_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_CA_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CA_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CA_MEDIUM__A 0x831F98 +#define SCU_RAM_QAM_LC_CA_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_CA_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_CA_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99 +#define SCU_RAM_QAM_LC_CA_FINE__W 16 +#define SCU_RAM_QAM_LC_CA_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_CA_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CA_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_CA_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_CA_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CA_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A +#define SCU_RAM_QAM_LC_CP_COARSE__W 16 +#define SCU_RAM_QAM_LC_CP_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_CP_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CP_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_CP_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_CP_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CP_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B +#define SCU_RAM_QAM_LC_CP_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_CP_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_CP_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C +#define SCU_RAM_QAM_LC_CP_FINE__W 16 +#define SCU_RAM_QAM_LC_CP_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_CP_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CP_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_CP_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_CP_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CP_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D +#define SCU_RAM_QAM_LC_CI_COARSE__W 16 +#define SCU_RAM_QAM_LC_CI_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_CI_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CI_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_CI_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_CI_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CI_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E +#define SCU_RAM_QAM_LC_CI_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_CI_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_CI_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F +#define SCU_RAM_QAM_LC_CI_FINE__W 16 +#define SCU_RAM_QAM_LC_CI_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_CI_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CI_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_CI_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_CI_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CI_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0 +#define SCU_RAM_QAM_LC_EP_COARSE__W 16 +#define SCU_RAM_QAM_LC_EP_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_EP_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_EP_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_EP_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_EP_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_EP_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1 +#define SCU_RAM_QAM_LC_EP_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_EP_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_EP_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2 +#define SCU_RAM_QAM_LC_EP_FINE__W 16 +#define SCU_RAM_QAM_LC_EP_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_EP_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_EP_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_EP_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_EP_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_EP_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3 +#define SCU_RAM_QAM_LC_EI_COARSE__W 16 +#define SCU_RAM_QAM_LC_EI_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_EI_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_EI_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_EI_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_EI_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_EI_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4 +#define SCU_RAM_QAM_LC_EI_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_EI_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_EI_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5 +#define SCU_RAM_QAM_LC_EI_FINE__W 16 +#define SCU_RAM_QAM_LC_EI_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_EI_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_EI_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_EI_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_EI_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_EI_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6 +#define SCU_RAM_QAM_LC_CF_COARSE__W 16 +#define SCU_RAM_QAM_LC_CF_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_CF_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_CF_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_CF_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CF_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7 +#define SCU_RAM_QAM_LC_CF_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_CF_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_CF_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8 +#define SCU_RAM_QAM_LC_CF_FINE__W 16 +#define SCU_RAM_QAM_LC_CF_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_CF_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_CF_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_CF_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CF_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9 +#define SCU_RAM_QAM_LC_CF1_COARSE__W 16 +#define SCU_RAM_QAM_LC_CF1_COARSE__M 0xFFFF +#define SCU_RAM_QAM_LC_CF1_COARSE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__B 0 +#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__W 8 +#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA +#define SCU_RAM_QAM_LC_CF1_MEDIUM__W 16 +#define SCU_RAM_QAM_LC_CF1_MEDIUM__M 0xFFFF +#define SCU_RAM_QAM_LC_CF1_MEDIUM__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__B 0 +#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__W 8 +#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB +#define SCU_RAM_QAM_LC_CF1_FINE__W 16 +#define SCU_RAM_QAM_LC_CF1_FINE__M 0xFFFF +#define SCU_RAM_QAM_LC_CF1_FINE__PRE 0x0 + +#define SCU_RAM_QAM_LC_CF1_FINE_BIT__B 0 +#define SCU_RAM_QAM_LC_CF1_FINE_BIT__W 8 +#define SCU_RAM_QAM_LC_CF1_FINE_BIT__M 0xFF +#define SCU_RAM_QAM_LC_CF1_FINE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC +#define SCU_RAM_QAM_SL_SIG_POWER__W 16 +#define SCU_RAM_QAM_SL_SIG_POWER__M 0xFFFF +#define SCU_RAM_QAM_SL_SIG_POWER__PRE 0x0 + +#define SCU_RAM_QAM_SL_SIG_POWER_BIT__B 0 +#define SCU_RAM_QAM_SL_SIG_POWER_BIT__W 16 +#define SCU_RAM_QAM_SL_SIG_POWER_BIT__M 0xFFFF +#define SCU_RAM_QAM_SL_SIG_POWER_BIT__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD +#define SCU_RAM_QAM_EQ_CMA_RAD0__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD0__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD0__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__B 0 +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__PRE 0x0 +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_16 0x34CD +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_32 0x1A33 +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_64 0x3418 +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_128 0x1814 +#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_256 0x2CEE + +#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE +#define SCU_RAM_QAM_EQ_CMA_RAD1__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD1__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD1__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__B 0 +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__PRE 0x0 +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_16 0x34CD +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_32 0x1A33 +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_64 0x314A +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_128 0x19C6 +#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_256 0x2F34 + +#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF +#define SCU_RAM_QAM_EQ_CMA_RAD2__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD2__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD2__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__B 0 +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__PRE 0x0 +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_16 0x34CD +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_32 0x1A33 +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_64 0x2ED4 +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_128 0x18FA +#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_256 0x30FF + +#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0 +#define SCU_RAM_QAM_EQ_CMA_RAD3__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD3__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD3__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__B 0 +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__PRE 0x0 +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_16 0x34CD +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_32 0x1A33 +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_64 0x35F1 +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_128 0x1909 +#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_256 0x3283 + +#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1 +#define SCU_RAM_QAM_EQ_CMA_RAD4__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD4__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD4__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__B 0 +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__PRE 0x0 +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_16 0x34CD +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_32 0x1A33 +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_64 0x35F1 +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_128 0x1A00 +#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_256 0x353D + +#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2 +#define SCU_RAM_QAM_EQ_CMA_RAD5__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD5__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD5__PRE 0x0 + +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__B 0 +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__W 14 +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__M 0x3FFF +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__PRE 0x0 +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_16 0x34CD +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_32 0x1A33 +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_64 0x3CF9 +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_128 0x1C46 +#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_256 0x3C19 + +#define SCU_RAM_QAM_CTL_ENA__A 0x831FB3 +#define SCU_RAM_QAM_CTL_ENA__W 16 +#define SCU_RAM_QAM_CTL_ENA__M 0xFFFF +#define SCU_RAM_QAM_CTL_ENA__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_AMP__B 0 +#define SCU_RAM_QAM_CTL_ENA_AMP__W 1 +#define SCU_RAM_QAM_CTL_ENA_AMP__M 0x1 +#define SCU_RAM_QAM_CTL_ENA_AMP__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_ACQ__B 1 +#define SCU_RAM_QAM_CTL_ENA_ACQ__W 1 +#define SCU_RAM_QAM_CTL_ENA_ACQ__M 0x2 +#define SCU_RAM_QAM_CTL_ENA_ACQ__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_EQU__B 2 +#define SCU_RAM_QAM_CTL_ENA_EQU__W 1 +#define SCU_RAM_QAM_CTL_ENA_EQU__M 0x4 +#define SCU_RAM_QAM_CTL_ENA_EQU__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_SLC__B 3 +#define SCU_RAM_QAM_CTL_ENA_SLC__W 1 +#define SCU_RAM_QAM_CTL_ENA_SLC__M 0x8 +#define SCU_RAM_QAM_CTL_ENA_SLC__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_LC__B 4 +#define SCU_RAM_QAM_CTL_ENA_LC__W 1 +#define SCU_RAM_QAM_CTL_ENA_LC__M 0x10 +#define SCU_RAM_QAM_CTL_ENA_LC__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_AGC__B 5 +#define SCU_RAM_QAM_CTL_ENA_AGC__W 1 +#define SCU_RAM_QAM_CTL_ENA_AGC__M 0x20 +#define SCU_RAM_QAM_CTL_ENA_AGC__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_FEC__B 6 +#define SCU_RAM_QAM_CTL_ENA_FEC__W 1 +#define SCU_RAM_QAM_CTL_ENA_FEC__M 0x40 +#define SCU_RAM_QAM_CTL_ENA_FEC__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_AXIS__B 7 +#define SCU_RAM_QAM_CTL_ENA_AXIS__W 1 +#define SCU_RAM_QAM_CTL_ENA_AXIS__M 0x80 +#define SCU_RAM_QAM_CTL_ENA_AXIS__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_FMHUM__B 8 +#define SCU_RAM_QAM_CTL_ENA_FMHUM__W 1 +#define SCU_RAM_QAM_CTL_ENA_FMHUM__M 0x100 +#define SCU_RAM_QAM_CTL_ENA_FMHUM__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_EQTIME__B 9 +#define SCU_RAM_QAM_CTL_ENA_EQTIME__W 1 +#define SCU_RAM_QAM_CTL_ENA_EQTIME__M 0x200 +#define SCU_RAM_QAM_CTL_ENA_EQTIME__PRE 0x0 + +#define SCU_RAM_QAM_CTL_ENA_EXTLCK__B 10 +#define SCU_RAM_QAM_CTL_ENA_EXTLCK__W 1 +#define SCU_RAM_QAM_CTL_ENA_EXTLCK__M 0x400 +#define SCU_RAM_QAM_CTL_ENA_EXTLCK__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_1__A 0x831FB4 +#define SCU_RAM_QAM_WR_RSV_1__W 16 +#define SCU_RAM_QAM_WR_RSV_1__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_1__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_1_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_1_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_1_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_1_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_2__A 0x831FB5 +#define SCU_RAM_QAM_WR_RSV_2__W 16 +#define SCU_RAM_QAM_WR_RSV_2__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_2__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_2_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_2_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_2_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_2_BIT__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_3__A 0x831FB6 +#define SCU_RAM_QAM_WR_RSV_3__W 16 +#define SCU_RAM_QAM_WR_RSV_3__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_3__PRE 0x0 + +#define SCU_RAM_QAM_WR_RSV_3_BIT__B 0 +#define SCU_RAM_QAM_WR_RSV_3_BIT__W 16 +#define SCU_RAM_QAM_WR_RSV_3_BIT__M 0xFFFF +#define SCU_RAM_QAM_WR_RSV_3_BIT__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__A 0x831FB7 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__W 3 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__M 0x7 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__B 0 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__W 3 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__M 0x7 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__PRE 0x0 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_UNKNOWN 0x0 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_16 0x3 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_32 0x4 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_64 0x5 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_128 0x6 +#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_256 0x7 + +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__A 0x831FB8 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__W 8 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__M 0xFF +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__B 0 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__W 8 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__M 0xFF +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__PRE 0x0 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J1 0x0 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J1_V2 0x1 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J2 0x2 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I64_J2 0x3 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J3 0x4 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I32_J4 0x5 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J4 0x6 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I16_J8 0x7 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J5 0x8 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I8_J16 0x9 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J6 0xA +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J7 0xC +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J8 0xE +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I12_J17 0x10 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I5_J4 0x11 +#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_UNKNOWN 0xFE + +#define SCU_RAM_QAM_RD_RSV_4__A 0x831FB9 +#define SCU_RAM_QAM_RD_RSV_4__W 16 +#define SCU_RAM_QAM_RD_RSV_4__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_4__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_4_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_4_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_4_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_4_BIT__PRE 0x0 + +#define SCU_RAM_QAM_LOCKED__A 0x831FBA +#define SCU_RAM_QAM_LOCKED__W 16 +#define SCU_RAM_QAM_LOCKED__M 0xFFFF +#define SCU_RAM_QAM_LOCKED__PRE 0x0 + +#define SCU_RAM_QAM_LOCKED_INTLEVEL__B 0 +#define SCU_RAM_QAM_LOCKED_INTLEVEL__W 8 +#define SCU_RAM_QAM_LOCKED_INTLEVEL__M 0xFF +#define SCU_RAM_QAM_LOCKED_INTLEVEL__PRE 0x0 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_NOT_LOCKED 0x0 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_AMP_OK 0x1 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_RATE_OK 0x2 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_FREQ_OK 0x3 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_UPRIGHT_OK 0x4 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_PHNOISE_OK 0x5 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_TRACK_OK 0x6 +#define SCU_RAM_QAM_LOCKED_INTLEVEL_IMPNOISE_OK 0x7 + +#define SCU_RAM_QAM_LOCKED_LOCKED__B 8 +#define SCU_RAM_QAM_LOCKED_LOCKED__W 8 +#define SCU_RAM_QAM_LOCKED_LOCKED__M 0xFF00 +#define SCU_RAM_QAM_LOCKED_LOCKED__PRE 0x0 +#define SCU_RAM_QAM_LOCKED_LOCKED_NOT_LOCKED 0x0 +#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000 +#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000 +#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000 + +#define SCU_RAM_QAM_EVENTS_OCC_HI__A 0x831FBB +#define SCU_RAM_QAM_EVENTS_OCC_HI__W 16 +#define SCU_RAM_QAM_EVENTS_OCC_HI__M 0xFFFF +#define SCU_RAM_QAM_EVENTS_OCC_HI__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__B 0 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__M 0x1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__B 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__M 0x2 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__B 2 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__M 0x4 +#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__B 3 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__M 0x8 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__B 4 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__M 0x10 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__B 5 +#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__M 0x20 +#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__B 6 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__M 0x40 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__B 7 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__M 0x80 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__B 8 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__M 0x100 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__B 9 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__M 0x200 +#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__B 10 +#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__M 0x400 +#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__B 11 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__M 0x800 +#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__B 12 +#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__W 4 +#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__M 0xF000 +#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO__A 0x831FBC +#define SCU_RAM_QAM_EVENTS_OCC_LO__W 16 +#define SCU_RAM_QAM_EVENTS_OCC_LO__M 0xFFFF +#define SCU_RAM_QAM_EVENTS_OCC_LO__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__B 0 +#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__M 0x1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__B 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__M 0x2 +#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__B 2 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__M 0x4 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__B 3 +#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__M 0x8 +#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__B 4 +#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__M 0x10 +#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__B 5 +#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__M 0x20 +#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__B 6 +#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__M 0x40 +#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__B 7 +#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__M 0x80 +#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__B 8 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__M 0x100 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__B 9 +#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__M 0x200 +#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__B 10 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__M 0x400 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__B 11 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__M 0x800 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__B 12 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__M 0x1000 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__B 13 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__M 0x2000 +#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__B 14 +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__M 0x4000 +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__B 15 +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__W 1 +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__M 0x8000 +#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_SCHED_HI__A 0x831FBD +#define SCU_RAM_QAM_EVENTS_SCHED_HI__W 16 +#define SCU_RAM_QAM_EVENTS_SCHED_HI__M 0xFFFF +#define SCU_RAM_QAM_EVENTS_SCHED_HI__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__B 0 +#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__W 16 +#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__M 0xFFFF +#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_SCHED_LO__A 0x831FBE +#define SCU_RAM_QAM_EVENTS_SCHED_LO__W 16 +#define SCU_RAM_QAM_EVENTS_SCHED_LO__M 0xFFFF +#define SCU_RAM_QAM_EVENTS_SCHED_LO__PRE 0x0 + +#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__B 0 +#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__W 16 +#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__M 0xFFFF +#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__PRE 0x0 + +#define SCU_RAM_QAM_TASKLETS_SCHED__A 0x831FBF +#define SCU_RAM_QAM_TASKLETS_SCHED__W 16 +#define SCU_RAM_QAM_TASKLETS_SCHED__M 0xFFFF +#define SCU_RAM_QAM_TASKLETS_SCHED__PRE 0x0 + +#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__B 0 +#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__W 16 +#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__M 0xFFFF +#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__PRE 0x0 + +#define SCU_RAM_QAM_TASKLETS_RUN__A 0x831FC0 +#define SCU_RAM_QAM_TASKLETS_RUN__W 16 +#define SCU_RAM_QAM_TASKLETS_RUN__M 0xFFFF +#define SCU_RAM_QAM_TASKLETS_RUN__PRE 0x0 + +#define SCU_RAM_QAM_TASKLETS_RUN_BIT__B 0 +#define SCU_RAM_QAM_TASKLETS_RUN_BIT__W 16 +#define SCU_RAM_QAM_TASKLETS_RUN_BIT__M 0xFFFF +#define SCU_RAM_QAM_TASKLETS_RUN_BIT__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__A 0x831FC1 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__W 16 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__M 0xFFFF +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__B 0 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__W 16 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__M 0xFFFF +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__A 0x831FC2 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__W 16 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__M 0xFFFF +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__PRE 0x0 + +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__B 0 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__W 16 +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__M 0xFFFF +#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_5__A 0x831FC3 +#define SCU_RAM_QAM_RD_RSV_5__W 16 +#define SCU_RAM_QAM_RD_RSV_5__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_5__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_5_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_5_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_5_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_5_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_6__A 0x831FC4 +#define SCU_RAM_QAM_RD_RSV_6__W 16 +#define SCU_RAM_QAM_RD_RSV_6__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_6__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_6_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_6_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_6_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_6_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_7__A 0x831FC5 +#define SCU_RAM_QAM_RD_RSV_7__W 16 +#define SCU_RAM_QAM_RD_RSV_7__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_7__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_7_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_7_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_7_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_7_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_8__A 0x831FC6 +#define SCU_RAM_QAM_RD_RSV_8__W 16 +#define SCU_RAM_QAM_RD_RSV_8__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_8__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_8_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_8_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_8_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_8_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_9__A 0x831FC7 +#define SCU_RAM_QAM_RD_RSV_9__W 16 +#define SCU_RAM_QAM_RD_RSV_9__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_9__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_9_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_9_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_9_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_9_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_10__A 0x831FC8 +#define SCU_RAM_QAM_RD_RSV_10__W 16 +#define SCU_RAM_QAM_RD_RSV_10__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_10__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_10_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_10_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_10_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_10_BIT__PRE 0x0 + +#define SCU_RAM_QAM_AGC_TPOW_OFFS__A 0x831FC9 +#define SCU_RAM_QAM_AGC_TPOW_OFFS__W 16 +#define SCU_RAM_QAM_AGC_TPOW_OFFS__M 0xFFFF +#define SCU_RAM_QAM_AGC_TPOW_OFFS__PRE 0x0 + +#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__B 0 +#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__W 16 +#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__M 0xFFFF +#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_STATE__A 0x831FCA +#define SCU_RAM_QAM_FSM_STATE__W 4 +#define SCU_RAM_QAM_FSM_STATE__M 0xF +#define SCU_RAM_QAM_FSM_STATE__PRE 0x0 + +#define SCU_RAM_QAM_FSM_STATE_BIT__B 0 +#define SCU_RAM_QAM_FSM_STATE_BIT__W 4 +#define SCU_RAM_QAM_FSM_STATE_BIT__M 0xF +#define SCU_RAM_QAM_FSM_STATE_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_AMP 0x0 +#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_RATE 0x1 +#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_FREQ 0x2 +#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_UPRIGHT 0x3 +#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_PHASE 0x4 +#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING_PHNOISE 0x5 +#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING 0x6 +#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING_BURST 0x7 + +#define SCU_RAM_QAM_FSM_STATE_NEW__A 0x831FCB +#define SCU_RAM_QAM_FSM_STATE_NEW__W 4 +#define SCU_RAM_QAM_FSM_STATE_NEW__M 0xF +#define SCU_RAM_QAM_FSM_STATE_NEW__PRE 0x0 + +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__B 0 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__W 4 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__M 0xF +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__PRE 0x0 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_AMP 0x0 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_RATE 0x1 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_FREQ 0x2 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_UPRIGHT 0x3 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_PHASE 0x4 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING_PHNOISE 0x5 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING 0x6 +#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING_BURST 0x7 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS__A 0x831FCC +#define SCU_RAM_QAM_FSM_LOCK_FLAGS__W 9 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS__M 0x1FF +#define SCU_RAM_QAM_FSM_LOCK_FLAGS__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__B 0 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__M 0x1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__B 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__M 0x2 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__B 2 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__M 0x4 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__B 3 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__M 0x8 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__B 4 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__M 0x10 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__B 5 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__M 0x20 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__B 6 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__M 0x40 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__B 7 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__M 0x80 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__PRE 0x0 + +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__B 8 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__W 1 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__M 0x100 +#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RATE_VARIATION__A 0x831FCD +#define SCU_RAM_QAM_FSM_RATE_VARIATION__W 16 +#define SCU_RAM_QAM_FSM_RATE_VARIATION__M 0xFFFF +#define SCU_RAM_QAM_FSM_RATE_VARIATION__PRE 0x0 + +#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__B 0 +#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__W 16 +#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__PRE 0x0 + +#define SCU_RAM_QAM_FSM_FREQ_VARIATION__A 0x831FCE +#define SCU_RAM_QAM_FSM_FREQ_VARIATION__W 16 +#define SCU_RAM_QAM_FSM_FREQ_VARIATION__M 0xFFFF +#define SCU_RAM_QAM_FSM_FREQ_VARIATION__PRE 0x0 + +#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__B 0 +#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__W 16 +#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__M 0xFFFF +#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__PRE 0x0 + +#define SCU_RAM_QAM_ERR_STATE__A 0x831FCF +#define SCU_RAM_QAM_ERR_STATE__W 4 +#define SCU_RAM_QAM_ERR_STATE__M 0xF +#define SCU_RAM_QAM_ERR_STATE__PRE 0x0 + +#define SCU_RAM_QAM_ERR_STATE_BIT__B 0 +#define SCU_RAM_QAM_ERR_STATE_BIT__W 4 +#define SCU_RAM_QAM_ERR_STATE_BIT__M 0xF +#define SCU_RAM_QAM_ERR_STATE_BIT__PRE 0x0 +#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_AMP 0x0 +#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_RATE 0x1 +#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_FREQ 0x2 +#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_UPRIGHT 0x3 +#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_PHASE 0x4 +#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING_PHNOISE 0x5 +#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING 0x6 +#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING_BURST 0x7 + +#define SCU_RAM_QAM_ERR_LOCK_FLAGS__A 0x831FD0 +#define SCU_RAM_QAM_ERR_LOCK_FLAGS__W 9 +#define SCU_RAM_QAM_ERR_LOCK_FLAGS__M 0x1FF +#define SCU_RAM_QAM_ERR_LOCK_FLAGS__PRE 0x0 + +#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__B 0 +#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__W 1 +#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__M 0x1 +#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__PRE 0x0 + +#define SCU_RAM_QAM_EQ_LOCK__A 0x831FD1 +#define SCU_RAM_QAM_EQ_LOCK__W 1 +#define SCU_RAM_QAM_EQ_LOCK__M 0x1 +#define SCU_RAM_QAM_EQ_LOCK__PRE 0x0 + +#define SCU_RAM_QAM_EQ_LOCK_BIT__B 0 +#define SCU_RAM_QAM_EQ_LOCK_BIT__W 1 +#define SCU_RAM_QAM_EQ_LOCK_BIT__M 0x1 +#define SCU_RAM_QAM_EQ_LOCK_BIT__PRE 0x0 + +#define SCU_RAM_QAM_EQ_STATE__A 0x831FD2 +#define SCU_RAM_QAM_EQ_STATE__W 16 +#define SCU_RAM_QAM_EQ_STATE__M 0xFFFF +#define SCU_RAM_QAM_EQ_STATE__PRE 0x0 + +#define SCU_RAM_QAM_EQ_STATE_BIT__B 0 +#define SCU_RAM_QAM_EQ_STATE_BIT__W 16 +#define SCU_RAM_QAM_EQ_STATE_BIT__M 0xFFFF +#define SCU_RAM_QAM_EQ_STATE_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_0__A 0x831FD3 +#define SCU_RAM_QAM_RD_RSV_0__W 16 +#define SCU_RAM_QAM_RD_RSV_0__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_0__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_0_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_0_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_0_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_0_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_1__A 0x831FD4 +#define SCU_RAM_QAM_RD_RSV_1__W 16 +#define SCU_RAM_QAM_RD_RSV_1__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_1__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_1_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_1_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_1_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_1_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_2__A 0x831FD5 +#define SCU_RAM_QAM_RD_RSV_2__W 16 +#define SCU_RAM_QAM_RD_RSV_2__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_2__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_2_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_2_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_2_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_2_BIT__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_3__A 0x831FD6 +#define SCU_RAM_QAM_RD_RSV_3__W 16 +#define SCU_RAM_QAM_RD_RSV_3__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_3__PRE 0x0 + +#define SCU_RAM_QAM_RD_RSV_3_BIT__B 0 +#define SCU_RAM_QAM_RD_RSV_3_BIT__W 16 +#define SCU_RAM_QAM_RD_RSV_3_BIT__M 0xFFFF +#define SCU_RAM_QAM_RD_RSV_3_BIT__PRE 0x0 + +#define SCU_RAM_VSB_CTL_MODE__A 0x831FD7 +#define SCU_RAM_VSB_CTL_MODE__W 2 +#define SCU_RAM_VSB_CTL_MODE__M 0x3 +#define SCU_RAM_VSB_CTL_MODE__PRE 0x0 + +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__B 0 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__W 1 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__M 0x1 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__PRE 0x0 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC_OFF 0x0 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC_ON 0x1 + +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__B 1 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__W 1 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__M 0x2 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__PRE 0x0 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON_OFF 0x0 +#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON_ON 0x2 + +#define SCU_RAM_VSB_NOTCH_THRESHOLD__A 0x831FD8 +#define SCU_RAM_VSB_NOTCH_THRESHOLD__W 16 +#define SCU_RAM_VSB_NOTCH_THRESHOLD__M 0xFFFF +#define SCU_RAM_VSB_NOTCH_THRESHOLD__PRE 0x0 + +#define SCU_RAM_VSB_RSV_0__A 0x831FD9 +#define SCU_RAM_VSB_RSV_0__W 16 +#define SCU_RAM_VSB_RSV_0__M 0xFFFF +#define SCU_RAM_VSB_RSV_0__PRE 0x0 + +#define SCU_RAM_VSB_RSV_1__A 0x831FDA +#define SCU_RAM_VSB_RSV_1__W 16 +#define SCU_RAM_VSB_RSV_1__M 0xFFFF +#define SCU_RAM_VSB_RSV_1__PRE 0x0 + +#define SCU_RAM_VSB_RSV_2__A 0x831FDB +#define SCU_RAM_VSB_RSV_2__W 16 +#define SCU_RAM_VSB_RSV_2__M 0xFFFF +#define SCU_RAM_VSB_RSV_2__PRE 0x0 + +#define SCU_RAM_VSB_RSV_3__A 0x831FDC +#define SCU_RAM_VSB_RSV_3__W 16 +#define SCU_RAM_VSB_RSV_3__M 0xFFFF +#define SCU_RAM_VSB_RSV_3__PRE 0x0 + +#define SCU_RAM_VSB_RSV_4__A 0x831FDD +#define SCU_RAM_VSB_RSV_4__W 16 +#define SCU_RAM_VSB_RSV_4__M 0xFFFF +#define SCU_RAM_VSB_RSV_4__PRE 0x0 + +#define SCU_RAM_VSB_RSV_5__A 0x831FDE +#define SCU_RAM_VSB_RSV_5__W 16 +#define SCU_RAM_VSB_RSV_5__M 0xFFFF +#define SCU_RAM_VSB_RSV_5__PRE 0x0 + +#define SCU_RAM_VSB_RSV_6__A 0x831FDF +#define SCU_RAM_VSB_RSV_6__W 16 +#define SCU_RAM_VSB_RSV_6__M 0xFFFF +#define SCU_RAM_VSB_RSV_6__PRE 0x0 + +#define SCU_RAM_VSB_RSV_7__A 0x831FE0 +#define SCU_RAM_VSB_RSV_7__W 16 +#define SCU_RAM_VSB_RSV_7__M 0xFFFF +#define SCU_RAM_VSB_RSV_7__PRE 0x0 + +#define SCU_RAM_VSB_RSV_8__A 0x831FE1 +#define SCU_RAM_VSB_RSV_8__W 16 +#define SCU_RAM_VSB_RSV_8__M 0xFFFF +#define SCU_RAM_VSB_RSV_8__PRE 0x0 + +#define SCU_RAM_VSB_RSV_9__A 0x831FE2 +#define SCU_RAM_VSB_RSV_9__W 16 +#define SCU_RAM_VSB_RSV_9__M 0xFFFF +#define SCU_RAM_VSB_RSV_9__PRE 0x0 + +#define SCU_RAM_VSB_RSV_10__A 0x831FE3 +#define SCU_RAM_VSB_RSV_10__W 16 +#define SCU_RAM_VSB_RSV_10__M 0xFFFF +#define SCU_RAM_VSB_RSV_10__PRE 0x0 + +#define SCU_RAM_VSB_RSV_11__A 0x831FE4 +#define SCU_RAM_VSB_RSV_11__W 16 +#define SCU_RAM_VSB_RSV_11__M 0xFFFF +#define SCU_RAM_VSB_RSV_11__PRE 0x0 + +#define SCU_RAM_VSB_RSV_12__A 0x831FE5 +#define SCU_RAM_VSB_RSV_12__W 16 +#define SCU_RAM_VSB_RSV_12__M 0xFFFF +#define SCU_RAM_VSB_RSV_12__PRE 0x0 + +#define SCU_RAM_VSB_RSV_13__A 0x831FE6 +#define SCU_RAM_VSB_RSV_13__W 16 +#define SCU_RAM_VSB_RSV_13__M 0xFFFF +#define SCU_RAM_VSB_RSV_13__PRE 0x0 + +#define SCU_RAM_VSB_AGC_POW_TGT__A 0x831FE7 +#define SCU_RAM_VSB_AGC_POW_TGT__W 15 +#define SCU_RAM_VSB_AGC_POW_TGT__M 0x7FFF +#define SCU_RAM_VSB_AGC_POW_TGT__PRE 0x0 + +#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__A 0x831FE8 +#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__W 8 +#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__M 0xFF +#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__PRE 0x0 + +#define SCU_RAM_VSB_FIELD_NUMBER__A 0x831FE9 +#define SCU_RAM_VSB_FIELD_NUMBER__W 9 +#define SCU_RAM_VSB_FIELD_NUMBER__M 0x1FF +#define SCU_RAM_VSB_FIELD_NUMBER__PRE 0x0 + +#define SCU_RAM_VSB_SEGMENT_NUMBER__A 0x831FEA +#define SCU_RAM_VSB_SEGMENT_NUMBER__W 10 +#define SCU_RAM_VSB_SEGMENT_NUMBER__M 0x3FF +#define SCU_RAM_VSB_SEGMENT_NUMBER__PRE 0x0 + +#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB +#define SCU_RAM_DRIVER_VER_HI__W 16 +#define SCU_RAM_DRIVER_VER_HI__M 0xFFFF +#define SCU_RAM_DRIVER_VER_HI__PRE 0x0 + +#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC +#define SCU_RAM_DRIVER_VER_LO__W 16 +#define SCU_RAM_DRIVER_VER_LO__M 0xFFFF +#define SCU_RAM_DRIVER_VER_LO__PRE 0x0 + +#define SCU_RAM_PARAM_15__A 0x831FED +#define SCU_RAM_PARAM_15__W 16 +#define SCU_RAM_PARAM_15__M 0xFFFF +#define SCU_RAM_PARAM_15__PRE 0x0 + +#define SCU_RAM_PARAM_14__A 0x831FEE +#define SCU_RAM_PARAM_14__W 16 +#define SCU_RAM_PARAM_14__M 0xFFFF +#define SCU_RAM_PARAM_14__PRE 0x0 + +#define SCU_RAM_PARAM_13__A 0x831FEF +#define SCU_RAM_PARAM_13__W 16 +#define SCU_RAM_PARAM_13__M 0xFFFF +#define SCU_RAM_PARAM_13__PRE 0x0 + +#define SCU_RAM_PARAM_12__A 0x831FF0 +#define SCU_RAM_PARAM_12__W 16 +#define SCU_RAM_PARAM_12__M 0xFFFF +#define SCU_RAM_PARAM_12__PRE 0x0 + +#define SCU_RAM_PARAM_11__A 0x831FF1 +#define SCU_RAM_PARAM_11__W 16 +#define SCU_RAM_PARAM_11__M 0xFFFF +#define SCU_RAM_PARAM_11__PRE 0x0 + +#define SCU_RAM_PARAM_10__A 0x831FF2 +#define SCU_RAM_PARAM_10__W 16 +#define SCU_RAM_PARAM_10__M 0xFFFF +#define SCU_RAM_PARAM_10__PRE 0x0 + +#define SCU_RAM_PARAM_9__A 0x831FF3 +#define SCU_RAM_PARAM_9__W 16 +#define SCU_RAM_PARAM_9__M 0xFFFF +#define SCU_RAM_PARAM_9__PRE 0x0 + +#define SCU_RAM_PARAM_8__A 0x831FF4 +#define SCU_RAM_PARAM_8__W 16 +#define SCU_RAM_PARAM_8__M 0xFFFF +#define SCU_RAM_PARAM_8__PRE 0x0 + +#define SCU_RAM_PARAM_7__A 0x831FF5 +#define SCU_RAM_PARAM_7__W 16 +#define SCU_RAM_PARAM_7__M 0xFFFF +#define SCU_RAM_PARAM_7__PRE 0x0 + +#define SCU_RAM_PARAM_6__A 0x831FF6 +#define SCU_RAM_PARAM_6__W 16 +#define SCU_RAM_PARAM_6__M 0xFFFF +#define SCU_RAM_PARAM_6__PRE 0x0 + +#define SCU_RAM_PARAM_5__A 0x831FF7 +#define SCU_RAM_PARAM_5__W 16 +#define SCU_RAM_PARAM_5__M 0xFFFF +#define SCU_RAM_PARAM_5__PRE 0x0 + +#define SCU_RAM_PARAM_4__A 0x831FF8 +#define SCU_RAM_PARAM_4__W 16 +#define SCU_RAM_PARAM_4__M 0xFFFF +#define SCU_RAM_PARAM_4__PRE 0x0 + +#define SCU_RAM_PARAM_3__A 0x831FF9 +#define SCU_RAM_PARAM_3__W 16 +#define SCU_RAM_PARAM_3__M 0xFFFF +#define SCU_RAM_PARAM_3__PRE 0x0 + +#define SCU_RAM_PARAM_2__A 0x831FFA +#define SCU_RAM_PARAM_2__W 16 +#define SCU_RAM_PARAM_2__M 0xFFFF +#define SCU_RAM_PARAM_2__PRE 0x0 + +#define SCU_RAM_PARAM_1__A 0x831FFB +#define SCU_RAM_PARAM_1__W 16 +#define SCU_RAM_PARAM_1__M 0xFFFF +#define SCU_RAM_PARAM_1__PRE 0x0 +#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NOT_LOCKED 0x0 +#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_DEMOD_LOCKED 0x4000 +#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_LOCKED 0x8000 +#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NEVER_LOCK 0xC000 + +#define SCU_RAM_PARAM_0__A 0x831FFC +#define SCU_RAM_PARAM_0__W 16 +#define SCU_RAM_PARAM_0__M 0xFFFF +#define SCU_RAM_PARAM_0__PRE 0x0 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_MN_STANDARD 0x2 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_B_STANDARD 0x103 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_G_STANDARD 0x3 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_DK_STANDARD 0x4 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_L_STANDARD 0x9 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_LP_STANDARD 0x109 +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_I_STANDARD 0xA +#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_FM_STANDARD 0x40 +#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_A 0x0 +#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_B 0x1 +#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_C 0x2 +#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_D 0x3 +#define SCU_RAM_PARAM_0_RESULT_OK 0x0 +#define SCU_RAM_PARAM_0_RESULT_UNKCMD 0xFFFF +#define SCU_RAM_PARAM_0_RESULT_UNKSTD 0xFFFE +#define SCU_RAM_PARAM_0_RESULT_INVPAR 0xFFFD +#define SCU_RAM_PARAM_0_RESULT_SIZE 0xFFFC + +#define SCU_RAM_COMMAND__A 0x831FFD +#define SCU_RAM_COMMAND__W 16 +#define SCU_RAM_COMMAND__M 0xFFFF +#define SCU_RAM_COMMAND__PRE 0x0 +#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1 +#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2 +#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3 +#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4 +#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5 +#define SCU_RAM_COMMAND_CMD_DEMOD_GET_PARAM 0x6 +#define SCU_RAM_COMMAND_CMD_DEMOD_HOLD 0x7 +#define SCU_RAM_COMMAND_CMD_DEMOD_RESUME 0x8 +#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9 +#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_ACTIVATE 0x80 +#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_INACTIVATE 0x81 +#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_SIGNAL 0x82 +#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_MONITOR 0x83 +#define SCU_RAM_COMMAND_CMD_STD_QAM_TSK_ENABLE 0x84 +#define SCU_RAM_COMMAND_CMD_STD_QAM_FSM_SET_STATE 0x85 +#define SCU_RAM_COMMAND_CMD_DEBUG_GET_IRQ_REGS 0x80 +#define SCU_RAM_COMMAND_CMD_DEBUG_HTOL 0x81 +#define SCU_RAM_COMMAND_CMD_DEBUG_GET_STACK_POINTER 0x82 +#define SCU_RAM_COMMAND_CMD_DEBUG_START_STACK_CHECK 0x83 +#define SCU_RAM_COMMAND_CMD_DEBUG_STOP_STACK_CHECK 0x84 +#define SCU_RAM_COMMAND_CMD_ADMIN_NOP 0xFF +#define SCU_RAM_COMMAND_CMD_ADMIN_GET_VERSION 0xFE +#define SCU_RAM_COMMAND_CMD_ADMIN_GET_JTAG_VERSION 0xFD +#define SCU_RAM_COMMAND_CMD_AUX_SCU_ATOMIC_ACCESS 0xC0 + +#define SCU_RAM_COMMAND_STANDARD__B 8 +#define SCU_RAM_COMMAND_STANDARD__W 8 +#define SCU_RAM_COMMAND_STANDARD__M 0xFF00 +#define SCU_RAM_COMMAND_STANDARD__PRE 0x0 +#define SCU_RAM_COMMAND_STANDARD_ATV 0x100 +#define SCU_RAM_COMMAND_STANDARD_QAM 0x200 +#define SCU_RAM_COMMAND_STANDARD_VSB 0x300 +#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400 +#define SCU_RAM_COMMAND_STANDARD_OOB 0x8000 +#define SCU_RAM_COMMAND_STANDARD_TOP 0xFF00 + +#define SCU_RAM_VERSION_HI__A 0x831FFE +#define SCU_RAM_VERSION_HI__W 16 +#define SCU_RAM_VERSION_HI__M 0xFFFF +#define SCU_RAM_VERSION_HI__PRE 0x0 + +#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__B 12 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__W 4 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__M 0xF000 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__PRE 0x0 + +#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__B 8 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__W 4 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__M 0xF00 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__PRE 0x0 + +#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__B 4 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__W 4 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__M 0xF0 +#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__PRE 0x0 + +#define SCU_RAM_VERSION_HI_VER_MINOR_N1__B 0 +#define SCU_RAM_VERSION_HI_VER_MINOR_N1__W 4 +#define SCU_RAM_VERSION_HI_VER_MINOR_N1__M 0xF +#define SCU_RAM_VERSION_HI_VER_MINOR_N1__PRE 0x0 + +#define SCU_RAM_VERSION_LO__A 0x831FFF +#define SCU_RAM_VERSION_LO__W 16 +#define SCU_RAM_VERSION_LO__M 0xFFFF +#define SCU_RAM_VERSION_LO__PRE 0x0 + +#define SCU_RAM_VERSION_LO_VER_PATCH_N4__B 12 +#define SCU_RAM_VERSION_LO_VER_PATCH_N4__W 4 +#define SCU_RAM_VERSION_LO_VER_PATCH_N4__M 0xF000 +#define SCU_RAM_VERSION_LO_VER_PATCH_N4__PRE 0x0 + +#define SCU_RAM_VERSION_LO_VER_PATCH_N3__B 8 +#define SCU_RAM_VERSION_LO_VER_PATCH_N3__W 4 +#define SCU_RAM_VERSION_LO_VER_PATCH_N3__M 0xF00 +#define SCU_RAM_VERSION_LO_VER_PATCH_N3__PRE 0x0 + +#define SCU_RAM_VERSION_LO_VER_PATCH_N2__B 4 +#define SCU_RAM_VERSION_LO_VER_PATCH_N2__W 4 +#define SCU_RAM_VERSION_LO_VER_PATCH_N2__M 0xF0 +#define SCU_RAM_VERSION_LO_VER_PATCH_N2__PRE 0x0 + +#define SCU_RAM_VERSION_LO_VER_PATCH_N1__B 0 +#define SCU_RAM_VERSION_LO_VER_PATCH_N1__W 4 +#define SCU_RAM_VERSION_LO_VER_PATCH_N1__M 0xF +#define SCU_RAM_VERSION_LO_VER_PATCH_N1__PRE 0x0 + +#define SIO_COMM_EXEC__A 0x400000 +#define SIO_COMM_EXEC__W 2 +#define SIO_COMM_EXEC__M 0x3 +#define SIO_COMM_EXEC__PRE 0x0 +#define SIO_COMM_EXEC_STOP 0x0 +#define SIO_COMM_EXEC_ACTIVE 0x1 +#define SIO_COMM_EXEC_HOLD 0x2 + +#define SIO_COMM_STATE__A 0x400001 +#define SIO_COMM_STATE__W 16 +#define SIO_COMM_STATE__M 0xFFFF +#define SIO_COMM_STATE__PRE 0x0 +#define SIO_COMM_MB__A 0x400002 +#define SIO_COMM_MB__W 16 +#define SIO_COMM_MB__M 0xFFFF +#define SIO_COMM_MB__PRE 0x0 +#define SIO_COMM_INT_REQ__A 0x400003 +#define SIO_COMM_INT_REQ__W 16 +#define SIO_COMM_INT_REQ__M 0xFFFF +#define SIO_COMM_INT_REQ__PRE 0x0 + +#define SIO_COMM_INT_REQ_HI_REQ__B 0 +#define SIO_COMM_INT_REQ_HI_REQ__W 1 +#define SIO_COMM_INT_REQ_HI_REQ__M 0x1 +#define SIO_COMM_INT_REQ_HI_REQ__PRE 0x0 + +#define SIO_COMM_INT_REQ_SA_REQ__B 1 +#define SIO_COMM_INT_REQ_SA_REQ__W 1 +#define SIO_COMM_INT_REQ_SA_REQ__M 0x2 +#define SIO_COMM_INT_REQ_SA_REQ__PRE 0x0 + +#define SIO_COMM_INT_STA__A 0x400005 +#define SIO_COMM_INT_STA__W 16 +#define SIO_COMM_INT_STA__M 0xFFFF +#define SIO_COMM_INT_STA__PRE 0x0 +#define SIO_COMM_INT_MSK__A 0x400006 +#define SIO_COMM_INT_MSK__W 16 +#define SIO_COMM_INT_MSK__M 0xFFFF +#define SIO_COMM_INT_MSK__PRE 0x0 +#define SIO_COMM_INT_STM__A 0x400007 +#define SIO_COMM_INT_STM__W 16 +#define SIO_COMM_INT_STM__M 0xFFFF +#define SIO_COMM_INT_STM__PRE 0x0 + +#define SIO_TOP_COMM_EXEC__A 0x410000 +#define SIO_TOP_COMM_EXEC__W 2 +#define SIO_TOP_COMM_EXEC__M 0x3 +#define SIO_TOP_COMM_EXEC__PRE 0x0 +#define SIO_TOP_COMM_EXEC_STOP 0x0 +#define SIO_TOP_COMM_EXEC_ACTIVE 0x1 +#define SIO_TOP_COMM_EXEC_HOLD 0x2 + +#define SIO_TOP_COMM_KEY__A 0x41000F +#define SIO_TOP_COMM_KEY__W 16 +#define SIO_TOP_COMM_KEY__M 0xFFFF +#define SIO_TOP_COMM_KEY__PRE 0x0 +#define SIO_TOP_COMM_KEY_KEY 0xFABA + +#define SIO_TOP_JTAGID_LO__A 0x410012 +#define SIO_TOP_JTAGID_LO__W 16 +#define SIO_TOP_JTAGID_LO__M 0xFFFF +#define SIO_TOP_JTAGID_LO__PRE 0x0 + +#define SIO_TOP_JTAGID_HI__A 0x410013 +#define SIO_TOP_JTAGID_HI__W 16 +#define SIO_TOP_JTAGID_HI__M 0xFFFF +#define SIO_TOP_JTAGID_HI__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_FLG_SMM__A 0x420010 +#define SIO_HI_RA_RAM_S0_FLG_SMM__W 1 +#define SIO_HI_RA_RAM_S0_FLG_SMM__M 0x1 +#define SIO_HI_RA_RAM_S0_FLG_SMM__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_DEV_ID__A 0x420011 +#define SIO_HI_RA_RAM_S0_DEV_ID__W 7 +#define SIO_HI_RA_RAM_S0_DEV_ID__M 0x7F +#define SIO_HI_RA_RAM_S0_DEV_ID__PRE 0x52 + +#define SIO_HI_RA_RAM_S0_FLG_CRC__A 0x420012 +#define SIO_HI_RA_RAM_S0_FLG_CRC__W 1 +#define SIO_HI_RA_RAM_S0_FLG_CRC__M 0x1 +#define SIO_HI_RA_RAM_S0_FLG_CRC__PRE 0x0 +#define SIO_HI_RA_RAM_S0_FLG_ACC__A 0x420013 +#define SIO_HI_RA_RAM_S0_FLG_ACC__W 4 +#define SIO_HI_RA_RAM_S0_FLG_ACC__M 0xF +#define SIO_HI_RA_RAM_S0_FLG_ACC__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__B 0 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__W 2 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__M 0x3 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__B 2 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__W 1 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__M 0x4 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__B 3 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__W 1 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__M 0x8 +#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_STATE__A 0x420014 +#define SIO_HI_RA_RAM_S0_STATE__W 1 +#define SIO_HI_RA_RAM_S0_STATE__M 0x1 +#define SIO_HI_RA_RAM_S0_STATE__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__B 0 +#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__W 1 +#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__M 0x1 +#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_BLK_BNK__A 0x420015 +#define SIO_HI_RA_RAM_S0_BLK_BNK__W 12 +#define SIO_HI_RA_RAM_S0_BLK_BNK__M 0xFFF +#define SIO_HI_RA_RAM_S0_BLK_BNK__PRE 0x82 + +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__B 0 +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__W 6 +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__M 0x3F +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__PRE 0x2 + +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__B 6 +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__W 6 +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__M 0xFC0 +#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__PRE 0x80 + +#define SIO_HI_RA_RAM_S0_ADDR__A 0x420016 +#define SIO_HI_RA_RAM_S0_ADDR__W 16 +#define SIO_HI_RA_RAM_S0_ADDR__M 0xFFFF +#define SIO_HI_RA_RAM_S0_ADDR__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__B 0 +#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__W 16 +#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__M 0xFFFF +#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_CRC__A 0x420017 +#define SIO_HI_RA_RAM_S0_CRC__W 16 +#define SIO_HI_RA_RAM_S0_CRC__M 0xFFFF +#define SIO_HI_RA_RAM_S0_CRC__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_BUFFER__A 0x420018 +#define SIO_HI_RA_RAM_S0_BUFFER__W 16 +#define SIO_HI_RA_RAM_S0_BUFFER__M 0xFFFF +#define SIO_HI_RA_RAM_S0_BUFFER__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_RMWBUF__A 0x420019 +#define SIO_HI_RA_RAM_S0_RMWBUF__W 16 +#define SIO_HI_RA_RAM_S0_RMWBUF__M 0xFFFF +#define SIO_HI_RA_RAM_S0_RMWBUF__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_FLG_VB__A 0x42001A +#define SIO_HI_RA_RAM_S0_FLG_VB__W 1 +#define SIO_HI_RA_RAM_S0_FLG_VB__M 0x1 +#define SIO_HI_RA_RAM_S0_FLG_VB__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_TEMP0__A 0x42001B +#define SIO_HI_RA_RAM_S0_TEMP0__W 16 +#define SIO_HI_RA_RAM_S0_TEMP0__M 0xFFFF +#define SIO_HI_RA_RAM_S0_TEMP0__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_TEMP1__A 0x42001C +#define SIO_HI_RA_RAM_S0_TEMP1__W 16 +#define SIO_HI_RA_RAM_S0_TEMP1__M 0xFFFF +#define SIO_HI_RA_RAM_S0_TEMP1__PRE 0x0 + +#define SIO_HI_RA_RAM_S0_OFFSET__A 0x42001D +#define SIO_HI_RA_RAM_S0_OFFSET__W 16 +#define SIO_HI_RA_RAM_S0_OFFSET__M 0xFFFF +#define SIO_HI_RA_RAM_S0_OFFSET__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_FLG_SMM__A 0x420020 +#define SIO_HI_RA_RAM_S1_FLG_SMM__W 1 +#define SIO_HI_RA_RAM_S1_FLG_SMM__M 0x1 +#define SIO_HI_RA_RAM_S1_FLG_SMM__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_DEV_ID__A 0x420021 +#define SIO_HI_RA_RAM_S1_DEV_ID__W 7 +#define SIO_HI_RA_RAM_S1_DEV_ID__M 0x7F +#define SIO_HI_RA_RAM_S1_DEV_ID__PRE 0x52 + +#define SIO_HI_RA_RAM_S1_FLG_CRC__A 0x420022 +#define SIO_HI_RA_RAM_S1_FLG_CRC__W 1 +#define SIO_HI_RA_RAM_S1_FLG_CRC__M 0x1 +#define SIO_HI_RA_RAM_S1_FLG_CRC__PRE 0x0 +#define SIO_HI_RA_RAM_S1_FLG_ACC__A 0x420023 +#define SIO_HI_RA_RAM_S1_FLG_ACC__W 4 +#define SIO_HI_RA_RAM_S1_FLG_ACC__M 0xF +#define SIO_HI_RA_RAM_S1_FLG_ACC__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__B 0 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__W 2 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__M 0x3 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__B 2 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__W 1 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__M 0x4 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__B 3 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__W 1 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__M 0x8 +#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_STATE__A 0x420024 +#define SIO_HI_RA_RAM_S1_STATE__W 1 +#define SIO_HI_RA_RAM_S1_STATE__M 0x1 +#define SIO_HI_RA_RAM_S1_STATE__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__B 0 +#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__W 1 +#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__M 0x1 +#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_BLK_BNK__A 0x420025 +#define SIO_HI_RA_RAM_S1_BLK_BNK__W 12 +#define SIO_HI_RA_RAM_S1_BLK_BNK__M 0xFFF +#define SIO_HI_RA_RAM_S1_BLK_BNK__PRE 0x82 + +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__B 0 +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__W 6 +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__M 0x3F +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__PRE 0x2 + +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__B 6 +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__W 6 +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__M 0xFC0 +#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__PRE 0x80 + +#define SIO_HI_RA_RAM_S1_ADDR__A 0x420026 +#define SIO_HI_RA_RAM_S1_ADDR__W 16 +#define SIO_HI_RA_RAM_S1_ADDR__M 0xFFFF +#define SIO_HI_RA_RAM_S1_ADDR__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__B 0 +#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__W 16 +#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__M 0xFFFF +#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_CRC__A 0x420027 +#define SIO_HI_RA_RAM_S1_CRC__W 16 +#define SIO_HI_RA_RAM_S1_CRC__M 0xFFFF +#define SIO_HI_RA_RAM_S1_CRC__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_BUFFER__A 0x420028 +#define SIO_HI_RA_RAM_S1_BUFFER__W 16 +#define SIO_HI_RA_RAM_S1_BUFFER__M 0xFFFF +#define SIO_HI_RA_RAM_S1_BUFFER__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_RMWBUF__A 0x420029 +#define SIO_HI_RA_RAM_S1_RMWBUF__W 16 +#define SIO_HI_RA_RAM_S1_RMWBUF__M 0xFFFF +#define SIO_HI_RA_RAM_S1_RMWBUF__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_FLG_VB__A 0x42002A +#define SIO_HI_RA_RAM_S1_FLG_VB__W 1 +#define SIO_HI_RA_RAM_S1_FLG_VB__M 0x1 +#define SIO_HI_RA_RAM_S1_FLG_VB__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_TEMP0__A 0x42002B +#define SIO_HI_RA_RAM_S1_TEMP0__W 16 +#define SIO_HI_RA_RAM_S1_TEMP0__M 0xFFFF +#define SIO_HI_RA_RAM_S1_TEMP0__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_TEMP1__A 0x42002C +#define SIO_HI_RA_RAM_S1_TEMP1__W 16 +#define SIO_HI_RA_RAM_S1_TEMP1__M 0xFFFF +#define SIO_HI_RA_RAM_S1_TEMP1__PRE 0x0 + +#define SIO_HI_RA_RAM_S1_OFFSET__A 0x42002D +#define SIO_HI_RA_RAM_S1_OFFSET__W 16 +#define SIO_HI_RA_RAM_S1_OFFSET__M 0xFFFF +#define SIO_HI_RA_RAM_S1_OFFSET__PRE 0x0 +#define SIO_HI_RA_RAM_SEMA__A 0x420030 +#define SIO_HI_RA_RAM_SEMA__W 1 +#define SIO_HI_RA_RAM_SEMA__M 0x1 +#define SIO_HI_RA_RAM_SEMA__PRE 0x0 +#define SIO_HI_RA_RAM_SEMA_FREE 0x0 +#define SIO_HI_RA_RAM_SEMA_BUSY 0x1 + +#define SIO_HI_RA_RAM_RES__A 0x420031 +#define SIO_HI_RA_RAM_RES__W 3 +#define SIO_HI_RA_RAM_RES__M 0x7 +#define SIO_HI_RA_RAM_RES__PRE 0x0 +#define SIO_HI_RA_RAM_RES_OK 0x0 +#define SIO_HI_RA_RAM_RES_ERROR 0x1 +#define SIO_HI_RA_RAM_RES_I2C_START_FOUND 0x1 +#define SIO_HI_RA_RAM_RES_I2C_STOP_FOUND 0x2 +#define SIO_HI_RA_RAM_RES_I2C_ARB_LOST 0x3 +#define SIO_HI_RA_RAM_RES_I2C_ERROR 0x4 + +#define SIO_HI_RA_RAM_CMD__A 0x420032 +#define SIO_HI_RA_RAM_CMD__W 4 +#define SIO_HI_RA_RAM_CMD__M 0xF +#define SIO_HI_RA_RAM_CMD__PRE 0x0 +#define SIO_HI_RA_RAM_CMD_NULL 0x0 +#define SIO_HI_RA_RAM_CMD_UIO 0x1 +#define SIO_HI_RA_RAM_CMD_RESET 0x2 +#define SIO_HI_RA_RAM_CMD_CONFIG 0x3 +#define SIO_HI_RA_RAM_CMD_INTERNAL_TRANSFER 0x4 +#define SIO_HI_RA_RAM_CMD_I2C_TRANSMIT 0x5 +#define SIO_HI_RA_RAM_CMD_EXEC 0x6 +#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7 +#define SIO_HI_RA_RAM_CMD_ATOMIC_COPY 0x8 + +#define SIO_HI_RA_RAM_PAR_1__A 0x420033 +#define SIO_HI_RA_RAM_PAR_1__W 16 +#define SIO_HI_RA_RAM_PAR_1__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_1__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_1_PAR1__B 0 +#define SIO_HI_RA_RAM_PAR_1_PAR1__W 16 +#define SIO_HI_RA_RAM_PAR_1_PAR1__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_1_PAR1__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945 + +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__B 0 +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__W 6 +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__M 0x3F +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__B 6 +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__W 6 +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__M 0xFC0 +#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__B 0 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__W 1 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__M 0x1 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__B 1 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__W 1 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__M 0x2 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE_DISABLE 0x0 +#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE_ENABLE 0x2 + +#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__B 0 +#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__W 10 +#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__M 0x3FF +#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__B 0 +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__W 6 +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__M 0x3F +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__B 6 +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__W 6 +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__M 0xFC0 +#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_2__A 0x420034 +#define SIO_HI_RA_RAM_PAR_2__W 16 +#define SIO_HI_RA_RAM_PAR_2__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_2__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_2_PAR2__B 0 +#define SIO_HI_RA_RAM_PAR_2_PAR2__W 16 +#define SIO_HI_RA_RAM_PAR_2_PAR2__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_2_PAR2__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__B 0 +#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__W 7 +#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F +#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__PRE 0x25 + +#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__B 0 +#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__W 16 +#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__B 0 +#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__W 16 +#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__B 2 +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__W 1 +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__M 0x4 +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0 +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4 + +#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__B 0 +#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__W 16 +#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_3__A 0x420035 +#define SIO_HI_RA_RAM_PAR_3__W 16 +#define SIO_HI_RA_RAM_PAR_3__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_3__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_3_PAR3__B 0 +#define SIO_HI_RA_RAM_PAR_3_PAR3__W 16 +#define SIO_HI_RA_RAM_PAR_3_PAR3__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_3_PAR3__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__B 0 +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__W 7 +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__PRE 0x3F + +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7 +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__W 7 +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__M 0x3F80 +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__PRE 0x1F80 + +#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__B 0 +#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__W 16 +#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__B 0 +#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__W 3 +#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__M 0x7 +#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_3_ACP_RW__B 3 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW__W 1 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW__M 0x8 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8 + +#define SIO_HI_RA_RAM_PAR_4__A 0x420036 +#define SIO_HI_RA_RAM_PAR_4__W 16 +#define SIO_HI_RA_RAM_PAR_4__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_4__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_4_PAR4__B 0 +#define SIO_HI_RA_RAM_PAR_4_PAR4__W 16 +#define SIO_HI_RA_RAM_PAR_4_PAR4__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_4_PAR4__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__B 0 +#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__W 8 +#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__M 0xFF +#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__PRE 0xC1 + +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__B 0 +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__W 6 +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__M 0x3F +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__B 6 +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__W 6 +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__M 0xFC0 +#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__B 0 +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__W 6 +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__M 0x3F +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__B 6 +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__W 6 +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__M 0xFC0 +#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_5__A 0x420037 +#define SIO_HI_RA_RAM_PAR_5__W 16 +#define SIO_HI_RA_RAM_PAR_5__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_5__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_5_PAR5__B 0 +#define SIO_HI_RA_RAM_PAR_5_PAR5__W 16 +#define SIO_HI_RA_RAM_PAR_5_PAR5__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_5_PAR5__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__B 0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__W 1 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__M 0x1 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_NO_SLAVE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1 + +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__B 1 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__W 1 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__M 0x2 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1_NO_SLAVE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1_SLAVE 0x2 + +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__B 3 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__W 1 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_AWAKE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8 + +#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__B 5 +#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__W 1 +#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__M 0x20 +#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__PRE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST_DISABLE 0x0 +#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST_ENABLE 0x20 + +#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__B 0 +#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__W 16 +#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__B 0 +#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__W 16 +#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_6__A 0x420038 +#define SIO_HI_RA_RAM_PAR_6__W 16 +#define SIO_HI_RA_RAM_PAR_6__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_6__PRE 0x95FF +#define SIO_HI_RA_RAM_PAR_6_PAR6__B 0 +#define SIO_HI_RA_RAM_PAR_6_PAR6__W 16 +#define SIO_HI_RA_RAM_PAR_6_PAR6__M 0xFFFF +#define SIO_HI_RA_RAM_PAR_6_PAR6__PRE 0x0 + +#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__B 0 +#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__W 8 +#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__M 0xFF +#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__PRE 0xFF + +#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__B 8 +#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__W 8 +#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__M 0xFF00 +#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__PRE 0x9500 + +#define SIO_HI_RA_RAM_AB_TEMP__A 0x42006E +#define SIO_HI_RA_RAM_AB_TEMP__W 16 +#define SIO_HI_RA_RAM_AB_TEMP__M 0xFFFF +#define SIO_HI_RA_RAM_AB_TEMP__PRE 0x0 + +#define SIO_HI_RA_RAM_I2C_CTL__A 0x42006F +#define SIO_HI_RA_RAM_I2C_CTL__W 16 +#define SIO_HI_RA_RAM_I2C_CTL__M 0xFFFF +#define SIO_HI_RA_RAM_I2C_CTL__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY0__A 0x420070 +#define SIO_HI_RA_RAM_VB_ENTRY0__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY0__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY0__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__B 0 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__W 4 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__M 0xF +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__B 4 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__W 4 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__M 0xF0 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__B 8 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__W 4 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__M 0xF00 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__B 12 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__W 4 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__M 0xF000 +#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET0__A 0x420071 +#define SIO_HI_RA_RAM_VB_OFFSET0__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET0__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET0__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY1__A 0x420072 +#define SIO_HI_RA_RAM_VB_ENTRY1__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY1__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY1__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET1__A 0x420073 +#define SIO_HI_RA_RAM_VB_OFFSET1__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET1__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET1__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY2__A 0x420074 +#define SIO_HI_RA_RAM_VB_ENTRY2__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY2__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY2__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET2__A 0x420075 +#define SIO_HI_RA_RAM_VB_OFFSET2__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET2__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET2__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY3__A 0x420076 +#define SIO_HI_RA_RAM_VB_ENTRY3__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY3__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY3__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET3__A 0x420077 +#define SIO_HI_RA_RAM_VB_OFFSET3__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET3__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET3__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY4__A 0x420078 +#define SIO_HI_RA_RAM_VB_ENTRY4__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY4__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY4__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET4__A 0x420079 +#define SIO_HI_RA_RAM_VB_OFFSET4__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET4__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET4__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY5__A 0x42007A +#define SIO_HI_RA_RAM_VB_ENTRY5__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY5__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY5__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET5__A 0x42007B +#define SIO_HI_RA_RAM_VB_OFFSET5__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET5__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET5__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY6__A 0x42007C +#define SIO_HI_RA_RAM_VB_ENTRY6__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY6__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY6__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET6__A 0x42007D +#define SIO_HI_RA_RAM_VB_OFFSET6__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET6__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET6__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_ENTRY7__A 0x42007E +#define SIO_HI_RA_RAM_VB_ENTRY7__W 16 +#define SIO_HI_RA_RAM_VB_ENTRY7__M 0xFFFF +#define SIO_HI_RA_RAM_VB_ENTRY7__PRE 0x0 +#define SIO_HI_RA_RAM_VB_OFFSET7__A 0x42007F +#define SIO_HI_RA_RAM_VB_OFFSET7__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET7__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET7__PRE 0x0 + +#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__B 0 +#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__W 16 +#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__M 0xFFFF +#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__PRE 0x0 + +#define SIO_HI_IF_RAM_TRP_BPT_0__A 0x430000 +#define SIO_HI_IF_RAM_TRP_BPT_0__W 12 +#define SIO_HI_IF_RAM_TRP_BPT_0__M 0xFFF +#define SIO_HI_IF_RAM_TRP_BPT_0__PRE 0x0 +#define SIO_HI_IF_RAM_TRP_BPT_1__A 0x430001 +#define SIO_HI_IF_RAM_TRP_BPT_1__W 12 +#define SIO_HI_IF_RAM_TRP_BPT_1__M 0xFFF +#define SIO_HI_IF_RAM_TRP_BPT_1__PRE 0x0 +#define SIO_HI_IF_RAM_TRP_STK_0__A 0x430002 +#define SIO_HI_IF_RAM_TRP_STK_0__W 12 +#define SIO_HI_IF_RAM_TRP_STK_0__M 0xFFF +#define SIO_HI_IF_RAM_TRP_STK_0__PRE 0x0 +#define SIO_HI_IF_RAM_TRP_STK_1__A 0x430003 +#define SIO_HI_IF_RAM_TRP_STK_1__W 12 +#define SIO_HI_IF_RAM_TRP_STK_1__M 0xFFF +#define SIO_HI_IF_RAM_TRP_STK_1__PRE 0x0 +#define SIO_HI_IF_RAM_FUN_BASE__A 0x430300 +#define SIO_HI_IF_RAM_FUN_BASE__W 12 +#define SIO_HI_IF_RAM_FUN_BASE__M 0xFFF +#define SIO_HI_IF_RAM_FUN_BASE__PRE 0x0 + +#define SIO_HI_IF_COMM_EXEC__A 0x440000 +#define SIO_HI_IF_COMM_EXEC__W 2 +#define SIO_HI_IF_COMM_EXEC__M 0x3 +#define SIO_HI_IF_COMM_EXEC__PRE 0x0 +#define SIO_HI_IF_COMM_EXEC_STOP 0x0 +#define SIO_HI_IF_COMM_EXEC_ACTIVE 0x1 +#define SIO_HI_IF_COMM_EXEC_HOLD 0x2 +#define SIO_HI_IF_COMM_EXEC_STEP 0x3 + +#define SIO_HI_IF_COMM_STATE__A 0x440001 +#define SIO_HI_IF_COMM_STATE__W 10 +#define SIO_HI_IF_COMM_STATE__M 0x3FF +#define SIO_HI_IF_COMM_STATE__PRE 0x0 +#define SIO_HI_IF_COMM_INT_REQ__A 0x440003 +#define SIO_HI_IF_COMM_INT_REQ__W 1 +#define SIO_HI_IF_COMM_INT_REQ__M 0x1 +#define SIO_HI_IF_COMM_INT_REQ__PRE 0x0 +#define SIO_HI_IF_COMM_INT_STA__A 0x440005 +#define SIO_HI_IF_COMM_INT_STA__W 1 +#define SIO_HI_IF_COMM_INT_STA__M 0x1 +#define SIO_HI_IF_COMM_INT_STA__PRE 0x0 +#define SIO_HI_IF_COMM_INT_STA_STAT__B 0 +#define SIO_HI_IF_COMM_INT_STA_STAT__W 1 +#define SIO_HI_IF_COMM_INT_STA_STAT__M 0x1 +#define SIO_HI_IF_COMM_INT_STA_STAT__PRE 0x0 + +#define SIO_HI_IF_COMM_INT_MSK__A 0x440006 +#define SIO_HI_IF_COMM_INT_MSK__W 1 +#define SIO_HI_IF_COMM_INT_MSK__M 0x1 +#define SIO_HI_IF_COMM_INT_MSK__PRE 0x0 +#define SIO_HI_IF_COMM_INT_MSK_STAT__B 0 +#define SIO_HI_IF_COMM_INT_MSK_STAT__W 1 +#define SIO_HI_IF_COMM_INT_MSK_STAT__M 0x1 +#define SIO_HI_IF_COMM_INT_MSK_STAT__PRE 0x0 + +#define SIO_HI_IF_COMM_INT_STM__A 0x440007 +#define SIO_HI_IF_COMM_INT_STM__W 1 +#define SIO_HI_IF_COMM_INT_STM__M 0x1 +#define SIO_HI_IF_COMM_INT_STM__PRE 0x0 +#define SIO_HI_IF_COMM_INT_STM_STAT__B 0 +#define SIO_HI_IF_COMM_INT_STM_STAT__W 1 +#define SIO_HI_IF_COMM_INT_STM_STAT__M 0x1 +#define SIO_HI_IF_COMM_INT_STM_STAT__PRE 0x0 + +#define SIO_HI_IF_STK_0__A 0x440010 +#define SIO_HI_IF_STK_0__W 10 +#define SIO_HI_IF_STK_0__M 0x3FF +#define SIO_HI_IF_STK_0__PRE 0x2 + +#define SIO_HI_IF_STK_0_ADDR__B 0 +#define SIO_HI_IF_STK_0_ADDR__W 10 +#define SIO_HI_IF_STK_0_ADDR__M 0x3FF +#define SIO_HI_IF_STK_0_ADDR__PRE 0x2 + +#define SIO_HI_IF_STK_1__A 0x440011 +#define SIO_HI_IF_STK_1__W 10 +#define SIO_HI_IF_STK_1__M 0x3FF +#define SIO_HI_IF_STK_1__PRE 0x2 +#define SIO_HI_IF_STK_1_ADDR__B 0 +#define SIO_HI_IF_STK_1_ADDR__W 10 +#define SIO_HI_IF_STK_1_ADDR__M 0x3FF +#define SIO_HI_IF_STK_1_ADDR__PRE 0x2 + +#define SIO_HI_IF_STK_2__A 0x440012 +#define SIO_HI_IF_STK_2__W 10 +#define SIO_HI_IF_STK_2__M 0x3FF +#define SIO_HI_IF_STK_2__PRE 0x2 +#define SIO_HI_IF_STK_2_ADDR__B 0 +#define SIO_HI_IF_STK_2_ADDR__W 10 +#define SIO_HI_IF_STK_2_ADDR__M 0x3FF +#define SIO_HI_IF_STK_2_ADDR__PRE 0x2 + +#define SIO_HI_IF_STK_3__A 0x440013 +#define SIO_HI_IF_STK_3__W 10 +#define SIO_HI_IF_STK_3__M 0x3FF +#define SIO_HI_IF_STK_3__PRE 0x2 + +#define SIO_HI_IF_STK_3_ADDR__B 0 +#define SIO_HI_IF_STK_3_ADDR__W 10 +#define SIO_HI_IF_STK_3_ADDR__M 0x3FF +#define SIO_HI_IF_STK_3_ADDR__PRE 0x2 + +#define SIO_HI_IF_BPT_IDX__A 0x44001F +#define SIO_HI_IF_BPT_IDX__W 1 +#define SIO_HI_IF_BPT_IDX__M 0x1 +#define SIO_HI_IF_BPT_IDX__PRE 0x0 + +#define SIO_HI_IF_BPT_IDX_ADDR__B 0 +#define SIO_HI_IF_BPT_IDX_ADDR__W 1 +#define SIO_HI_IF_BPT_IDX_ADDR__M 0x1 +#define SIO_HI_IF_BPT_IDX_ADDR__PRE 0x0 + +#define SIO_HI_IF_BPT__A 0x440020 +#define SIO_HI_IF_BPT__W 10 +#define SIO_HI_IF_BPT__M 0x3FF +#define SIO_HI_IF_BPT__PRE 0x2 + +#define SIO_HI_IF_BPT_ADDR__B 0 +#define SIO_HI_IF_BPT_ADDR__W 10 +#define SIO_HI_IF_BPT_ADDR__M 0x3FF +#define SIO_HI_IF_BPT_ADDR__PRE 0x2 + +#define SIO_CC_COMM_EXEC__A 0x450000 +#define SIO_CC_COMM_EXEC__W 2 +#define SIO_CC_COMM_EXEC__M 0x3 +#define SIO_CC_COMM_EXEC__PRE 0x0 +#define SIO_CC_COMM_EXEC_STOP 0x0 +#define SIO_CC_COMM_EXEC_ACTIVE 0x1 +#define SIO_CC_COMM_EXEC_HOLD 0x2 + +#define SIO_CC_PLL_MODE__A 0x450010 +#define SIO_CC_PLL_MODE__W 6 +#define SIO_CC_PLL_MODE__M 0x3F +#define SIO_CC_PLL_MODE__PRE 0x0 + +#define SIO_CC_PLL_MODE_FREF_SEL__B 0 +#define SIO_CC_PLL_MODE_FREF_SEL__W 2 +#define SIO_CC_PLL_MODE_FREF_SEL__M 0x3 +#define SIO_CC_PLL_MODE_FREF_SEL__PRE 0x0 +#define SIO_CC_PLL_MODE_FREF_SEL_OHW 0x0 +#define SIO_CC_PLL_MODE_FREF_SEL_27_00 0x1 +#define SIO_CC_PLL_MODE_FREF_SEL_20_25 0x2 +#define SIO_CC_PLL_MODE_FREF_SEL_4_00 0x3 + +#define SIO_CC_PLL_MODE_LOCKSEL__B 2 +#define SIO_CC_PLL_MODE_LOCKSEL__W 2 +#define SIO_CC_PLL_MODE_LOCKSEL__M 0xC +#define SIO_CC_PLL_MODE_LOCKSEL__PRE 0x0 + +#define SIO_CC_PLL_MODE_BYPASS__B 4 +#define SIO_CC_PLL_MODE_BYPASS__W 2 +#define SIO_CC_PLL_MODE_BYPASS__M 0x30 +#define SIO_CC_PLL_MODE_BYPASS__PRE 0x0 +#define SIO_CC_PLL_MODE_BYPASS_OHW 0x0 +#define SIO_CC_PLL_MODE_BYPASS_OFF 0x10 +#define SIO_CC_PLL_MODE_BYPASS_ON 0x20 + +#define SIO_CC_PLL_TEST__A 0x450011 +#define SIO_CC_PLL_TEST__W 8 +#define SIO_CC_PLL_TEST__M 0xFF +#define SIO_CC_PLL_TEST__PRE 0x0 + +#define SIO_CC_PLL_LOCK__A 0x450012 +#define SIO_CC_PLL_LOCK__W 1 +#define SIO_CC_PLL_LOCK__M 0x1 +#define SIO_CC_PLL_LOCK__PRE 0x0 +#define SIO_CC_CLK_MODE__A 0x450014 +#define SIO_CC_CLK_MODE__W 5 +#define SIO_CC_CLK_MODE__M 0x1F +#define SIO_CC_CLK_MODE__PRE 0x0 + +#define SIO_CC_CLK_MODE_DELAY__B 0 +#define SIO_CC_CLK_MODE_DELAY__W 4 +#define SIO_CC_CLK_MODE_DELAY__M 0xF +#define SIO_CC_CLK_MODE_DELAY__PRE 0x0 + +#define SIO_CC_CLK_MODE_INVERT__B 4 +#define SIO_CC_CLK_MODE_INVERT__W 1 +#define SIO_CC_CLK_MODE_INVERT__M 0x10 +#define SIO_CC_CLK_MODE_INVERT__PRE 0x0 + +#define SIO_CC_PWD_MODE__A 0x450015 +#define SIO_CC_PWD_MODE__W 3 +#define SIO_CC_PWD_MODE__M 0x7 +#define SIO_CC_PWD_MODE__PRE 0x0 + +#define SIO_CC_PWD_MODE_LEVEL__B 0 +#define SIO_CC_PWD_MODE_LEVEL__W 2 +#define SIO_CC_PWD_MODE_LEVEL__M 0x3 +#define SIO_CC_PWD_MODE_LEVEL__PRE 0x0 +#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0 +#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x1 +#define SIO_CC_PWD_MODE_LEVEL_PLL 0x2 +#define SIO_CC_PWD_MODE_LEVEL_OSC 0x3 + +#define SIO_CC_PWD_MODE_USE_LOCK__B 2 +#define SIO_CC_PWD_MODE_USE_LOCK__W 1 +#define SIO_CC_PWD_MODE_USE_LOCK__M 0x4 +#define SIO_CC_PWD_MODE_USE_LOCK__PRE 0x0 + +#define SIO_CC_SOFT_RST__A 0x450016 +#define SIO_CC_SOFT_RST__W 2 +#define SIO_CC_SOFT_RST__M 0x3 +#define SIO_CC_SOFT_RST__PRE 0x0 + +#define SIO_CC_SOFT_RST_SYS__B 0 +#define SIO_CC_SOFT_RST_SYS__W 1 +#define SIO_CC_SOFT_RST_SYS__M 0x1 +#define SIO_CC_SOFT_RST_SYS__PRE 0x0 + +#define SIO_CC_SOFT_RST_OSC__B 1 +#define SIO_CC_SOFT_RST_OSC__W 1 +#define SIO_CC_SOFT_RST_OSC__M 0x2 +#define SIO_CC_SOFT_RST_OSC__PRE 0x0 + +#define SIO_CC_UPDATE__A 0x450017 +#define SIO_CC_UPDATE__W 16 +#define SIO_CC_UPDATE__M 0xFFFF +#define SIO_CC_UPDATE__PRE 0x0 +#define SIO_CC_UPDATE_KEY 0xFABA + +#define SIO_SA_COMM_EXEC__A 0x460000 +#define SIO_SA_COMM_EXEC__W 2 +#define SIO_SA_COMM_EXEC__M 0x3 +#define SIO_SA_COMM_EXEC__PRE 0x0 +#define SIO_SA_COMM_EXEC_STOP 0x0 +#define SIO_SA_COMM_EXEC_ACTIVE 0x1 +#define SIO_SA_COMM_EXEC_HOLD 0x2 + +#define SIO_SA_COMM_INT_REQ__A 0x460003 +#define SIO_SA_COMM_INT_REQ__W 1 +#define SIO_SA_COMM_INT_REQ__M 0x1 +#define SIO_SA_COMM_INT_REQ__PRE 0x0 +#define SIO_SA_COMM_INT_STA__A 0x460005 +#define SIO_SA_COMM_INT_STA__W 4 +#define SIO_SA_COMM_INT_STA__M 0xF +#define SIO_SA_COMM_INT_STA__PRE 0x0 + +#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__B 0 +#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__W 1 +#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__M 0x1 +#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__PRE 0x0 + +#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__B 1 +#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__W 1 +#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__M 0x2 +#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__PRE 0x0 + +#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__B 2 +#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__W 1 +#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__M 0x4 +#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__PRE 0x0 + +#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__B 3 +#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__W 1 +#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__M 0x8 +#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__PRE 0x0 + +#define SIO_SA_COMM_INT_MSK__A 0x460006 +#define SIO_SA_COMM_INT_MSK__W 4 +#define SIO_SA_COMM_INT_MSK__M 0xF +#define SIO_SA_COMM_INT_MSK__PRE 0x0 + +#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__B 0 +#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__W 1 +#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__M 0x1 +#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__B 1 +#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__W 1 +#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__M 0x2 +#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__B 2 +#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__W 1 +#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__M 0x4 +#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__B 3 +#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__W 1 +#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__M 0x8 +#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_STM__A 0x460007 +#define SIO_SA_COMM_INT_STM__W 4 +#define SIO_SA_COMM_INT_STM__M 0xF +#define SIO_SA_COMM_INT_STM__PRE 0x0 + +#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__B 0 +#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__W 1 +#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__M 0x1 +#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__B 1 +#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__W 1 +#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__M 0x2 +#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__B 2 +#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__W 1 +#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__M 0x4 +#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__PRE 0x0 + +#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__B 3 +#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__W 1 +#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__M 0x8 +#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__PRE 0x0 + +#define SIO_SA_PRESCALER__A 0x460010 +#define SIO_SA_PRESCALER__W 13 +#define SIO_SA_PRESCALER__M 0x1FFF +#define SIO_SA_PRESCALER__PRE 0x18B7 +#define SIO_SA_TX_DATA0__A 0x460011 +#define SIO_SA_TX_DATA0__W 16 +#define SIO_SA_TX_DATA0__M 0xFFFF +#define SIO_SA_TX_DATA0__PRE 0x0 +#define SIO_SA_TX_DATA1__A 0x460012 +#define SIO_SA_TX_DATA1__W 16 +#define SIO_SA_TX_DATA1__M 0xFFFF +#define SIO_SA_TX_DATA1__PRE 0x0 +#define SIO_SA_TX_DATA2__A 0x460013 +#define SIO_SA_TX_DATA2__W 16 +#define SIO_SA_TX_DATA2__M 0xFFFF +#define SIO_SA_TX_DATA2__PRE 0x0 +#define SIO_SA_TX_DATA3__A 0x460014 +#define SIO_SA_TX_DATA3__W 16 +#define SIO_SA_TX_DATA3__M 0xFFFF +#define SIO_SA_TX_DATA3__PRE 0x0 +#define SIO_SA_TX_LENGTH__A 0x460015 +#define SIO_SA_TX_LENGTH__W 6 +#define SIO_SA_TX_LENGTH__M 0x3F +#define SIO_SA_TX_LENGTH__PRE 0x0 +#define SIO_SA_TX_COMMAND__A 0x460016 +#define SIO_SA_TX_COMMAND__W 2 +#define SIO_SA_TX_COMMAND__M 0x3 +#define SIO_SA_TX_COMMAND__PRE 0x3 + +#define SIO_SA_TX_COMMAND_TX_INVERT__B 0 +#define SIO_SA_TX_COMMAND_TX_INVERT__W 1 +#define SIO_SA_TX_COMMAND_TX_INVERT__M 0x1 +#define SIO_SA_TX_COMMAND_TX_INVERT__PRE 0x1 + +#define SIO_SA_TX_COMMAND_TX_ENABLE__B 1 +#define SIO_SA_TX_COMMAND_TX_ENABLE__W 1 +#define SIO_SA_TX_COMMAND_TX_ENABLE__M 0x2 +#define SIO_SA_TX_COMMAND_TX_ENABLE__PRE 0x2 + +#define SIO_SA_TX_STATUS__A 0x460017 +#define SIO_SA_TX_STATUS__W 2 +#define SIO_SA_TX_STATUS__M 0x3 +#define SIO_SA_TX_STATUS__PRE 0x0 + +#define SIO_SA_TX_STATUS_BUSY__B 0 +#define SIO_SA_TX_STATUS_BUSY__W 1 +#define SIO_SA_TX_STATUS_BUSY__M 0x1 +#define SIO_SA_TX_STATUS_BUSY__PRE 0x0 + +#define SIO_SA_TX_STATUS_BUFF_FULL__B 1 +#define SIO_SA_TX_STATUS_BUFF_FULL__W 1 +#define SIO_SA_TX_STATUS_BUFF_FULL__M 0x2 +#define SIO_SA_TX_STATUS_BUFF_FULL__PRE 0x0 + +#define SIO_SA_RX_DATA0__A 0x460018 +#define SIO_SA_RX_DATA0__W 16 +#define SIO_SA_RX_DATA0__M 0xFFFF +#define SIO_SA_RX_DATA0__PRE 0x0 +#define SIO_SA_RX_DATA1__A 0x460019 +#define SIO_SA_RX_DATA1__W 16 +#define SIO_SA_RX_DATA1__M 0xFFFF +#define SIO_SA_RX_DATA1__PRE 0x0 +#define SIO_SA_RX_LENGTH__A 0x46001A +#define SIO_SA_RX_LENGTH__W 6 +#define SIO_SA_RX_LENGTH__M 0x3F +#define SIO_SA_RX_LENGTH__PRE 0x0 +#define SIO_SA_RX_COMMAND__A 0x46001B +#define SIO_SA_RX_COMMAND__W 1 +#define SIO_SA_RX_COMMAND__M 0x1 +#define SIO_SA_RX_COMMAND__PRE 0x1 + +#define SIO_SA_RX_COMMAND_RX_INVERT__B 0 +#define SIO_SA_RX_COMMAND_RX_INVERT__W 1 +#define SIO_SA_RX_COMMAND_RX_INVERT__M 0x1 +#define SIO_SA_RX_COMMAND_RX_INVERT__PRE 0x1 + +#define SIO_SA_RX_STATUS__A 0x46001C +#define SIO_SA_RX_STATUS__W 2 +#define SIO_SA_RX_STATUS__M 0x3 +#define SIO_SA_RX_STATUS__PRE 0x0 + +#define SIO_SA_RX_STATUS_BUSY__B 0 +#define SIO_SA_RX_STATUS_BUSY__W 1 +#define SIO_SA_RX_STATUS_BUSY__M 0x1 +#define SIO_SA_RX_STATUS_BUSY__PRE 0x0 + +#define SIO_SA_RX_STATUS_BUFF_FULL__B 1 +#define SIO_SA_RX_STATUS_BUFF_FULL__W 1 +#define SIO_SA_RX_STATUS_BUFF_FULL__M 0x2 +#define SIO_SA_RX_STATUS_BUFF_FULL__PRE 0x0 + +#define SIO_PDR_COMM_EXEC__A 0x7F0000 +#define SIO_PDR_COMM_EXEC__W 2 +#define SIO_PDR_COMM_EXEC__M 0x3 +#define SIO_PDR_COMM_EXEC__PRE 0x0 +#define SIO_PDR_COMM_EXEC_STOP 0x0 +#define SIO_PDR_COMM_EXEC_ACTIVE 0x1 +#define SIO_PDR_COMM_EXEC_HOLD 0x2 + +#define SIO_PDR_MON_CFG__A 0x7F0010 +#define SIO_PDR_MON_CFG__W 2 +#define SIO_PDR_MON_CFG__M 0x3 +#define SIO_PDR_MON_CFG__PRE 0x0 + +#define SIO_PDR_MON_CFG_OSEL__B 0 +#define SIO_PDR_MON_CFG_OSEL__W 1 +#define SIO_PDR_MON_CFG_OSEL__M 0x1 +#define SIO_PDR_MON_CFG_OSEL__PRE 0x0 + +#define SIO_PDR_MON_CFG_IACT__B 1 +#define SIO_PDR_MON_CFG_IACT__W 1 +#define SIO_PDR_MON_CFG_IACT__M 0x2 +#define SIO_PDR_MON_CFG_IACT__PRE 0x0 + +#define SIO_PDR_FDB_CFG__A 0x7F0011 +#define SIO_PDR_FDB_CFG__W 2 +#define SIO_PDR_FDB_CFG__M 0x3 +#define SIO_PDR_FDB_CFG__PRE 0x0 +#define SIO_PDR_FDB_CFG_SEL__B 0 +#define SIO_PDR_FDB_CFG_SEL__W 2 +#define SIO_PDR_FDB_CFG_SEL__M 0x3 +#define SIO_PDR_FDB_CFG_SEL__PRE 0x0 + +#define SIO_PDR_SMA_RX_SEL__A 0x7F0012 +#define SIO_PDR_SMA_RX_SEL__W 4 +#define SIO_PDR_SMA_RX_SEL__M 0xF +#define SIO_PDR_SMA_RX_SEL__PRE 0x0 +#define SIO_PDR_SMA_RX_SEL_SEL__B 0 +#define SIO_PDR_SMA_RX_SEL_SEL__W 4 +#define SIO_PDR_SMA_RX_SEL_SEL__M 0xF +#define SIO_PDR_SMA_RX_SEL_SEL__PRE 0x0 + +#define SIO_PDR_SMA_TX_SILENT__A 0x7F0013 +#define SIO_PDR_SMA_TX_SILENT__W 1 +#define SIO_PDR_SMA_TX_SILENT__M 0x1 +#define SIO_PDR_SMA_TX_SILENT__PRE 0x0 +#define SIO_PDR_UIO_IN_LO__A 0x7F0014 +#define SIO_PDR_UIO_IN_LO__W 16 +#define SIO_PDR_UIO_IN_LO__M 0xFFFF +#define SIO_PDR_UIO_IN_LO__PRE 0x0 +#define SIO_PDR_UIO_IN_LO_DATA__B 0 +#define SIO_PDR_UIO_IN_LO_DATA__W 16 +#define SIO_PDR_UIO_IN_LO_DATA__M 0xFFFF +#define SIO_PDR_UIO_IN_LO_DATA__PRE 0x0 + +#define SIO_PDR_UIO_IN_HI__A 0x7F0015 +#define SIO_PDR_UIO_IN_HI__W 14 +#define SIO_PDR_UIO_IN_HI__M 0x3FFF +#define SIO_PDR_UIO_IN_HI__PRE 0x0 +#define SIO_PDR_UIO_IN_HI_DATA__B 0 +#define SIO_PDR_UIO_IN_HI_DATA__W 14 +#define SIO_PDR_UIO_IN_HI_DATA__M 0x3FFF +#define SIO_PDR_UIO_IN_HI_DATA__PRE 0x0 + +#define SIO_PDR_UIO_OUT_LO__A 0x7F0016 +#define SIO_PDR_UIO_OUT_LO__W 16 +#define SIO_PDR_UIO_OUT_LO__M 0xFFFF +#define SIO_PDR_UIO_OUT_LO__PRE 0x0 +#define SIO_PDR_UIO_OUT_LO_DATA__B 0 +#define SIO_PDR_UIO_OUT_LO_DATA__W 16 +#define SIO_PDR_UIO_OUT_LO_DATA__M 0xFFFF +#define SIO_PDR_UIO_OUT_LO_DATA__PRE 0x0 + +#define SIO_PDR_UIO_OUT_HI__A 0x7F0017 +#define SIO_PDR_UIO_OUT_HI__W 14 +#define SIO_PDR_UIO_OUT_HI__M 0x3FFF +#define SIO_PDR_UIO_OUT_HI__PRE 0x0 +#define SIO_PDR_UIO_OUT_HI_DATA__B 0 +#define SIO_PDR_UIO_OUT_HI_DATA__W 14 +#define SIO_PDR_UIO_OUT_HI_DATA__M 0x3FFF +#define SIO_PDR_UIO_OUT_HI_DATA__PRE 0x0 + +#define SIO_PDR_PWM1_MODE__A 0x7F0018 +#define SIO_PDR_PWM1_MODE__W 2 +#define SIO_PDR_PWM1_MODE__M 0x3 +#define SIO_PDR_PWM1_MODE__PRE 0x0 +#define SIO_PDR_PWM1_PRESCALE__A 0x7F0019 +#define SIO_PDR_PWM1_PRESCALE__W 6 +#define SIO_PDR_PWM1_PRESCALE__M 0x3F +#define SIO_PDR_PWM1_PRESCALE__PRE 0x0 +#define SIO_PDR_PWM1_VALUE__A 0x7F001A +#define SIO_PDR_PWM1_VALUE__W 11 +#define SIO_PDR_PWM1_VALUE__M 0x7FF +#define SIO_PDR_PWM1_VALUE__PRE 0x0 +#define SIO_PDR_PWM2_MODE__A 0x7F001C +#define SIO_PDR_PWM2_MODE__W 2 +#define SIO_PDR_PWM2_MODE__M 0x3 +#define SIO_PDR_PWM2_MODE__PRE 0x0 +#define SIO_PDR_PWM2_PRESCALE__A 0x7F001D +#define SIO_PDR_PWM2_PRESCALE__W 6 +#define SIO_PDR_PWM2_PRESCALE__M 0x3F +#define SIO_PDR_PWM2_PRESCALE__PRE 0x0 +#define SIO_PDR_PWM2_VALUE__A 0x7F001E +#define SIO_PDR_PWM2_VALUE__W 11 +#define SIO_PDR_PWM2_VALUE__M 0x7FF +#define SIO_PDR_PWM2_VALUE__PRE 0x0 +#define SIO_PDR_OHW_CFG__A 0x7F001F +#define SIO_PDR_OHW_CFG__W 7 +#define SIO_PDR_OHW_CFG__M 0x7F +#define SIO_PDR_OHW_CFG__PRE 0x0 + +#define SIO_PDR_OHW_CFG_FREF_SEL__B 0 +#define SIO_PDR_OHW_CFG_FREF_SEL__W 2 +#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3 +#define SIO_PDR_OHW_CFG_FREF_SEL__PRE 0x0 + +#define SIO_PDR_OHW_CFG_BYPASS__B 2 +#define SIO_PDR_OHW_CFG_BYPASS__W 1 +#define SIO_PDR_OHW_CFG_BYPASS__M 0x4 +#define SIO_PDR_OHW_CFG_BYPASS__PRE 0x0 + +#define SIO_PDR_OHW_CFG_ASEL__B 3 +#define SIO_PDR_OHW_CFG_ASEL__W 3 +#define SIO_PDR_OHW_CFG_ASEL__M 0x38 +#define SIO_PDR_OHW_CFG_ASEL__PRE 0x0 + +#define SIO_PDR_OHW_CFG_SPEED__B 6 +#define SIO_PDR_OHW_CFG_SPEED__W 1 +#define SIO_PDR_OHW_CFG_SPEED__M 0x40 +#define SIO_PDR_OHW_CFG_SPEED__PRE 0x0 + +#define SIO_PDR_I2S_WS_CFG__A 0x7F0020 +#define SIO_PDR_I2S_WS_CFG__W 9 +#define SIO_PDR_I2S_WS_CFG__M 0x1FF +#define SIO_PDR_I2S_WS_CFG__PRE 0x10 +#define SIO_PDR_I2S_WS_CFG_MODE__B 0 +#define SIO_PDR_I2S_WS_CFG_MODE__W 3 +#define SIO_PDR_I2S_WS_CFG_MODE__M 0x7 +#define SIO_PDR_I2S_WS_CFG_MODE__PRE 0x0 +#define SIO_PDR_I2S_WS_CFG_DRIVE__B 3 +#define SIO_PDR_I2S_WS_CFG_DRIVE__W 3 +#define SIO_PDR_I2S_WS_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2S_WS_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2S_WS_CFG_KEEP__B 6 +#define SIO_PDR_I2S_WS_CFG_KEEP__W 2 +#define SIO_PDR_I2S_WS_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2S_WS_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2S_WS_CFG_UIO__B 8 +#define SIO_PDR_I2S_WS_CFG_UIO__W 1 +#define SIO_PDR_I2S_WS_CFG_UIO__M 0x100 +#define SIO_PDR_I2S_WS_CFG_UIO__PRE 0x0 + +#define SIO_PDR_GPIO_CFG__A 0x7F0021 +#define SIO_PDR_GPIO_CFG__W 9 +#define SIO_PDR_GPIO_CFG__M 0x1FF +#define SIO_PDR_GPIO_CFG__PRE 0x10 +#define SIO_PDR_GPIO_CFG_MODE__B 0 +#define SIO_PDR_GPIO_CFG_MODE__W 3 +#define SIO_PDR_GPIO_CFG_MODE__M 0x7 +#define SIO_PDR_GPIO_CFG_MODE__PRE 0x0 +#define SIO_PDR_GPIO_CFG_DRIVE__B 3 +#define SIO_PDR_GPIO_CFG_DRIVE__W 3 +#define SIO_PDR_GPIO_CFG_DRIVE__M 0x38 +#define SIO_PDR_GPIO_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_GPIO_CFG_KEEP__B 6 +#define SIO_PDR_GPIO_CFG_KEEP__W 2 +#define SIO_PDR_GPIO_CFG_KEEP__M 0xC0 +#define SIO_PDR_GPIO_CFG_KEEP__PRE 0x0 +#define SIO_PDR_GPIO_CFG_UIO__B 8 +#define SIO_PDR_GPIO_CFG_UIO__W 1 +#define SIO_PDR_GPIO_CFG_UIO__M 0x100 +#define SIO_PDR_GPIO_CFG_UIO__PRE 0x0 + +#define SIO_PDR_IRQN_CFG__A 0x7F0022 +#define SIO_PDR_IRQN_CFG__W 9 +#define SIO_PDR_IRQN_CFG__M 0x1FF +#define SIO_PDR_IRQN_CFG__PRE 0x10 +#define SIO_PDR_IRQN_CFG_MODE__B 0 +#define SIO_PDR_IRQN_CFG_MODE__W 3 +#define SIO_PDR_IRQN_CFG_MODE__M 0x7 +#define SIO_PDR_IRQN_CFG_MODE__PRE 0x0 +#define SIO_PDR_IRQN_CFG_DRIVE__B 3 +#define SIO_PDR_IRQN_CFG_DRIVE__W 3 +#define SIO_PDR_IRQN_CFG_DRIVE__M 0x38 +#define SIO_PDR_IRQN_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_IRQN_CFG_KEEP__B 6 +#define SIO_PDR_IRQN_CFG_KEEP__W 2 +#define SIO_PDR_IRQN_CFG_KEEP__M 0xC0 +#define SIO_PDR_IRQN_CFG_KEEP__PRE 0x0 +#define SIO_PDR_IRQN_CFG_UIO__B 8 +#define SIO_PDR_IRQN_CFG_UIO__W 1 +#define SIO_PDR_IRQN_CFG_UIO__M 0x100 +#define SIO_PDR_IRQN_CFG_UIO__PRE 0x0 + +#define SIO_PDR_OOB_CRX_CFG__A 0x7F0023 +#define SIO_PDR_OOB_CRX_CFG__W 9 +#define SIO_PDR_OOB_CRX_CFG__M 0x1FF +#define SIO_PDR_OOB_CRX_CFG__PRE 0x10 +#define SIO_PDR_OOB_CRX_CFG_MODE__B 0 +#define SIO_PDR_OOB_CRX_CFG_MODE__W 3 +#define SIO_PDR_OOB_CRX_CFG_MODE__M 0x7 +#define SIO_PDR_OOB_CRX_CFG_MODE__PRE 0x0 +#define SIO_PDR_OOB_CRX_CFG_DRIVE__B 3 +#define SIO_PDR_OOB_CRX_CFG_DRIVE__W 3 +#define SIO_PDR_OOB_CRX_CFG_DRIVE__M 0x38 +#define SIO_PDR_OOB_CRX_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_OOB_CRX_CFG_KEEP__B 6 +#define SIO_PDR_OOB_CRX_CFG_KEEP__W 2 +#define SIO_PDR_OOB_CRX_CFG_KEEP__M 0xC0 +#define SIO_PDR_OOB_CRX_CFG_KEEP__PRE 0x0 +#define SIO_PDR_OOB_CRX_CFG_UIO__B 8 +#define SIO_PDR_OOB_CRX_CFG_UIO__W 1 +#define SIO_PDR_OOB_CRX_CFG_UIO__M 0x100 +#define SIO_PDR_OOB_CRX_CFG_UIO__PRE 0x0 + +#define SIO_PDR_OOB_DRX_CFG__A 0x7F0024 +#define SIO_PDR_OOB_DRX_CFG__W 9 +#define SIO_PDR_OOB_DRX_CFG__M 0x1FF +#define SIO_PDR_OOB_DRX_CFG__PRE 0x10 +#define SIO_PDR_OOB_DRX_CFG_MODE__B 0 +#define SIO_PDR_OOB_DRX_CFG_MODE__W 3 +#define SIO_PDR_OOB_DRX_CFG_MODE__M 0x7 +#define SIO_PDR_OOB_DRX_CFG_MODE__PRE 0x0 +#define SIO_PDR_OOB_DRX_CFG_DRIVE__B 3 +#define SIO_PDR_OOB_DRX_CFG_DRIVE__W 3 +#define SIO_PDR_OOB_DRX_CFG_DRIVE__M 0x38 +#define SIO_PDR_OOB_DRX_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_OOB_DRX_CFG_KEEP__B 6 +#define SIO_PDR_OOB_DRX_CFG_KEEP__W 2 +#define SIO_PDR_OOB_DRX_CFG_KEEP__M 0xC0 +#define SIO_PDR_OOB_DRX_CFG_KEEP__PRE 0x0 +#define SIO_PDR_OOB_DRX_CFG_UIO__B 8 +#define SIO_PDR_OOB_DRX_CFG_UIO__W 1 +#define SIO_PDR_OOB_DRX_CFG_UIO__M 0x100 +#define SIO_PDR_OOB_DRX_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MSTRT_CFG__A 0x7F0025 +#define SIO_PDR_MSTRT_CFG__W 9 +#define SIO_PDR_MSTRT_CFG__M 0x1FF +#define SIO_PDR_MSTRT_CFG__PRE 0x50 +#define SIO_PDR_MSTRT_CFG_MODE__B 0 +#define SIO_PDR_MSTRT_CFG_MODE__W 3 +#define SIO_PDR_MSTRT_CFG_MODE__M 0x7 +#define SIO_PDR_MSTRT_CFG_MODE__PRE 0x0 +#define SIO_PDR_MSTRT_CFG_DRIVE__B 3 +#define SIO_PDR_MSTRT_CFG_DRIVE__W 3 +#define SIO_PDR_MSTRT_CFG_DRIVE__M 0x38 +#define SIO_PDR_MSTRT_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MSTRT_CFG_KEEP__B 6 +#define SIO_PDR_MSTRT_CFG_KEEP__W 2 +#define SIO_PDR_MSTRT_CFG_KEEP__M 0xC0 +#define SIO_PDR_MSTRT_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MSTRT_CFG_UIO__B 8 +#define SIO_PDR_MSTRT_CFG_UIO__W 1 +#define SIO_PDR_MSTRT_CFG_UIO__M 0x100 +#define SIO_PDR_MSTRT_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MERR_CFG__A 0x7F0026 +#define SIO_PDR_MERR_CFG__W 9 +#define SIO_PDR_MERR_CFG__M 0x1FF +#define SIO_PDR_MERR_CFG__PRE 0x50 +#define SIO_PDR_MERR_CFG_MODE__B 0 +#define SIO_PDR_MERR_CFG_MODE__W 3 +#define SIO_PDR_MERR_CFG_MODE__M 0x7 +#define SIO_PDR_MERR_CFG_MODE__PRE 0x0 +#define SIO_PDR_MERR_CFG_DRIVE__B 3 +#define SIO_PDR_MERR_CFG_DRIVE__W 3 +#define SIO_PDR_MERR_CFG_DRIVE__M 0x38 +#define SIO_PDR_MERR_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MERR_CFG_KEEP__B 6 +#define SIO_PDR_MERR_CFG_KEEP__W 2 +#define SIO_PDR_MERR_CFG_KEEP__M 0xC0 +#define SIO_PDR_MERR_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MERR_CFG_UIO__B 8 +#define SIO_PDR_MERR_CFG_UIO__W 1 +#define SIO_PDR_MERR_CFG_UIO__M 0x100 +#define SIO_PDR_MERR_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MCLK_CFG__A 0x7F0028 +#define SIO_PDR_MCLK_CFG__W 9 +#define SIO_PDR_MCLK_CFG__M 0x1FF +#define SIO_PDR_MCLK_CFG__PRE 0x50 +#define SIO_PDR_MCLK_CFG_MODE__B 0 +#define SIO_PDR_MCLK_CFG_MODE__W 3 +#define SIO_PDR_MCLK_CFG_MODE__M 0x7 +#define SIO_PDR_MCLK_CFG_MODE__PRE 0x0 +#define SIO_PDR_MCLK_CFG_DRIVE__B 3 +#define SIO_PDR_MCLK_CFG_DRIVE__W 3 +#define SIO_PDR_MCLK_CFG_DRIVE__M 0x38 +#define SIO_PDR_MCLK_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MCLK_CFG_KEEP__B 6 +#define SIO_PDR_MCLK_CFG_KEEP__W 2 +#define SIO_PDR_MCLK_CFG_KEEP__M 0xC0 +#define SIO_PDR_MCLK_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MCLK_CFG_UIO__B 8 +#define SIO_PDR_MCLK_CFG_UIO__W 1 +#define SIO_PDR_MCLK_CFG_UIO__M 0x100 +#define SIO_PDR_MCLK_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MVAL_CFG__A 0x7F0029 +#define SIO_PDR_MVAL_CFG__W 9 +#define SIO_PDR_MVAL_CFG__M 0x1FF +#define SIO_PDR_MVAL_CFG__PRE 0x50 +#define SIO_PDR_MVAL_CFG_MODE__B 0 +#define SIO_PDR_MVAL_CFG_MODE__W 3 +#define SIO_PDR_MVAL_CFG_MODE__M 0x7 +#define SIO_PDR_MVAL_CFG_MODE__PRE 0x0 +#define SIO_PDR_MVAL_CFG_DRIVE__B 3 +#define SIO_PDR_MVAL_CFG_DRIVE__W 3 +#define SIO_PDR_MVAL_CFG_DRIVE__M 0x38 +#define SIO_PDR_MVAL_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MVAL_CFG_KEEP__B 6 +#define SIO_PDR_MVAL_CFG_KEEP__W 2 +#define SIO_PDR_MVAL_CFG_KEEP__M 0xC0 +#define SIO_PDR_MVAL_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MVAL_CFG_UIO__B 8 +#define SIO_PDR_MVAL_CFG_UIO__W 1 +#define SIO_PDR_MVAL_CFG_UIO__M 0x100 +#define SIO_PDR_MVAL_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD0_CFG__A 0x7F002A +#define SIO_PDR_MD0_CFG__W 9 +#define SIO_PDR_MD0_CFG__M 0x1FF +#define SIO_PDR_MD0_CFG__PRE 0x50 +#define SIO_PDR_MD0_CFG_MODE__B 0 +#define SIO_PDR_MD0_CFG_MODE__W 3 +#define SIO_PDR_MD0_CFG_MODE__M 0x7 +#define SIO_PDR_MD0_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD0_CFG_DRIVE__B 3 +#define SIO_PDR_MD0_CFG_DRIVE__W 3 +#define SIO_PDR_MD0_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD0_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD0_CFG_KEEP__B 6 +#define SIO_PDR_MD0_CFG_KEEP__W 2 +#define SIO_PDR_MD0_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD0_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD0_CFG_UIO__B 8 +#define SIO_PDR_MD0_CFG_UIO__W 1 +#define SIO_PDR_MD0_CFG_UIO__M 0x100 +#define SIO_PDR_MD0_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD1_CFG__A 0x7F002B +#define SIO_PDR_MD1_CFG__W 9 +#define SIO_PDR_MD1_CFG__M 0x1FF +#define SIO_PDR_MD1_CFG__PRE 0x50 +#define SIO_PDR_MD1_CFG_MODE__B 0 +#define SIO_PDR_MD1_CFG_MODE__W 3 +#define SIO_PDR_MD1_CFG_MODE__M 0x7 +#define SIO_PDR_MD1_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD1_CFG_DRIVE__B 3 +#define SIO_PDR_MD1_CFG_DRIVE__W 3 +#define SIO_PDR_MD1_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD1_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD1_CFG_KEEP__B 6 +#define SIO_PDR_MD1_CFG_KEEP__W 2 +#define SIO_PDR_MD1_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD1_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD1_CFG_UIO__B 8 +#define SIO_PDR_MD1_CFG_UIO__W 1 +#define SIO_PDR_MD1_CFG_UIO__M 0x100 +#define SIO_PDR_MD1_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD2_CFG__A 0x7F002C +#define SIO_PDR_MD2_CFG__W 9 +#define SIO_PDR_MD2_CFG__M 0x1FF +#define SIO_PDR_MD2_CFG__PRE 0x50 +#define SIO_PDR_MD2_CFG_MODE__B 0 +#define SIO_PDR_MD2_CFG_MODE__W 3 +#define SIO_PDR_MD2_CFG_MODE__M 0x7 +#define SIO_PDR_MD2_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD2_CFG_DRIVE__B 3 +#define SIO_PDR_MD2_CFG_DRIVE__W 3 +#define SIO_PDR_MD2_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD2_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD2_CFG_KEEP__B 6 +#define SIO_PDR_MD2_CFG_KEEP__W 2 +#define SIO_PDR_MD2_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD2_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD2_CFG_UIO__B 8 +#define SIO_PDR_MD2_CFG_UIO__W 1 +#define SIO_PDR_MD2_CFG_UIO__M 0x100 +#define SIO_PDR_MD2_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD3_CFG__A 0x7F002D +#define SIO_PDR_MD3_CFG__W 9 +#define SIO_PDR_MD3_CFG__M 0x1FF +#define SIO_PDR_MD3_CFG__PRE 0x50 +#define SIO_PDR_MD3_CFG_MODE__B 0 +#define SIO_PDR_MD3_CFG_MODE__W 3 +#define SIO_PDR_MD3_CFG_MODE__M 0x7 +#define SIO_PDR_MD3_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD3_CFG_DRIVE__B 3 +#define SIO_PDR_MD3_CFG_DRIVE__W 3 +#define SIO_PDR_MD3_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD3_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD3_CFG_KEEP__B 6 +#define SIO_PDR_MD3_CFG_KEEP__W 2 +#define SIO_PDR_MD3_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD3_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD3_CFG_UIO__B 8 +#define SIO_PDR_MD3_CFG_UIO__W 1 +#define SIO_PDR_MD3_CFG_UIO__M 0x100 +#define SIO_PDR_MD3_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD4_CFG__A 0x7F002F +#define SIO_PDR_MD4_CFG__W 9 +#define SIO_PDR_MD4_CFG__M 0x1FF +#define SIO_PDR_MD4_CFG__PRE 0x50 +#define SIO_PDR_MD4_CFG_MODE__B 0 +#define SIO_PDR_MD4_CFG_MODE__W 3 +#define SIO_PDR_MD4_CFG_MODE__M 0x7 +#define SIO_PDR_MD4_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD4_CFG_DRIVE__B 3 +#define SIO_PDR_MD4_CFG_DRIVE__W 3 +#define SIO_PDR_MD4_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD4_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD4_CFG_KEEP__B 6 +#define SIO_PDR_MD4_CFG_KEEP__W 2 +#define SIO_PDR_MD4_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD4_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD4_CFG_UIO__B 8 +#define SIO_PDR_MD4_CFG_UIO__W 1 +#define SIO_PDR_MD4_CFG_UIO__M 0x100 +#define SIO_PDR_MD4_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD5_CFG__A 0x7F0030 +#define SIO_PDR_MD5_CFG__W 9 +#define SIO_PDR_MD5_CFG__M 0x1FF +#define SIO_PDR_MD5_CFG__PRE 0x50 +#define SIO_PDR_MD5_CFG_MODE__B 0 +#define SIO_PDR_MD5_CFG_MODE__W 3 +#define SIO_PDR_MD5_CFG_MODE__M 0x7 +#define SIO_PDR_MD5_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD5_CFG_DRIVE__B 3 +#define SIO_PDR_MD5_CFG_DRIVE__W 3 +#define SIO_PDR_MD5_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD5_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD5_CFG_KEEP__B 6 +#define SIO_PDR_MD5_CFG_KEEP__W 2 +#define SIO_PDR_MD5_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD5_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD5_CFG_UIO__B 8 +#define SIO_PDR_MD5_CFG_UIO__W 1 +#define SIO_PDR_MD5_CFG_UIO__M 0x100 +#define SIO_PDR_MD5_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD6_CFG__A 0x7F0031 +#define SIO_PDR_MD6_CFG__W 9 +#define SIO_PDR_MD6_CFG__M 0x1FF +#define SIO_PDR_MD6_CFG__PRE 0x50 +#define SIO_PDR_MD6_CFG_MODE__B 0 +#define SIO_PDR_MD6_CFG_MODE__W 3 +#define SIO_PDR_MD6_CFG_MODE__M 0x7 +#define SIO_PDR_MD6_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD6_CFG_DRIVE__B 3 +#define SIO_PDR_MD6_CFG_DRIVE__W 3 +#define SIO_PDR_MD6_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD6_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD6_CFG_KEEP__B 6 +#define SIO_PDR_MD6_CFG_KEEP__W 2 +#define SIO_PDR_MD6_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD6_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD6_CFG_UIO__B 8 +#define SIO_PDR_MD6_CFG_UIO__W 1 +#define SIO_PDR_MD6_CFG_UIO__M 0x100 +#define SIO_PDR_MD6_CFG_UIO__PRE 0x0 + +#define SIO_PDR_MD7_CFG__A 0x7F0032 +#define SIO_PDR_MD7_CFG__W 9 +#define SIO_PDR_MD7_CFG__M 0x1FF +#define SIO_PDR_MD7_CFG__PRE 0x50 +#define SIO_PDR_MD7_CFG_MODE__B 0 +#define SIO_PDR_MD7_CFG_MODE__W 3 +#define SIO_PDR_MD7_CFG_MODE__M 0x7 +#define SIO_PDR_MD7_CFG_MODE__PRE 0x0 +#define SIO_PDR_MD7_CFG_DRIVE__B 3 +#define SIO_PDR_MD7_CFG_DRIVE__W 3 +#define SIO_PDR_MD7_CFG_DRIVE__M 0x38 +#define SIO_PDR_MD7_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_MD7_CFG_KEEP__B 6 +#define SIO_PDR_MD7_CFG_KEEP__W 2 +#define SIO_PDR_MD7_CFG_KEEP__M 0xC0 +#define SIO_PDR_MD7_CFG_KEEP__PRE 0x40 +#define SIO_PDR_MD7_CFG_UIO__B 8 +#define SIO_PDR_MD7_CFG_UIO__W 1 +#define SIO_PDR_MD7_CFG_UIO__M 0x100 +#define SIO_PDR_MD7_CFG_UIO__PRE 0x0 + +#define SIO_PDR_I2C_SCL1_CFG__A 0x7F0033 +#define SIO_PDR_I2C_SCL1_CFG__W 9 +#define SIO_PDR_I2C_SCL1_CFG__M 0x1FF +#define SIO_PDR_I2C_SCL1_CFG__PRE 0x11 +#define SIO_PDR_I2C_SCL1_CFG_MODE__B 0 +#define SIO_PDR_I2C_SCL1_CFG_MODE__W 3 +#define SIO_PDR_I2C_SCL1_CFG_MODE__M 0x7 +#define SIO_PDR_I2C_SCL1_CFG_MODE__PRE 0x1 +#define SIO_PDR_I2C_SCL1_CFG_DRIVE__B 3 +#define SIO_PDR_I2C_SCL1_CFG_DRIVE__W 3 +#define SIO_PDR_I2C_SCL1_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2C_SCL1_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2C_SCL1_CFG_KEEP__B 6 +#define SIO_PDR_I2C_SCL1_CFG_KEEP__W 2 +#define SIO_PDR_I2C_SCL1_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2C_SCL1_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2C_SCL1_CFG_UIO__B 8 +#define SIO_PDR_I2C_SCL1_CFG_UIO__W 1 +#define SIO_PDR_I2C_SCL1_CFG_UIO__M 0x100 +#define SIO_PDR_I2C_SCL1_CFG_UIO__PRE 0x0 + +#define SIO_PDR_I2C_SDA1_CFG__A 0x7F0034 +#define SIO_PDR_I2C_SDA1_CFG__W 9 +#define SIO_PDR_I2C_SDA1_CFG__M 0x1FF +#define SIO_PDR_I2C_SDA1_CFG__PRE 0x11 +#define SIO_PDR_I2C_SDA1_CFG_MODE__B 0 +#define SIO_PDR_I2C_SDA1_CFG_MODE__W 3 +#define SIO_PDR_I2C_SDA1_CFG_MODE__M 0x7 +#define SIO_PDR_I2C_SDA1_CFG_MODE__PRE 0x1 +#define SIO_PDR_I2C_SDA1_CFG_DRIVE__B 3 +#define SIO_PDR_I2C_SDA1_CFG_DRIVE__W 3 +#define SIO_PDR_I2C_SDA1_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2C_SDA1_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2C_SDA1_CFG_KEEP__B 6 +#define SIO_PDR_I2C_SDA1_CFG_KEEP__W 2 +#define SIO_PDR_I2C_SDA1_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2C_SDA1_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2C_SDA1_CFG_UIO__B 8 +#define SIO_PDR_I2C_SDA1_CFG_UIO__W 1 +#define SIO_PDR_I2C_SDA1_CFG_UIO__M 0x100 +#define SIO_PDR_I2C_SDA1_CFG_UIO__PRE 0x0 + +#define SIO_PDR_VSYNC_CFG__A 0x7F0036 +#define SIO_PDR_VSYNC_CFG__W 9 +#define SIO_PDR_VSYNC_CFG__M 0x1FF +#define SIO_PDR_VSYNC_CFG__PRE 0x10 +#define SIO_PDR_VSYNC_CFG_MODE__B 0 +#define SIO_PDR_VSYNC_CFG_MODE__W 3 +#define SIO_PDR_VSYNC_CFG_MODE__M 0x7 +#define SIO_PDR_VSYNC_CFG_MODE__PRE 0x0 +#define SIO_PDR_VSYNC_CFG_DRIVE__B 3 +#define SIO_PDR_VSYNC_CFG_DRIVE__W 3 +#define SIO_PDR_VSYNC_CFG_DRIVE__M 0x38 +#define SIO_PDR_VSYNC_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_VSYNC_CFG_KEEP__B 6 +#define SIO_PDR_VSYNC_CFG_KEEP__W 2 +#define SIO_PDR_VSYNC_CFG_KEEP__M 0xC0 +#define SIO_PDR_VSYNC_CFG_KEEP__PRE 0x0 +#define SIO_PDR_VSYNC_CFG_UIO__B 8 +#define SIO_PDR_VSYNC_CFG_UIO__W 1 +#define SIO_PDR_VSYNC_CFG_UIO__M 0x100 +#define SIO_PDR_VSYNC_CFG_UIO__PRE 0x0 + +#define SIO_PDR_SMA_RX_CFG__A 0x7F0037 +#define SIO_PDR_SMA_RX_CFG__W 9 +#define SIO_PDR_SMA_RX_CFG__M 0x1FF +#define SIO_PDR_SMA_RX_CFG__PRE 0x10 +#define SIO_PDR_SMA_RX_CFG_MODE__B 0 +#define SIO_PDR_SMA_RX_CFG_MODE__W 3 +#define SIO_PDR_SMA_RX_CFG_MODE__M 0x7 +#define SIO_PDR_SMA_RX_CFG_MODE__PRE 0x0 +#define SIO_PDR_SMA_RX_CFG_DRIVE__B 3 +#define SIO_PDR_SMA_RX_CFG_DRIVE__W 3 +#define SIO_PDR_SMA_RX_CFG_DRIVE__M 0x38 +#define SIO_PDR_SMA_RX_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_SMA_RX_CFG_KEEP__B 6 +#define SIO_PDR_SMA_RX_CFG_KEEP__W 2 +#define SIO_PDR_SMA_RX_CFG_KEEP__M 0xC0 +#define SIO_PDR_SMA_RX_CFG_KEEP__PRE 0x0 +#define SIO_PDR_SMA_RX_CFG_UIO__B 8 +#define SIO_PDR_SMA_RX_CFG_UIO__W 1 +#define SIO_PDR_SMA_RX_CFG_UIO__M 0x100 +#define SIO_PDR_SMA_RX_CFG_UIO__PRE 0x0 + +#define SIO_PDR_SMA_TX_CFG__A 0x7F0038 +#define SIO_PDR_SMA_TX_CFG__W 9 +#define SIO_PDR_SMA_TX_CFG__M 0x1FF +#define SIO_PDR_SMA_TX_CFG__PRE 0x90 +#define SIO_PDR_SMA_TX_CFG_MODE__B 0 +#define SIO_PDR_SMA_TX_CFG_MODE__W 3 +#define SIO_PDR_SMA_TX_CFG_MODE__M 0x7 +#define SIO_PDR_SMA_TX_CFG_MODE__PRE 0x0 +#define SIO_PDR_SMA_TX_CFG_DRIVE__B 3 +#define SIO_PDR_SMA_TX_CFG_DRIVE__W 3 +#define SIO_PDR_SMA_TX_CFG_DRIVE__M 0x38 +#define SIO_PDR_SMA_TX_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_SMA_TX_CFG_KEEP__B 6 +#define SIO_PDR_SMA_TX_CFG_KEEP__W 2 +#define SIO_PDR_SMA_TX_CFG_KEEP__M 0xC0 +#define SIO_PDR_SMA_TX_CFG_KEEP__PRE 0x80 +#define SIO_PDR_SMA_TX_CFG_UIO__B 8 +#define SIO_PDR_SMA_TX_CFG_UIO__W 1 +#define SIO_PDR_SMA_TX_CFG_UIO__M 0x100 +#define SIO_PDR_SMA_TX_CFG_UIO__PRE 0x0 + +#define SIO_PDR_I2C_SDA2_CFG__A 0x7F003F +#define SIO_PDR_I2C_SDA2_CFG__W 9 +#define SIO_PDR_I2C_SDA2_CFG__M 0x1FF +#define SIO_PDR_I2C_SDA2_CFG__PRE 0x11 +#define SIO_PDR_I2C_SDA2_CFG_MODE__B 0 +#define SIO_PDR_I2C_SDA2_CFG_MODE__W 3 +#define SIO_PDR_I2C_SDA2_CFG_MODE__M 0x7 +#define SIO_PDR_I2C_SDA2_CFG_MODE__PRE 0x1 +#define SIO_PDR_I2C_SDA2_CFG_DRIVE__B 3 +#define SIO_PDR_I2C_SDA2_CFG_DRIVE__W 3 +#define SIO_PDR_I2C_SDA2_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2C_SDA2_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2C_SDA2_CFG_KEEP__B 6 +#define SIO_PDR_I2C_SDA2_CFG_KEEP__W 2 +#define SIO_PDR_I2C_SDA2_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2C_SDA2_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2C_SDA2_CFG_UIO__B 8 +#define SIO_PDR_I2C_SDA2_CFG_UIO__W 1 +#define SIO_PDR_I2C_SDA2_CFG_UIO__M 0x100 +#define SIO_PDR_I2C_SDA2_CFG_UIO__PRE 0x0 + +#define SIO_PDR_I2C_SCL2_CFG__A 0x7F0040 +#define SIO_PDR_I2C_SCL2_CFG__W 9 +#define SIO_PDR_I2C_SCL2_CFG__M 0x1FF +#define SIO_PDR_I2C_SCL2_CFG__PRE 0x11 +#define SIO_PDR_I2C_SCL2_CFG_MODE__B 0 +#define SIO_PDR_I2C_SCL2_CFG_MODE__W 3 +#define SIO_PDR_I2C_SCL2_CFG_MODE__M 0x7 +#define SIO_PDR_I2C_SCL2_CFG_MODE__PRE 0x1 +#define SIO_PDR_I2C_SCL2_CFG_DRIVE__B 3 +#define SIO_PDR_I2C_SCL2_CFG_DRIVE__W 3 +#define SIO_PDR_I2C_SCL2_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2C_SCL2_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2C_SCL2_CFG_KEEP__B 6 +#define SIO_PDR_I2C_SCL2_CFG_KEEP__W 2 +#define SIO_PDR_I2C_SCL2_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2C_SCL2_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2C_SCL2_CFG_UIO__B 8 +#define SIO_PDR_I2C_SCL2_CFG_UIO__W 1 +#define SIO_PDR_I2C_SCL2_CFG_UIO__M 0x100 +#define SIO_PDR_I2C_SCL2_CFG_UIO__PRE 0x0 + +#define SIO_PDR_I2S_CL_CFG__A 0x7F0041 +#define SIO_PDR_I2S_CL_CFG__W 9 +#define SIO_PDR_I2S_CL_CFG__M 0x1FF +#define SIO_PDR_I2S_CL_CFG__PRE 0x10 +#define SIO_PDR_I2S_CL_CFG_MODE__B 0 +#define SIO_PDR_I2S_CL_CFG_MODE__W 3 +#define SIO_PDR_I2S_CL_CFG_MODE__M 0x7 +#define SIO_PDR_I2S_CL_CFG_MODE__PRE 0x0 +#define SIO_PDR_I2S_CL_CFG_DRIVE__B 3 +#define SIO_PDR_I2S_CL_CFG_DRIVE__W 3 +#define SIO_PDR_I2S_CL_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2S_CL_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2S_CL_CFG_KEEP__B 6 +#define SIO_PDR_I2S_CL_CFG_KEEP__W 2 +#define SIO_PDR_I2S_CL_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2S_CL_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2S_CL_CFG_UIO__B 8 +#define SIO_PDR_I2S_CL_CFG_UIO__W 1 +#define SIO_PDR_I2S_CL_CFG_UIO__M 0x100 +#define SIO_PDR_I2S_CL_CFG_UIO__PRE 0x0 + +#define SIO_PDR_I2S_DA_CFG__A 0x7F0042 +#define SIO_PDR_I2S_DA_CFG__W 9 +#define SIO_PDR_I2S_DA_CFG__M 0x1FF +#define SIO_PDR_I2S_DA_CFG__PRE 0x10 +#define SIO_PDR_I2S_DA_CFG_MODE__B 0 +#define SIO_PDR_I2S_DA_CFG_MODE__W 3 +#define SIO_PDR_I2S_DA_CFG_MODE__M 0x7 +#define SIO_PDR_I2S_DA_CFG_MODE__PRE 0x0 +#define SIO_PDR_I2S_DA_CFG_DRIVE__B 3 +#define SIO_PDR_I2S_DA_CFG_DRIVE__W 3 +#define SIO_PDR_I2S_DA_CFG_DRIVE__M 0x38 +#define SIO_PDR_I2S_DA_CFG_DRIVE__PRE 0x10 +#define SIO_PDR_I2S_DA_CFG_KEEP__B 6 +#define SIO_PDR_I2S_DA_CFG_KEEP__W 2 +#define SIO_PDR_I2S_DA_CFG_KEEP__M 0xC0 +#define SIO_PDR_I2S_DA_CFG_KEEP__PRE 0x0 +#define SIO_PDR_I2S_DA_CFG_UIO__B 8 +#define SIO_PDR_I2S_DA_CFG_UIO__W 1 +#define SIO_PDR_I2S_DA_CFG_UIO__M 0x100 +#define SIO_PDR_I2S_DA_CFG_UIO__PRE 0x0 + +#define SIO_PDR_GPIO_GPIO_FNC__A 0x7F0050 +#define SIO_PDR_GPIO_GPIO_FNC__W 2 +#define SIO_PDR_GPIO_GPIO_FNC__M 0x3 +#define SIO_PDR_GPIO_GPIO_FNC__PRE 0x0 +#define SIO_PDR_GPIO_GPIO_FNC_SEL__B 0 +#define SIO_PDR_GPIO_GPIO_FNC_SEL__W 2 +#define SIO_PDR_GPIO_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_GPIO_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_IRQN_GPIO_FNC__A 0x7F0051 +#define SIO_PDR_IRQN_GPIO_FNC__W 2 +#define SIO_PDR_IRQN_GPIO_FNC__M 0x3 +#define SIO_PDR_IRQN_GPIO_FNC__PRE 0x0 +#define SIO_PDR_IRQN_GPIO_FNC_SEL__B 0 +#define SIO_PDR_IRQN_GPIO_FNC_SEL__W 2 +#define SIO_PDR_IRQN_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_IRQN_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MSTRT_GPIO_FNC__A 0x7F0052 +#define SIO_PDR_MSTRT_GPIO_FNC__W 2 +#define SIO_PDR_MSTRT_GPIO_FNC__M 0x3 +#define SIO_PDR_MSTRT_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MSTRT_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MSTRT_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MSTRT_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MSTRT_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MERR_GPIO_FNC__A 0x7F0053 +#define SIO_PDR_MERR_GPIO_FNC__W 2 +#define SIO_PDR_MERR_GPIO_FNC__M 0x3 +#define SIO_PDR_MERR_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MERR_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MERR_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MERR_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MERR_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MCLK_GPIO_FNC__A 0x7F0054 +#define SIO_PDR_MCLK_GPIO_FNC__W 2 +#define SIO_PDR_MCLK_GPIO_FNC__M 0x3 +#define SIO_PDR_MCLK_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MCLK_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MCLK_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MCLK_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MCLK_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MVAL_GPIO_FNC__A 0x7F0055 +#define SIO_PDR_MVAL_GPIO_FNC__W 2 +#define SIO_PDR_MVAL_GPIO_FNC__M 0x3 +#define SIO_PDR_MVAL_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MVAL_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MVAL_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MVAL_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MVAL_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD0_GPIO_FNC__A 0x7F0056 +#define SIO_PDR_MD0_GPIO_FNC__W 2 +#define SIO_PDR_MD0_GPIO_FNC__M 0x3 +#define SIO_PDR_MD0_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD0_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD0_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD0_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD0_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD1_GPIO_FNC__A 0x7F0057 +#define SIO_PDR_MD1_GPIO_FNC__W 2 +#define SIO_PDR_MD1_GPIO_FNC__M 0x3 +#define SIO_PDR_MD1_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD1_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD1_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD1_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD1_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD2_GPIO_FNC__A 0x7F0058 +#define SIO_PDR_MD2_GPIO_FNC__W 2 +#define SIO_PDR_MD2_GPIO_FNC__M 0x3 +#define SIO_PDR_MD2_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD2_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD2_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD2_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD2_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD3_GPIO_FNC__A 0x7F0059 +#define SIO_PDR_MD3_GPIO_FNC__W 2 +#define SIO_PDR_MD3_GPIO_FNC__M 0x3 +#define SIO_PDR_MD3_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD3_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD3_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD3_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD3_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD4_GPIO_FNC__A 0x7F005A +#define SIO_PDR_MD4_GPIO_FNC__W 2 +#define SIO_PDR_MD4_GPIO_FNC__M 0x3 +#define SIO_PDR_MD4_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD4_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD4_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD4_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD4_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD5_GPIO_FNC__A 0x7F005B +#define SIO_PDR_MD5_GPIO_FNC__W 2 +#define SIO_PDR_MD5_GPIO_FNC__M 0x3 +#define SIO_PDR_MD5_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD5_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD5_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD5_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD5_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD6_GPIO_FNC__A 0x7F005C +#define SIO_PDR_MD6_GPIO_FNC__W 2 +#define SIO_PDR_MD6_GPIO_FNC__M 0x3 +#define SIO_PDR_MD6_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD6_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD6_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD6_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD6_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_MD7_GPIO_FNC__A 0x7F005D +#define SIO_PDR_MD7_GPIO_FNC__W 2 +#define SIO_PDR_MD7_GPIO_FNC__M 0x3 +#define SIO_PDR_MD7_GPIO_FNC__PRE 0x0 +#define SIO_PDR_MD7_GPIO_FNC_SEL__B 0 +#define SIO_PDR_MD7_GPIO_FNC_SEL__W 2 +#define SIO_PDR_MD7_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_MD7_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_SMA_RX_GPIO_FNC__A 0x7F005E +#define SIO_PDR_SMA_RX_GPIO_FNC__W 2 +#define SIO_PDR_SMA_RX_GPIO_FNC__M 0x3 +#define SIO_PDR_SMA_RX_GPIO_FNC__PRE 0x0 +#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__B 0 +#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__W 2 +#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__PRE 0x0 + +#define SIO_PDR_SMA_TX_GPIO_FNC__A 0x7F005F +#define SIO_PDR_SMA_TX_GPIO_FNC__W 2 +#define SIO_PDR_SMA_TX_GPIO_FNC__M 0x3 +#define SIO_PDR_SMA_TX_GPIO_FNC__PRE 0x0 +#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__B 0 +#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__W 2 +#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__M 0x3 +#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__PRE 0x0 + +#define VSB_COMM_EXEC__A 0x1C00000 +#define VSB_COMM_EXEC__W 2 +#define VSB_COMM_EXEC__M 0x3 +#define VSB_COMM_EXEC__PRE 0x0 +#define VSB_COMM_EXEC_STOP 0x0 +#define VSB_COMM_EXEC_ACTIVE 0x1 +#define VSB_COMM_EXEC_HOLD 0x2 + +#define VSB_COMM_MB__A 0x1C00002 +#define VSB_COMM_MB__W 16 +#define VSB_COMM_MB__M 0xFFFF +#define VSB_COMM_MB__PRE 0x0 +#define VSB_COMM_INT_REQ__A 0x1C00003 +#define VSB_COMM_INT_REQ__W 1 +#define VSB_COMM_INT_REQ__M 0x1 +#define VSB_COMM_INT_REQ__PRE 0x0 + +#define VSB_COMM_INT_REQ_TOP_INT_REQ__B 0 +#define VSB_COMM_INT_REQ_TOP_INT_REQ__W 1 +#define VSB_COMM_INT_REQ_TOP_INT_REQ__M 0x1 +#define VSB_COMM_INT_REQ_TOP_INT_REQ__PRE 0x0 + +#define VSB_COMM_INT_STA__A 0x1C00005 +#define VSB_COMM_INT_STA__W 16 +#define VSB_COMM_INT_STA__M 0xFFFF +#define VSB_COMM_INT_STA__PRE 0x0 + +#define VSB_COMM_INT_MSK__A 0x1C00006 +#define VSB_COMM_INT_MSK__W 16 +#define VSB_COMM_INT_MSK__M 0xFFFF +#define VSB_COMM_INT_MSK__PRE 0x0 + +#define VSB_COMM_INT_STM__A 0x1C00007 +#define VSB_COMM_INT_STM__W 16 +#define VSB_COMM_INT_STM__M 0xFFFF +#define VSB_COMM_INT_STM__PRE 0x0 + +#define VSB_TOP_COMM_EXEC__A 0x1C10000 +#define VSB_TOP_COMM_EXEC__W 2 +#define VSB_TOP_COMM_EXEC__M 0x3 +#define VSB_TOP_COMM_EXEC__PRE 0x0 +#define VSB_TOP_COMM_EXEC_STOP 0x0 +#define VSB_TOP_COMM_EXEC_ACTIVE 0x1 +#define VSB_TOP_COMM_EXEC_HOLD 0x2 + +#define VSB_TOP_COMM_MB__A 0x1C10002 +#define VSB_TOP_COMM_MB__W 10 +#define VSB_TOP_COMM_MB__M 0x3FF +#define VSB_TOP_COMM_MB__PRE 0x0 + +#define VSB_TOP_COMM_MB_CTL__B 0 +#define VSB_TOP_COMM_MB_CTL__W 1 +#define VSB_TOP_COMM_MB_CTL__M 0x1 +#define VSB_TOP_COMM_MB_CTL__PRE 0x0 +#define VSB_TOP_COMM_MB_CTL_CTL_OFF 0x0 +#define VSB_TOP_COMM_MB_CTL_CTL_ON 0x1 + +#define VSB_TOP_COMM_MB_OBS__B 1 +#define VSB_TOP_COMM_MB_OBS__W 1 +#define VSB_TOP_COMM_MB_OBS__M 0x2 +#define VSB_TOP_COMM_MB_OBS__PRE 0x0 +#define VSB_TOP_COMM_MB_OBS_OBS_OFF 0x0 +#define VSB_TOP_COMM_MB_OBS_OBS_ON 0x2 + +#define VSB_TOP_COMM_MB_MUX_CTL__B 2 +#define VSB_TOP_COMM_MB_MUX_CTL__W 4 +#define VSB_TOP_COMM_MB_MUX_CTL__M 0x3C +#define VSB_TOP_COMM_MB_MUX_CTL__PRE 0x0 + +#define VSB_TOP_COMM_MB_MUX_OBS__B 6 +#define VSB_TOP_COMM_MB_MUX_OBS__W 4 +#define VSB_TOP_COMM_MB_MUX_OBS__M 0x3C0 +#define VSB_TOP_COMM_MB_MUX_OBS__PRE 0x0 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FEC 0x0 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_IQM 0x40 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_IQM_AMPLITUDE 0x80 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_TCMEQ_1 0xC0 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_TCMEQ_2 0x100 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FFE_1 0x140 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FFE_2 0x180 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_DFE_1 0x1C0 +#define VSB_TOP_COMM_MB_MUX_OBS_VSB_DFE_2 0x200 + +#define VSB_TOP_COMM_INT_REQ__A 0x1C10003 +#define VSB_TOP_COMM_INT_REQ__W 1 +#define VSB_TOP_COMM_INT_REQ__M 0x1 +#define VSB_TOP_COMM_INT_REQ__PRE 0x0 +#define VSB_TOP_COMM_INT_STA__A 0x1C10005 +#define VSB_TOP_COMM_INT_STA__W 6 +#define VSB_TOP_COMM_INT_STA__M 0x3F +#define VSB_TOP_COMM_INT_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__B 0 +#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__W 1 +#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__M 0x1 +#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_STA_LOCK_STA__B 1 +#define VSB_TOP_COMM_INT_STA_LOCK_STA__W 1 +#define VSB_TOP_COMM_INT_STA_LOCK_STA__M 0x2 +#define VSB_TOP_COMM_INT_STA_LOCK_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__B 2 +#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__W 1 +#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__M 0x4 +#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__B 3 +#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__W 1 +#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__M 0x8 +#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__B 4 +#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__W 1 +#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__M 0x10 +#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_STA_MERSER_STA__B 5 +#define VSB_TOP_COMM_INT_STA_MERSER_STA__W 1 +#define VSB_TOP_COMM_INT_STA_MERSER_STA__M 0x20 +#define VSB_TOP_COMM_INT_STA_MERSER_STA__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK__A 0x1C10006 +#define VSB_TOP_COMM_INT_MSK__W 6 +#define VSB_TOP_COMM_INT_MSK__M 0x3F +#define VSB_TOP_COMM_INT_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__B 0 +#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__W 1 +#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__M 0x1 +#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__B 1 +#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__W 1 +#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__M 0x2 +#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__B 2 +#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__W 1 +#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__M 0x4 +#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__B 3 +#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__W 1 +#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__M 0x8 +#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__B 4 +#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__W 1 +#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__M 0x10 +#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__B 5 +#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__W 1 +#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__M 0x20 +#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM__A 0x1C10007 +#define VSB_TOP_COMM_INT_STM__W 6 +#define VSB_TOP_COMM_INT_STM__M 0x3F +#define VSB_TOP_COMM_INT_STM__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__B 0 +#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__W 1 +#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__M 0x1 +#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM_LOCK_STM__B 1 +#define VSB_TOP_COMM_INT_STM_LOCK_STM__W 1 +#define VSB_TOP_COMM_INT_STM_LOCK_STM__M 0x2 +#define VSB_TOP_COMM_INT_STM_LOCK_STM__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__B 2 +#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__W 1 +#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__M 0x4 +#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__B 3 +#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__W 1 +#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__M 0x8 +#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__B 4 +#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__W 1 +#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__M 0x10 +#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__PRE 0x0 + +#define VSB_TOP_COMM_INT_STM_MERSER_STM__B 5 +#define VSB_TOP_COMM_INT_STM_MERSER_STM__W 1 +#define VSB_TOP_COMM_INT_STM_MERSER_STM__M 0x20 +#define VSB_TOP_COMM_INT_STM_MERSER_STM__PRE 0x0 + +#define VSB_TOP_CKGN1ACQ__A 0x1C10010 +#define VSB_TOP_CKGN1ACQ__W 8 +#define VSB_TOP_CKGN1ACQ__M 0xFF +#define VSB_TOP_CKGN1ACQ__PRE 0x4 + +#define VSB_TOP_CKGN1TRK__A 0x1C10011 +#define VSB_TOP_CKGN1TRK__W 8 +#define VSB_TOP_CKGN1TRK__M 0xFF +#define VSB_TOP_CKGN1TRK__PRE 0x0 + +#define VSB_TOP_CKGN2ACQ__A 0x1C10012 +#define VSB_TOP_CKGN2ACQ__W 8 +#define VSB_TOP_CKGN2ACQ__M 0xFF +#define VSB_TOP_CKGN2ACQ__PRE 0x2 + +#define VSB_TOP_CKGN2TRK__A 0x1C10013 +#define VSB_TOP_CKGN2TRK__W 8 +#define VSB_TOP_CKGN2TRK__M 0xFF +#define VSB_TOP_CKGN2TRK__PRE 0x1 + +#define VSB_TOP_CKGN3__A 0x1C10014 +#define VSB_TOP_CKGN3__W 8 +#define VSB_TOP_CKGN3__M 0xFF +#define VSB_TOP_CKGN3__PRE 0x5 + +#define VSB_TOP_CYGN1ACQ__A 0x1C10015 +#define VSB_TOP_CYGN1ACQ__W 8 +#define VSB_TOP_CYGN1ACQ__M 0xFF +#define VSB_TOP_CYGN1ACQ__PRE 0x3 + +#define VSB_TOP_CYGN1TRK__A 0x1C10016 +#define VSB_TOP_CYGN1TRK__W 8 +#define VSB_TOP_CYGN1TRK__M 0xFF +#define VSB_TOP_CYGN1TRK__PRE 0x0 + +#define VSB_TOP_CYGN2ACQ__A 0x1C10017 +#define VSB_TOP_CYGN2ACQ__W 8 +#define VSB_TOP_CYGN2ACQ__M 0xFF +#define VSB_TOP_CYGN2ACQ__PRE 0x3 + +#define VSB_TOP_CYGN2TRK__A 0x1C10018 +#define VSB_TOP_CYGN2TRK__W 8 +#define VSB_TOP_CYGN2TRK__M 0xFF +#define VSB_TOP_CYGN2TRK__PRE 0x2 + +#define VSB_TOP_CYGN3__A 0x1C10019 +#define VSB_TOP_CYGN3__W 8 +#define VSB_TOP_CYGN3__M 0xFF +#define VSB_TOP_CYGN3__PRE 0x6 +#define VSB_TOP_SYNCCTRLWORD__A 0x1C1001A +#define VSB_TOP_SYNCCTRLWORD__W 5 +#define VSB_TOP_SYNCCTRLWORD__M 0x1F +#define VSB_TOP_SYNCCTRLWORD__PRE 0x0 + +#define VSB_TOP_SYNCCTRLWORD_PRST__B 0 +#define VSB_TOP_SYNCCTRLWORD_PRST__W 1 +#define VSB_TOP_SYNCCTRLWORD_PRST__M 0x1 +#define VSB_TOP_SYNCCTRLWORD_PRST__PRE 0x0 + +#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__B 1 +#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__W 1 +#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__M 0x2 +#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__PRE 0x0 + +#define VSB_TOP_SYNCCTRLWORD_INVCNST__B 2 +#define VSB_TOP_SYNCCTRLWORD_INVCNST__W 1 +#define VSB_TOP_SYNCCTRLWORD_INVCNST__M 0x4 +#define VSB_TOP_SYNCCTRLWORD_INVCNST__PRE 0x0 + +#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__B 3 +#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__W 1 +#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__M 0x8 +#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__PRE 0x0 + +#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__B 4 +#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__W 1 +#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__M 0x10 +#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__PRE 0x0 + +#define VSB_TOP_MAINSMUP__A 0x1C1001B +#define VSB_TOP_MAINSMUP__W 8 +#define VSB_TOP_MAINSMUP__M 0xFF +#define VSB_TOP_MAINSMUP__PRE 0xFF + +#define VSB_TOP_EQSMUP__A 0x1C1001C +#define VSB_TOP_EQSMUP__W 8 +#define VSB_TOP_EQSMUP__M 0xFF +#define VSB_TOP_EQSMUP__PRE 0xFF +#define VSB_TOP_SYSMUXCTRL__A 0x1C1001D +#define VSB_TOP_SYSMUXCTRL__W 13 +#define VSB_TOP_SYSMUXCTRL__M 0x1FFF +#define VSB_TOP_SYSMUXCTRL__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__B 0 +#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__M 0x1 +#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__B 1 +#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__M 0x2 +#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__B 2 +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__M 0x4 +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__B 3 +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__M 0x8 +#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__B 4 +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__M 0x10 +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__B 5 +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__M 0x20 +#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__B 6 +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__M 0x40 +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__B 7 +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__M 0x80 +#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__B 8 +#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__W 4 +#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__M 0xF00 +#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__PRE 0x0 + +#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__B 12 +#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__W 1 +#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__M 0x1000 +#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__PRE 0x0 + +#define VSB_TOP_SNRTH_RCA1__A 0x1C1001E +#define VSB_TOP_SNRTH_RCA1__W 8 +#define VSB_TOP_SNRTH_RCA1__M 0xFF +#define VSB_TOP_SNRTH_RCA1__PRE 0x53 + +#define VSB_TOP_SNRTH_RCA1_DN__B 0 +#define VSB_TOP_SNRTH_RCA1_DN__W 4 +#define VSB_TOP_SNRTH_RCA1_DN__M 0xF +#define VSB_TOP_SNRTH_RCA1_DN__PRE 0x3 + +#define VSB_TOP_SNRTH_RCA1_UP__B 4 +#define VSB_TOP_SNRTH_RCA1_UP__W 4 +#define VSB_TOP_SNRTH_RCA1_UP__M 0xF0 +#define VSB_TOP_SNRTH_RCA1_UP__PRE 0x50 + +#define VSB_TOP_SNRTH_RCA2__A 0x1C1001F +#define VSB_TOP_SNRTH_RCA2__W 8 +#define VSB_TOP_SNRTH_RCA2__M 0xFF +#define VSB_TOP_SNRTH_RCA2__PRE 0x75 + +#define VSB_TOP_SNRTH_RCA2_DN__B 0 +#define VSB_TOP_SNRTH_RCA2_DN__W 4 +#define VSB_TOP_SNRTH_RCA2_DN__M 0xF +#define VSB_TOP_SNRTH_RCA2_DN__PRE 0x5 + +#define VSB_TOP_SNRTH_RCA2_UP__B 4 +#define VSB_TOP_SNRTH_RCA2_UP__W 4 +#define VSB_TOP_SNRTH_RCA2_UP__M 0xF0 +#define VSB_TOP_SNRTH_RCA2_UP__PRE 0x70 + +#define VSB_TOP_SNRTH_DDM1__A 0x1C10020 +#define VSB_TOP_SNRTH_DDM1__W 8 +#define VSB_TOP_SNRTH_DDM1__M 0xFF +#define VSB_TOP_SNRTH_DDM1__PRE 0xCA + +#define VSB_TOP_SNRTH_DDM1_DN__B 0 +#define VSB_TOP_SNRTH_DDM1_DN__W 4 +#define VSB_TOP_SNRTH_DDM1_DN__M 0xF +#define VSB_TOP_SNRTH_DDM1_DN__PRE 0xA + +#define VSB_TOP_SNRTH_DDM1_UP__B 4 +#define VSB_TOP_SNRTH_DDM1_UP__W 4 +#define VSB_TOP_SNRTH_DDM1_UP__M 0xF0 +#define VSB_TOP_SNRTH_DDM1_UP__PRE 0xC0 + +#define VSB_TOP_SNRTH_DDM2__A 0x1C10021 +#define VSB_TOP_SNRTH_DDM2__W 8 +#define VSB_TOP_SNRTH_DDM2__M 0xFF +#define VSB_TOP_SNRTH_DDM2__PRE 0xCA + +#define VSB_TOP_SNRTH_DDM2_DN__B 0 +#define VSB_TOP_SNRTH_DDM2_DN__W 4 +#define VSB_TOP_SNRTH_DDM2_DN__M 0xF +#define VSB_TOP_SNRTH_DDM2_DN__PRE 0xA + +#define VSB_TOP_SNRTH_DDM2_UP__B 4 +#define VSB_TOP_SNRTH_DDM2_UP__W 4 +#define VSB_TOP_SNRTH_DDM2_UP__M 0xF0 +#define VSB_TOP_SNRTH_DDM2_UP__PRE 0xC0 + +#define VSB_TOP_SNRTH_PT__A 0x1C10022 +#define VSB_TOP_SNRTH_PT__W 8 +#define VSB_TOP_SNRTH_PT__M 0xFF +#define VSB_TOP_SNRTH_PT__PRE 0xD8 + +#define VSB_TOP_SNRTH_PT_DN__B 0 +#define VSB_TOP_SNRTH_PT_DN__W 4 +#define VSB_TOP_SNRTH_PT_DN__M 0xF +#define VSB_TOP_SNRTH_PT_DN__PRE 0x8 + +#define VSB_TOP_SNRTH_PT_UP__B 4 +#define VSB_TOP_SNRTH_PT_UP__W 4 +#define VSB_TOP_SNRTH_PT_UP__M 0xF0 +#define VSB_TOP_SNRTH_PT_UP__PRE 0xD0 + +#define VSB_TOP_CYSMSTATES__A 0x1C10023 +#define VSB_TOP_CYSMSTATES__W 8 +#define VSB_TOP_CYSMSTATES__M 0xFF +#define VSB_TOP_CYSMSTATES__PRE 0x0 + +#define VSB_TOP_CYSMSTATES_SYSST__B 0 +#define VSB_TOP_CYSMSTATES_SYSST__W 4 +#define VSB_TOP_CYSMSTATES_SYSST__M 0xF +#define VSB_TOP_CYSMSTATES_SYSST__PRE 0x0 + +#define VSB_TOP_CYSMSTATES_EQST__B 4 +#define VSB_TOP_CYSMSTATES_EQST__W 4 +#define VSB_TOP_CYSMSTATES_EQST__M 0xF0 +#define VSB_TOP_CYSMSTATES_EQST__PRE 0x0 + +#define VSB_TOP_SMALL_NOTCH_CONTROL__A 0x1C10024 +#define VSB_TOP_SMALL_NOTCH_CONTROL__W 8 +#define VSB_TOP_SMALL_NOTCH_CONTROL__M 0xFF +#define VSB_TOP_SMALL_NOTCH_CONTROL__PRE 0x0 + +#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__B 0 +#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__W 1 +#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__M 0x1 +#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__PRE 0x0 + +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__B 1 +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__W 1 +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__M 0x2 +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__PRE 0x0 + +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__B 2 +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__W 1 +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__M 0x4 +#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__PRE 0x0 + +#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__B 3 +#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__W 4 +#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__M 0x78 +#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__PRE 0x0 + +#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__B 7 +#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__W 1 +#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__M 0x80 +#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__PRE 0x0 + +#define VSB_TOP_TAPREADCYC__A 0x1C10025 +#define VSB_TOP_TAPREADCYC__W 9 +#define VSB_TOP_TAPREADCYC__M 0x1FF +#define VSB_TOP_TAPREADCYC__PRE 0x1 + +#define VSB_TOP_VALIDPKLVL__A 0x1C10026 +#define VSB_TOP_VALIDPKLVL__W 13 +#define VSB_TOP_VALIDPKLVL__M 0x1FFF +#define VSB_TOP_VALIDPKLVL__PRE 0x100 + +#define VSB_TOP_CENTROID_FINE_DELAY__A 0x1C10027 +#define VSB_TOP_CENTROID_FINE_DELAY__W 10 +#define VSB_TOP_CENTROID_FINE_DELAY__M 0x3FF +#define VSB_TOP_CENTROID_FINE_DELAY__PRE 0xFF + +#define VSB_TOP_CENTROID_SMACH_DELAY__A 0x1C10028 +#define VSB_TOP_CENTROID_SMACH_DELAY__W 10 +#define VSB_TOP_CENTROID_SMACH_DELAY__M 0x3FF +#define VSB_TOP_CENTROID_SMACH_DELAY__PRE 0x1FF + +#define VSB_TOP_SNR__A 0x1C10029 +#define VSB_TOP_SNR__W 14 +#define VSB_TOP_SNR__M 0x3FFF +#define VSB_TOP_SNR__PRE 0x0 +#define VSB_TOP_LOCKSTATUS__A 0x1C1002A +#define VSB_TOP_LOCKSTATUS__W 7 +#define VSB_TOP_LOCKSTATUS__M 0x7F +#define VSB_TOP_LOCKSTATUS__PRE 0x0 + +#define VSB_TOP_LOCKSTATUS_VSBMODE__B 0 +#define VSB_TOP_LOCKSTATUS_VSBMODE__W 4 +#define VSB_TOP_LOCKSTATUS_VSBMODE__M 0xF +#define VSB_TOP_LOCKSTATUS_VSBMODE__PRE 0x0 + +#define VSB_TOP_LOCKSTATUS_FRMLOCK__B 4 +#define VSB_TOP_LOCKSTATUS_FRMLOCK__W 1 +#define VSB_TOP_LOCKSTATUS_FRMLOCK__M 0x10 +#define VSB_TOP_LOCKSTATUS_FRMLOCK__PRE 0x0 + +#define VSB_TOP_LOCKSTATUS_CYLOCK__B 5 +#define VSB_TOP_LOCKSTATUS_CYLOCK__W 1 +#define VSB_TOP_LOCKSTATUS_CYLOCK__M 0x20 +#define VSB_TOP_LOCKSTATUS_CYLOCK__PRE 0x0 + +#define VSB_TOP_LOCKSTATUS_DDMON__B 6 +#define VSB_TOP_LOCKSTATUS_DDMON__W 1 +#define VSB_TOP_LOCKSTATUS_DDMON__M 0x40 +#define VSB_TOP_LOCKSTATUS_DDMON__PRE 0x0 + +#define VSB_TOP_CTST__A 0x1C1002B +#define VSB_TOP_CTST__W 4 +#define VSB_TOP_CTST__M 0xF +#define VSB_TOP_CTST__PRE 0x0 +#define VSB_TOP_EQSMRSTCTRL__A 0x1C1002C +#define VSB_TOP_EQSMRSTCTRL__W 7 +#define VSB_TOP_EQSMRSTCTRL__M 0x7F +#define VSB_TOP_EQSMRSTCTRL__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_RCAON__B 0 +#define VSB_TOP_EQSMRSTCTRL_RCAON__W 1 +#define VSB_TOP_EQSMRSTCTRL_RCAON__M 0x1 +#define VSB_TOP_EQSMRSTCTRL_RCAON__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_DFEON__B 1 +#define VSB_TOP_EQSMRSTCTRL_DFEON__W 1 +#define VSB_TOP_EQSMRSTCTRL_DFEON__M 0x2 +#define VSB_TOP_EQSMRSTCTRL_DFEON__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_DDMEN1__B 2 +#define VSB_TOP_EQSMRSTCTRL_DDMEN1__W 1 +#define VSB_TOP_EQSMRSTCTRL_DDMEN1__M 0x4 +#define VSB_TOP_EQSMRSTCTRL_DDMEN1__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_DDMEN2__B 3 +#define VSB_TOP_EQSMRSTCTRL_DDMEN2__W 1 +#define VSB_TOP_EQSMRSTCTRL_DDMEN2__M 0x8 +#define VSB_TOP_EQSMRSTCTRL_DDMEN2__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__B 4 +#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__W 1 +#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__M 0x10 +#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__B 5 +#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__W 1 +#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__M 0x20 +#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__PRE 0x0 + +#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__B 6 +#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__W 1 +#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__M 0x40 +#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL__A 0x1C1002D +#define VSB_TOP_EQSMTRNCTRL__W 7 +#define VSB_TOP_EQSMTRNCTRL__M 0x7F +#define VSB_TOP_EQSMTRNCTRL__PRE 0x40 + +#define VSB_TOP_EQSMTRNCTRL_RCAON__B 0 +#define VSB_TOP_EQSMTRNCTRL_RCAON__W 1 +#define VSB_TOP_EQSMTRNCTRL_RCAON__M 0x1 +#define VSB_TOP_EQSMTRNCTRL_RCAON__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL_DFEON__B 1 +#define VSB_TOP_EQSMTRNCTRL_DFEON__W 1 +#define VSB_TOP_EQSMTRNCTRL_DFEON__M 0x2 +#define VSB_TOP_EQSMTRNCTRL_DFEON__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL_DDMEN1__B 2 +#define VSB_TOP_EQSMTRNCTRL_DDMEN1__W 1 +#define VSB_TOP_EQSMTRNCTRL_DDMEN1__M 0x4 +#define VSB_TOP_EQSMTRNCTRL_DDMEN1__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL_DDMEN2__B 3 +#define VSB_TOP_EQSMTRNCTRL_DDMEN2__W 1 +#define VSB_TOP_EQSMTRNCTRL_DDMEN2__M 0x8 +#define VSB_TOP_EQSMTRNCTRL_DDMEN2__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__B 4 +#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__W 1 +#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__M 0x10 +#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__B 5 +#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__W 1 +#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__M 0x20 +#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__PRE 0x0 + +#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__B 6 +#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__W 1 +#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__M 0x40 +#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__PRE 0x40 + +#define VSB_TOP_EQSMRCA1CTRL__A 0x1C1002E +#define VSB_TOP_EQSMRCA1CTRL__W 7 +#define VSB_TOP_EQSMRCA1CTRL__M 0x7F +#define VSB_TOP_EQSMRCA1CTRL__PRE 0x1 + +#define VSB_TOP_EQSMRCA1CTRL_RCAON__B 0 +#define VSB_TOP_EQSMRCA1CTRL_RCAON__W 1 +#define VSB_TOP_EQSMRCA1CTRL_RCAON__M 0x1 +#define VSB_TOP_EQSMRCA1CTRL_RCAON__PRE 0x1 + +#define VSB_TOP_EQSMRCA1CTRL_DFEON__B 1 +#define VSB_TOP_EQSMRCA1CTRL_DFEON__W 1 +#define VSB_TOP_EQSMRCA1CTRL_DFEON__M 0x2 +#define VSB_TOP_EQSMRCA1CTRL_DFEON__PRE 0x0 + +#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__B 2 +#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__W 1 +#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__M 0x4 +#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__PRE 0x0 + +#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__B 3 +#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__W 1 +#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__M 0x8 +#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__PRE 0x0 + +#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__B 4 +#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__W 1 +#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__M 0x10 +#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__PRE 0x0 + +#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__B 5 +#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__W 1 +#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__M 0x20 +#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__PRE 0x0 + +#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__B 6 +#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__W 1 +#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__M 0x40 +#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__PRE 0x0 + +#define VSB_TOP_EQSMRCA2CTRL__A 0x1C1002F +#define VSB_TOP_EQSMRCA2CTRL__W 7 +#define VSB_TOP_EQSMRCA2CTRL__M 0x7F +#define VSB_TOP_EQSMRCA2CTRL__PRE 0x3 + +#define VSB_TOP_EQSMRCA2CTRL_RCAON__B 0 +#define VSB_TOP_EQSMRCA2CTRL_RCAON__W 1 +#define VSB_TOP_EQSMRCA2CTRL_RCAON__M 0x1 +#define VSB_TOP_EQSMRCA2CTRL_RCAON__PRE 0x1 + +#define VSB_TOP_EQSMRCA2CTRL_DFEON__B 1 +#define VSB_TOP_EQSMRCA2CTRL_DFEON__W 1 +#define VSB_TOP_EQSMRCA2CTRL_DFEON__M 0x2 +#define VSB_TOP_EQSMRCA2CTRL_DFEON__PRE 0x2 + +#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__B 2 +#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__W 1 +#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__M 0x4 +#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__PRE 0x0 + +#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__B 3 +#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__W 1 +#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__M 0x8 +#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__PRE 0x0 + +#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__B 4 +#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__W 1 +#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__M 0x10 +#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__PRE 0x0 + +#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__B 5 +#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__W 1 +#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__M 0x20 +#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__PRE 0x0 + +#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__B 6 +#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__W 1 +#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__M 0x40 +#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__PRE 0x0 + +#define VSB_TOP_EQSMDDM1CTRL__A 0x1C10030 +#define VSB_TOP_EQSMDDM1CTRL__W 7 +#define VSB_TOP_EQSMDDM1CTRL__M 0x7F +#define VSB_TOP_EQSMDDM1CTRL__PRE 0x6 + +#define VSB_TOP_EQSMDDM1CTRL_RCAON__B 0 +#define VSB_TOP_EQSMDDM1CTRL_RCAON__W 1 +#define VSB_TOP_EQSMDDM1CTRL_RCAON__M 0x1 +#define VSB_TOP_EQSMDDM1CTRL_RCAON__PRE 0x0 + +#define VSB_TOP_EQSMDDM1CTRL_DFEON__B 1 +#define VSB_TOP_EQSMDDM1CTRL_DFEON__W 1 +#define VSB_TOP_EQSMDDM1CTRL_DFEON__M 0x2 +#define VSB_TOP_EQSMDDM1CTRL_DFEON__PRE 0x2 + +#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__B 2 +#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__W 1 +#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__M 0x4 +#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__PRE 0x4 + +#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__B 3 +#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__W 1 +#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__M 0x8 +#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__PRE 0x0 + +#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__B 4 +#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__W 1 +#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__M 0x10 +#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__PRE 0x0 + +#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__B 5 +#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__W 1 +#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__M 0x20 +#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__PRE 0x0 + +#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__B 6 +#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__W 1 +#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__M 0x40 +#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__PRE 0x0 + +#define VSB_TOP_EQSMDDM2CTRL__A 0x1C10031 +#define VSB_TOP_EQSMDDM2CTRL__W 7 +#define VSB_TOP_EQSMDDM2CTRL__M 0x7F +#define VSB_TOP_EQSMDDM2CTRL__PRE 0x1E + +#define VSB_TOP_EQSMDDM2CTRL_RCAON__B 0 +#define VSB_TOP_EQSMDDM2CTRL_RCAON__W 1 +#define VSB_TOP_EQSMDDM2CTRL_RCAON__M 0x1 +#define VSB_TOP_EQSMDDM2CTRL_RCAON__PRE 0x0 + +#define VSB_TOP_EQSMDDM2CTRL_DFEON__B 1 +#define VSB_TOP_EQSMDDM2CTRL_DFEON__W 1 +#define VSB_TOP_EQSMDDM2CTRL_DFEON__M 0x2 +#define VSB_TOP_EQSMDDM2CTRL_DFEON__PRE 0x2 + +#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__B 2 +#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__W 1 +#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__M 0x4 +#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__PRE 0x4 + +#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__B 3 +#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__W 1 +#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__M 0x8 +#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__PRE 0x8 + +#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__B 4 +#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__W 1 +#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__M 0x10 +#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__PRE 0x10 + +#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__B 5 +#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__W 1 +#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__M 0x20 +#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__PRE 0x0 + +#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__B 6 +#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__W 1 +#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__M 0x40 +#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__PRE 0x0 + +#define VSB_TOP_SYSSMRSTCTRL__A 0x1C10032 +#define VSB_TOP_SYSSMRSTCTRL__W 11 +#define VSB_TOP_SYSSMRSTCTRL__M 0x7FF +#define VSB_TOP_SYSSMRSTCTRL__PRE 0x7F9 + +#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__B 0 +#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__W 1 +#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__M 0x1 +#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__PRE 0x1 + +#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__B 1 +#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__W 1 +#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__M 0x2 +#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__PRE 0x0 + +#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__B 2 +#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__W 1 +#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__M 0x4 +#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__PRE 0x0 + +#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__B 3 +#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__W 1 +#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__M 0x8 +#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__PRE 0x8 + +#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__B 4 +#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__W 1 +#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__M 0x10 +#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__PRE 0x10 + +#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__B 5 +#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__W 1 +#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__M 0x20 +#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__PRE 0x20 + +#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__B 6 +#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__W 1 +#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__M 0x40 +#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__PRE 0x40 + +#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__B 7 +#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__W 1 +#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__M 0x80 +#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__PRE 0x80 + +#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__B 8 +#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__W 1 +#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__M 0x100 +#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__PRE 0x100 + +#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__B 9 +#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__W 1 +#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__M 0x200 +#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__PRE 0x200 + +#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__B 10 +#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__W 1 +#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__M 0x400 +#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__PRE 0x400 + +#define VSB_TOP_SYSSMCYCTRL__A 0x1C10033 +#define VSB_TOP_SYSSMCYCTRL__W 11 +#define VSB_TOP_SYSSMCYCTRL__M 0x7FF +#define VSB_TOP_SYSSMCYCTRL__PRE 0x4E9 + +#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__B 0 +#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__W 1 +#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__M 0x1 +#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__PRE 0x1 + +#define VSB_TOP_SYSSMCYCTRL_CTCALEN__B 1 +#define VSB_TOP_SYSSMCYCTRL_CTCALEN__W 1 +#define VSB_TOP_SYSSMCYCTRL_CTCALEN__M 0x2 +#define VSB_TOP_SYSSMCYCTRL_CTCALEN__PRE 0x0 + +#define VSB_TOP_SYSSMCYCTRL_STARTTRN__B 2 +#define VSB_TOP_SYSSMCYCTRL_STARTTRN__W 1 +#define VSB_TOP_SYSSMCYCTRL_STARTTRN__M 0x4 +#define VSB_TOP_SYSSMCYCTRL_STARTTRN__PRE 0x0 + +#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__B 3 +#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__W 1 +#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__M 0x8 +#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__PRE 0x8 + +#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__B 4 +#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__W 1 +#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__M 0x10 +#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__PRE 0x0 + +#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__B 5 +#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__W 1 +#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__M 0x20 +#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__PRE 0x20 + +#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__B 6 +#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__W 1 +#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__M 0x40 +#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__PRE 0x40 + +#define VSB_TOP_SYSSMCYCTRL_CKFRZ__B 7 +#define VSB_TOP_SYSSMCYCTRL_CKFRZ__W 1 +#define VSB_TOP_SYSSMCYCTRL_CKFRZ__M 0x80 +#define VSB_TOP_SYSSMCYCTRL_CKFRZ__PRE 0x80 + +#define VSB_TOP_SYSSMCYCTRL_CKBWSW__B 8 +#define VSB_TOP_SYSSMCYCTRL_CKBWSW__W 1 +#define VSB_TOP_SYSSMCYCTRL_CKBWSW__M 0x100 +#define VSB_TOP_SYSSMCYCTRL_CKBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__B 9 +#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__W 1 +#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__M 0x200 +#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__B 10 +#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__W 1 +#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__M 0x400 +#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__PRE 0x400 + +#define VSB_TOP_SYSSMTRNCTRL__A 0x1C10034 +#define VSB_TOP_SYSSMTRNCTRL__W 11 +#define VSB_TOP_SYSSMTRNCTRL__M 0x7FF +#define VSB_TOP_SYSSMTRNCTRL__PRE 0x204 + +#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__B 0 +#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__W 1 +#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__M 0x1 +#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__B 1 +#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__W 1 +#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__M 0x2 +#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__B 2 +#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__W 1 +#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__M 0x4 +#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__PRE 0x4 + +#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__B 3 +#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__W 1 +#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__M 0x8 +#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__B 4 +#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__W 1 +#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__M 0x10 +#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__B 5 +#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__W 1 +#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__M 0x20 +#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__B 6 +#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__W 1 +#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__M 0x40 +#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__B 7 +#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__W 1 +#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__M 0x80 +#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__B 8 +#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__W 1 +#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__M 0x100 +#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__B 9 +#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__W 1 +#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__M 0x200 +#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__PRE 0x200 + +#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__B 10 +#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__W 1 +#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__M 0x400 +#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL__A 0x1C10035 +#define VSB_TOP_SYSSMEQCTRL__W 11 +#define VSB_TOP_SYSSMEQCTRL__M 0x7FF +#define VSB_TOP_SYSSMEQCTRL__PRE 0x304 + +#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__B 0 +#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__W 1 +#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__M 0x1 +#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_CTCALEN__B 1 +#define VSB_TOP_SYSSMEQCTRL_CTCALEN__W 1 +#define VSB_TOP_SYSSMEQCTRL_CTCALEN__M 0x2 +#define VSB_TOP_SYSSMEQCTRL_CTCALEN__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_STARTTRN__B 2 +#define VSB_TOP_SYSSMEQCTRL_STARTTRN__W 1 +#define VSB_TOP_SYSSMEQCTRL_STARTTRN__M 0x4 +#define VSB_TOP_SYSSMEQCTRL_STARTTRN__PRE 0x4 + +#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__B 3 +#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__W 1 +#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__M 0x8 +#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__B 4 +#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__W 1 +#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__M 0x10 +#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__B 5 +#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__W 1 +#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__M 0x20 +#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__B 6 +#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__W 1 +#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__M 0x40 +#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_CKFRZ__B 7 +#define VSB_TOP_SYSSMEQCTRL_CKFRZ__W 1 +#define VSB_TOP_SYSSMEQCTRL_CKFRZ__M 0x80 +#define VSB_TOP_SYSSMEQCTRL_CKFRZ__PRE 0x0 + +#define VSB_TOP_SYSSMEQCTRL_CKBWSW__B 8 +#define VSB_TOP_SYSSMEQCTRL_CKBWSW__W 1 +#define VSB_TOP_SYSSMEQCTRL_CKBWSW__M 0x100 +#define VSB_TOP_SYSSMEQCTRL_CKBWSW__PRE 0x100 + +#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__B 9 +#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__W 1 +#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__M 0x200 +#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__PRE 0x200 + +#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__B 10 +#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__W 1 +#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__M 0x400 +#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__PRE 0x0 + +#define VSB_TOP_SYSSMAGCCTRL__A 0x1C10036 +#define VSB_TOP_SYSSMAGCCTRL__W 11 +#define VSB_TOP_SYSSMAGCCTRL__M 0x7FF +#define VSB_TOP_SYSSMAGCCTRL__PRE 0xF9 + +#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__B 0 +#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__W 1 +#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__M 0x1 +#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__PRE 0x1 + +#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__B 1 +#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__W 1 +#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__M 0x2 +#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__PRE 0x0 + +#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__B 2 +#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__W 1 +#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__M 0x4 +#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__PRE 0x0 + +#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__B 3 +#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__W 1 +#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__M 0x8 +#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__PRE 0x8 + +#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__B 4 +#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__W 1 +#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__M 0x10 +#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__PRE 0x10 + +#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__B 5 +#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__W 1 +#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__M 0x20 +#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__PRE 0x20 + +#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__B 6 +#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__W 1 +#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__M 0x40 +#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__PRE 0x40 + +#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__B 7 +#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__W 1 +#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__M 0x80 +#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__PRE 0x80 + +#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__B 8 +#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__W 1 +#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__M 0x100 +#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__B 9 +#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__W 1 +#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__M 0x200 +#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__B 10 +#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__W 1 +#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__M 0x400 +#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL__A 0x1C10037 +#define VSB_TOP_SYSSMCTCTRL__W 11 +#define VSB_TOP_SYSSMCTCTRL__M 0x7FF +#define VSB_TOP_SYSSMCTCTRL__PRE 0x4A + +#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__B 0 +#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__W 1 +#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__M 0x1 +#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_CTCALEN__B 1 +#define VSB_TOP_SYSSMCTCTRL_CTCALEN__W 1 +#define VSB_TOP_SYSSMCTCTRL_CTCALEN__M 0x2 +#define VSB_TOP_SYSSMCTCTRL_CTCALEN__PRE 0x2 + +#define VSB_TOP_SYSSMCTCTRL_STARTTRN__B 2 +#define VSB_TOP_SYSSMCTCTRL_STARTTRN__W 1 +#define VSB_TOP_SYSSMCTCTRL_STARTTRN__M 0x4 +#define VSB_TOP_SYSSMCTCTRL_STARTTRN__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__B 3 +#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__W 1 +#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__M 0x8 +#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__PRE 0x8 + +#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__B 4 +#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__W 1 +#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__M 0x10 +#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__B 5 +#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__W 1 +#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__M 0x20 +#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__B 6 +#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__W 1 +#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__M 0x40 +#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__PRE 0x40 + +#define VSB_TOP_SYSSMCTCTRL_CKFRZ__B 7 +#define VSB_TOP_SYSSMCTCTRL_CKFRZ__W 1 +#define VSB_TOP_SYSSMCTCTRL_CKFRZ__M 0x80 +#define VSB_TOP_SYSSMCTCTRL_CKFRZ__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_CKBWSW__B 8 +#define VSB_TOP_SYSSMCTCTRL_CKBWSW__W 1 +#define VSB_TOP_SYSSMCTCTRL_CKBWSW__M 0x100 +#define VSB_TOP_SYSSMCTCTRL_CKBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__B 9 +#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__W 1 +#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__M 0x200 +#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__PRE 0x0 + +#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__B 10 +#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__W 1 +#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__M 0x400 +#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__PRE 0x0 + +#define VSB_TOP_EQCTRL__A 0x1C10038 +#define VSB_TOP_EQCTRL__W 10 +#define VSB_TOP_EQCTRL__M 0x3FF +#define VSB_TOP_EQCTRL__PRE 0x6 + +#define VSB_TOP_EQCTRL_STASSIGNEN__B 0 +#define VSB_TOP_EQCTRL_STASSIGNEN__W 1 +#define VSB_TOP_EQCTRL_STASSIGNEN__M 0x1 +#define VSB_TOP_EQCTRL_STASSIGNEN__PRE 0x0 + +#define VSB_TOP_EQCTRL_ORCANCMAEN__B 1 +#define VSB_TOP_EQCTRL_ORCANCMAEN__W 1 +#define VSB_TOP_EQCTRL_ORCANCMAEN__M 0x2 +#define VSB_TOP_EQCTRL_ORCANCMAEN__PRE 0x2 + +#define VSB_TOP_EQCTRL_ODAGCGO__B 2 +#define VSB_TOP_EQCTRL_ODAGCGO__W 1 +#define VSB_TOP_EQCTRL_ODAGCGO__M 0x4 +#define VSB_TOP_EQCTRL_ODAGCGO__PRE 0x4 + +#define VSB_TOP_EQCTRL_OPTGAIN__B 3 +#define VSB_TOP_EQCTRL_OPTGAIN__W 3 +#define VSB_TOP_EQCTRL_OPTGAIN__M 0x38 +#define VSB_TOP_EQCTRL_OPTGAIN__PRE 0x0 + +#define VSB_TOP_EQCTRL_TAPRAMWRTEN__B 6 +#define VSB_TOP_EQCTRL_TAPRAMWRTEN__W 1 +#define VSB_TOP_EQCTRL_TAPRAMWRTEN__M 0x40 +#define VSB_TOP_EQCTRL_TAPRAMWRTEN__PRE 0x0 + +#define VSB_TOP_EQCTRL_CMAGAIN__B 7 +#define VSB_TOP_EQCTRL_CMAGAIN__W 3 +#define VSB_TOP_EQCTRL_CMAGAIN__M 0x380 +#define VSB_TOP_EQCTRL_CMAGAIN__PRE 0x0 + +#define VSB_TOP_PREEQAGCCTRL__A 0x1C10039 +#define VSB_TOP_PREEQAGCCTRL__W 5 +#define VSB_TOP_PREEQAGCCTRL__M 0x1F +#define VSB_TOP_PREEQAGCCTRL__PRE 0x10 + +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__B 0 +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__W 4 +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__M 0xF +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__PRE 0x0 + +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__B 4 +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__W 1 +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__M 0x10 +#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__PRE 0x10 + +#define VSB_TOP_PREEQAGCPWRREFLVLHI__A 0x1C1003A +#define VSB_TOP_PREEQAGCPWRREFLVLHI__W 8 +#define VSB_TOP_PREEQAGCPWRREFLVLHI__M 0xFF +#define VSB_TOP_PREEQAGCPWRREFLVLHI__PRE 0x0 + +#define VSB_TOP_PREEQAGCPWRREFLVLLO__A 0x1C1003B +#define VSB_TOP_PREEQAGCPWRREFLVLLO__W 16 +#define VSB_TOP_PREEQAGCPWRREFLVLLO__M 0xFFFF +#define VSB_TOP_PREEQAGCPWRREFLVLLO__PRE 0x1D66 + +#define VSB_TOP_CORINGSEL__A 0x1C1003C +#define VSB_TOP_CORINGSEL__W 8 +#define VSB_TOP_CORINGSEL__M 0xFF +#define VSB_TOP_CORINGSEL__PRE 0x3 +#define VSB_TOP_BEDETCTRL__A 0x1C1003D +#define VSB_TOP_BEDETCTRL__W 9 +#define VSB_TOP_BEDETCTRL__M 0x1FF +#define VSB_TOP_BEDETCTRL__PRE 0x145 + +#define VSB_TOP_BEDETCTRL_MIXRATIO__B 0 +#define VSB_TOP_BEDETCTRL_MIXRATIO__W 3 +#define VSB_TOP_BEDETCTRL_MIXRATIO__M 0x7 +#define VSB_TOP_BEDETCTRL_MIXRATIO__PRE 0x5 + +#define VSB_TOP_BEDETCTRL_CYOFFSEL__B 3 +#define VSB_TOP_BEDETCTRL_CYOFFSEL__W 1 +#define VSB_TOP_BEDETCTRL_CYOFFSEL__M 0x8 +#define VSB_TOP_BEDETCTRL_CYOFFSEL__PRE 0x0 + +#define VSB_TOP_BEDETCTRL_DATAOFFSEL__B 4 +#define VSB_TOP_BEDETCTRL_DATAOFFSEL__W 1 +#define VSB_TOP_BEDETCTRL_DATAOFFSEL__M 0x10 +#define VSB_TOP_BEDETCTRL_DATAOFFSEL__PRE 0x0 + +#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__B 5 +#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__W 1 +#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__M 0x20 +#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__PRE 0x0 + +#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__B 6 +#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__W 1 +#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__M 0x40 +#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__PRE 0x40 + +#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__B 7 +#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__W 1 +#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__M 0x80 +#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__PRE 0x0 + +#define VSB_TOP_BEDETCTRL_BYPASS_DMP__B 8 +#define VSB_TOP_BEDETCTRL_BYPASS_DMP__W 1 +#define VSB_TOP_BEDETCTRL_BYPASS_DMP__M 0x100 +#define VSB_TOP_BEDETCTRL_BYPASS_DMP__PRE 0x100 + +#define VSB_TOP_LBAGCREFLVL__A 0x1C1003E +#define VSB_TOP_LBAGCREFLVL__W 12 +#define VSB_TOP_LBAGCREFLVL__M 0xFFF +#define VSB_TOP_LBAGCREFLVL__PRE 0x200 + +#define VSB_TOP_UBAGCREFLVL__A 0x1C1003F +#define VSB_TOP_UBAGCREFLVL__W 12 +#define VSB_TOP_UBAGCREFLVL__M 0xFFF +#define VSB_TOP_UBAGCREFLVL__PRE 0x400 + +#define VSB_TOP_NOTCH1_BIN_NUM__A 0x1C10040 +#define VSB_TOP_NOTCH1_BIN_NUM__W 11 +#define VSB_TOP_NOTCH1_BIN_NUM__M 0x7FF +#define VSB_TOP_NOTCH1_BIN_NUM__PRE 0xB2 + +#define VSB_TOP_NOTCH2_BIN_NUM__A 0x1C10041 +#define VSB_TOP_NOTCH2_BIN_NUM__W 11 +#define VSB_TOP_NOTCH2_BIN_NUM__M 0x7FF +#define VSB_TOP_NOTCH2_BIN_NUM__PRE 0x40B + +#define VSB_TOP_NOTCH_START_BIN_NUM__A 0x1C10042 +#define VSB_TOP_NOTCH_START_BIN_NUM__W 11 +#define VSB_TOP_NOTCH_START_BIN_NUM__M 0x7FF +#define VSB_TOP_NOTCH_START_BIN_NUM__PRE 0x7C0 + +#define VSB_TOP_NOTCH_STOP_BIN_NUM__A 0x1C10043 +#define VSB_TOP_NOTCH_STOP_BIN_NUM__W 11 +#define VSB_TOP_NOTCH_STOP_BIN_NUM__M 0x7FF +#define VSB_TOP_NOTCH_STOP_BIN_NUM__PRE 0x43F + +#define VSB_TOP_NOTCH_TEST_DURATION__A 0x1C10044 +#define VSB_TOP_NOTCH_TEST_DURATION__W 11 +#define VSB_TOP_NOTCH_TEST_DURATION__M 0x7FF +#define VSB_TOP_NOTCH_TEST_DURATION__PRE 0x7FF + +#define VSB_TOP_RESULT_LARGE_PEAK_BIN__A 0x1C10045 +#define VSB_TOP_RESULT_LARGE_PEAK_BIN__W 11 +#define VSB_TOP_RESULT_LARGE_PEAK_BIN__M 0x7FF +#define VSB_TOP_RESULT_LARGE_PEAK_BIN__PRE 0x0 + +#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__A 0x1C10046 +#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__W 16 +#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__M 0xFFFF +#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__PRE 0x0 + +#define VSB_TOP_RESULT_SMALL_PEAK_BIN__A 0x1C10047 +#define VSB_TOP_RESULT_SMALL_PEAK_BIN__W 11 +#define VSB_TOP_RESULT_SMALL_PEAK_BIN__M 0x7FF +#define VSB_TOP_RESULT_SMALL_PEAK_BIN__PRE 0x0 + +#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__A 0x1C10048 +#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__W 16 +#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__M 0xFFFF +#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__PRE 0x0 + +#define VSB_TOP_NOTCH_SWEEP_RUNNING__A 0x1C10049 +#define VSB_TOP_NOTCH_SWEEP_RUNNING__W 1 +#define VSB_TOP_NOTCH_SWEEP_RUNNING__M 0x1 +#define VSB_TOP_NOTCH_SWEEP_RUNNING__PRE 0x0 + +#define VSB_TOP_PREEQDAGCRATIO__A 0x1C1004A +#define VSB_TOP_PREEQDAGCRATIO__W 13 +#define VSB_TOP_PREEQDAGCRATIO__M 0x1FFF +#define VSB_TOP_PREEQDAGCRATIO__PRE 0x0 +#define VSB_TOP_AGC_TRUNCCTRL__A 0x1C1004B +#define VSB_TOP_AGC_TRUNCCTRL__W 4 +#define VSB_TOP_AGC_TRUNCCTRL__M 0xF +#define VSB_TOP_AGC_TRUNCCTRL__PRE 0xF + +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__B 0 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__W 2 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__M 0x3 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__PRE 0x3 + +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__B 2 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__W 1 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__M 0x4 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__PRE 0x4 + +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__B 3 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__W 1 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__M 0x8 +#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__PRE 0x8 + +#define VSB_TOP_BEAGC_DEADZONEINIT__A 0x1C1004C +#define VSB_TOP_BEAGC_DEADZONEINIT__W 8 +#define VSB_TOP_BEAGC_DEADZONEINIT__M 0xFF +#define VSB_TOP_BEAGC_DEADZONEINIT__PRE 0x50 + +#define VSB_TOP_BEAGC_REFLEVEL__A 0x1C1004D +#define VSB_TOP_BEAGC_REFLEVEL__W 9 +#define VSB_TOP_BEAGC_REFLEVEL__M 0x1FF +#define VSB_TOP_BEAGC_REFLEVEL__PRE 0xAE + +#define VSB_TOP_BEAGC_GAINSHIFT__A 0x1C1004E +#define VSB_TOP_BEAGC_GAINSHIFT__W 3 +#define VSB_TOP_BEAGC_GAINSHIFT__M 0x7 +#define VSB_TOP_BEAGC_GAINSHIFT__PRE 0x3 + +#define VSB_TOP_BEAGC_REGINIT__A 0x1C1004F +#define VSB_TOP_BEAGC_REGINIT__W 15 +#define VSB_TOP_BEAGC_REGINIT__M 0x7FFF +#define VSB_TOP_BEAGC_REGINIT__PRE 0x40 + +#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__B 14 +#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__W 1 +#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__M 0x4000 +#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__PRE 0x0 + +#define VSB_TOP_BEAGC_SCALE__A 0x1C10050 +#define VSB_TOP_BEAGC_SCALE__W 14 +#define VSB_TOP_BEAGC_SCALE__M 0x3FFF +#define VSB_TOP_BEAGC_SCALE__PRE 0x0 + +#define VSB_TOP_CFAGC_DEADZONEINIT__A 0x1C10051 +#define VSB_TOP_CFAGC_DEADZONEINIT__W 8 +#define VSB_TOP_CFAGC_DEADZONEINIT__M 0xFF +#define VSB_TOP_CFAGC_DEADZONEINIT__PRE 0x50 + +#define VSB_TOP_CFAGC_REFLEVEL__A 0x1C10052 +#define VSB_TOP_CFAGC_REFLEVEL__W 9 +#define VSB_TOP_CFAGC_REFLEVEL__M 0x1FF +#define VSB_TOP_CFAGC_REFLEVEL__PRE 0xAE + +#define VSB_TOP_CFAGC_GAINSHIFT__A 0x1C10053 +#define VSB_TOP_CFAGC_GAINSHIFT__W 3 +#define VSB_TOP_CFAGC_GAINSHIFT__M 0x7 +#define VSB_TOP_CFAGC_GAINSHIFT__PRE 0x3 + +#define VSB_TOP_CFAGC_REGINIT__A 0x1C10054 +#define VSB_TOP_CFAGC_REGINIT__W 15 +#define VSB_TOP_CFAGC_REGINIT__M 0x7FFF +#define VSB_TOP_CFAGC_REGINIT__PRE 0x80 + +#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__B 14 +#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__W 1 +#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__M 0x4000 +#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__PRE 0x0 + +#define VSB_TOP_CFAGC_SCALE__A 0x1C10055 +#define VSB_TOP_CFAGC_SCALE__W 14 +#define VSB_TOP_CFAGC_SCALE__M 0x3FFF +#define VSB_TOP_CFAGC_SCALE__PRE 0x0 + +#define VSB_TOP_CKTRKONCTL__A 0x1C10056 +#define VSB_TOP_CKTRKONCTL__W 2 +#define VSB_TOP_CKTRKONCTL__M 0x3 +#define VSB_TOP_CKTRKONCTL__PRE 0x0 + +#define VSB_TOP_CYTRKONCTL__A 0x1C10057 +#define VSB_TOP_CYTRKONCTL__W 2 +#define VSB_TOP_CYTRKONCTL__M 0x3 +#define VSB_TOP_CYTRKONCTL__PRE 0x0 + +#define VSB_TOP_PTONCTL__A 0x1C10058 +#define VSB_TOP_PTONCTL__W 2 +#define VSB_TOP_PTONCTL__M 0x3 +#define VSB_TOP_PTONCTL__PRE 0x0 + +#define VSB_TOP_NOTCH_SCALE_1__A 0x1C10059 +#define VSB_TOP_NOTCH_SCALE_1__W 8 +#define VSB_TOP_NOTCH_SCALE_1__M 0xFF +#define VSB_TOP_NOTCH_SCALE_1__PRE 0xA + +#define VSB_TOP_NOTCH_SCALE_2__A 0x1C1005A +#define VSB_TOP_NOTCH_SCALE_2__W 8 +#define VSB_TOP_NOTCH_SCALE_2__M 0xFF +#define VSB_TOP_NOTCH_SCALE_2__PRE 0xA + +#define VSB_TOP_FIRSTLARGFFETAP__A 0x1C1005B +#define VSB_TOP_FIRSTLARGFFETAP__W 12 +#define VSB_TOP_FIRSTLARGFFETAP__M 0xFFF +#define VSB_TOP_FIRSTLARGFFETAP__PRE 0x0 + +#define VSB_TOP_FIRSTLARGFFETAPADDR__A 0x1C1005C +#define VSB_TOP_FIRSTLARGFFETAPADDR__W 11 +#define VSB_TOP_FIRSTLARGFFETAPADDR__M 0x7FF +#define VSB_TOP_FIRSTLARGFFETAPADDR__PRE 0x0 + +#define VSB_TOP_SECONDLARGFFETAP__A 0x1C1005D +#define VSB_TOP_SECONDLARGFFETAP__W 12 +#define VSB_TOP_SECONDLARGFFETAP__M 0xFFF +#define VSB_TOP_SECONDLARGFFETAP__PRE 0x0 + +#define VSB_TOP_SECONDLARGFFETAPADDR__A 0x1C1005E +#define VSB_TOP_SECONDLARGFFETAPADDR__W 11 +#define VSB_TOP_SECONDLARGFFETAPADDR__M 0x7FF +#define VSB_TOP_SECONDLARGFFETAPADDR__PRE 0x0 + +#define VSB_TOP_FIRSTLARGDFETAP__A 0x1C1005F +#define VSB_TOP_FIRSTLARGDFETAP__W 12 +#define VSB_TOP_FIRSTLARGDFETAP__M 0xFFF +#define VSB_TOP_FIRSTLARGDFETAP__PRE 0x0 + +#define VSB_TOP_FIRSTLARGDFETAPADDR__A 0x1C10060 +#define VSB_TOP_FIRSTLARGDFETAPADDR__W 11 +#define VSB_TOP_FIRSTLARGDFETAPADDR__M 0x7FF +#define VSB_TOP_FIRSTLARGDFETAPADDR__PRE 0x0 + +#define VSB_TOP_SECONDLARGDFETAP__A 0x1C10061 +#define VSB_TOP_SECONDLARGDFETAP__W 12 +#define VSB_TOP_SECONDLARGDFETAP__M 0xFFF +#define VSB_TOP_SECONDLARGDFETAP__PRE 0x0 + +#define VSB_TOP_SECONDLARGDFETAPADDR__A 0x1C10062 +#define VSB_TOP_SECONDLARGDFETAPADDR__W 11 +#define VSB_TOP_SECONDLARGDFETAPADDR__M 0x7FF +#define VSB_TOP_SECONDLARGDFETAPADDR__PRE 0x0 + +#define VSB_TOP_PARAOWDBUS__A 0x1C10063 +#define VSB_TOP_PARAOWDBUS__W 12 +#define VSB_TOP_PARAOWDBUS__M 0xFFF +#define VSB_TOP_PARAOWDBUS__PRE 0x0 +#define VSB_TOP_PARAOWCTRL__A 0x1C10064 +#define VSB_TOP_PARAOWCTRL__W 7 +#define VSB_TOP_PARAOWCTRL__M 0x7F +#define VSB_TOP_PARAOWCTRL__PRE 0x0 + +#define VSB_TOP_PARAOWCTRL_PARAOWABUS__B 0 +#define VSB_TOP_PARAOWCTRL_PARAOWABUS__W 6 +#define VSB_TOP_PARAOWCTRL_PARAOWABUS__M 0x3F +#define VSB_TOP_PARAOWCTRL_PARAOWABUS__PRE 0x0 + +#define VSB_TOP_PARAOWCTRL_PARAOWEN__B 6 +#define VSB_TOP_PARAOWCTRL_PARAOWEN__W 1 +#define VSB_TOP_PARAOWCTRL_PARAOWEN__M 0x40 +#define VSB_TOP_PARAOWCTRL_PARAOWEN__PRE 0x0 + +#define VSB_TOP_CURRENTSEGLOCAT__A 0x1C10065 +#define VSB_TOP_CURRENTSEGLOCAT__W 10 +#define VSB_TOP_CURRENTSEGLOCAT__M 0x3FF +#define VSB_TOP_CURRENTSEGLOCAT__PRE 0x0 + +#define VSB_TOP_MEASUREMENT_PERIOD__A 0x1C10066 +#define VSB_TOP_MEASUREMENT_PERIOD__W 16 +#define VSB_TOP_MEASUREMENT_PERIOD__M 0xFFFF +#define VSB_TOP_MEASUREMENT_PERIOD__PRE 0x0 + +#define VSB_TOP_NR_SYM_ERRS__A 0x1C10067 +#define VSB_TOP_NR_SYM_ERRS__W 16 +#define VSB_TOP_NR_SYM_ERRS__M 0xFFFF +#define VSB_TOP_NR_SYM_ERRS__PRE 0xFFFF + +#define VSB_TOP_ERR_ENERGY_L__A 0x1C10068 +#define VSB_TOP_ERR_ENERGY_L__W 16 +#define VSB_TOP_ERR_ENERGY_L__M 0xFFFF +#define VSB_TOP_ERR_ENERGY_L__PRE 0xFFFF + +#define VSB_TOP_ERR_ENERGY_H__A 0x1C10069 +#define VSB_TOP_ERR_ENERGY_H__W 16 +#define VSB_TOP_ERR_ENERGY_H__M 0xFFFF +#define VSB_TOP_ERR_ENERGY_H__PRE 0xFFFF + +#define VSB_TOP_SLICER_SEL_8LEV__A 0x1C1006A +#define VSB_TOP_SLICER_SEL_8LEV__W 1 +#define VSB_TOP_SLICER_SEL_8LEV__M 0x1 +#define VSB_TOP_SLICER_SEL_8LEV__PRE 0x1 + +#define VSB_TOP_BNFIELD__A 0x1C1006B +#define VSB_TOP_BNFIELD__W 3 +#define VSB_TOP_BNFIELD__M 0x7 +#define VSB_TOP_BNFIELD__PRE 0x3 + +#define VSB_TOP_CLPLASTNUM__A 0x1C1006C +#define VSB_TOP_CLPLASTNUM__W 8 +#define VSB_TOP_CLPLASTNUM__M 0xFF +#define VSB_TOP_CLPLASTNUM__PRE 0x0 + +#define VSB_TOP_BNSQERR__A 0x1C1006D +#define VSB_TOP_BNSQERR__W 16 +#define VSB_TOP_BNSQERR__M 0xFFFF +#define VSB_TOP_BNSQERR__PRE 0x1AD + +#define VSB_TOP_BNTHRESH__A 0x1C1006E +#define VSB_TOP_BNTHRESH__W 9 +#define VSB_TOP_BNTHRESH__M 0x1FF +#define VSB_TOP_BNTHRESH__PRE 0x120 + +#define VSB_TOP_BNCLPNUM__A 0x1C1006F +#define VSB_TOP_BNCLPNUM__W 16 +#define VSB_TOP_BNCLPNUM__M 0xFFFF +#define VSB_TOP_BNCLPNUM__PRE 0x0 +#define VSB_TOP_PHASELOCKCTRL__A 0x1C10070 +#define VSB_TOP_PHASELOCKCTRL__W 7 +#define VSB_TOP_PHASELOCKCTRL__M 0x7F +#define VSB_TOP_PHASELOCKCTRL__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__B 0 +#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__W 1 +#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__M 0x1 +#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__B 1 +#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__W 1 +#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__M 0x2 +#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__B 2 +#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__W 1 +#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__M 0x4 +#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__B 3 +#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__W 1 +#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__M 0x8 +#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__B 4 +#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__W 1 +#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__M 0x10 +#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__B 5 +#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__W 1 +#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__M 0x20 +#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__PRE 0x0 + +#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__B 6 +#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__W 1 +#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__M 0x40 +#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__PRE 0x0 + +#define VSB_TOP_DLOCKACCUM__A 0x1C10071 +#define VSB_TOP_DLOCKACCUM__W 16 +#define VSB_TOP_DLOCKACCUM__M 0xFFFF +#define VSB_TOP_DLOCKACCUM__PRE 0x0 + +#define VSB_TOP_PLOCKACCUM__A 0x1C10072 +#define VSB_TOP_PLOCKACCUM__W 16 +#define VSB_TOP_PLOCKACCUM__M 0xFFFF +#define VSB_TOP_PLOCKACCUM__PRE 0x0 + +#define VSB_TOP_CLOCKACCUM__A 0x1C10073 +#define VSB_TOP_CLOCKACCUM__W 16 +#define VSB_TOP_CLOCKACCUM__M 0xFFFF +#define VSB_TOP_CLOCKACCUM__PRE 0x0 + +#define VSB_TOP_DCRMVACUMI__A 0x1C10074 +#define VSB_TOP_DCRMVACUMI__W 10 +#define VSB_TOP_DCRMVACUMI__M 0x3FF +#define VSB_TOP_DCRMVACUMI__PRE 0x0 + +#define VSB_TOP_DCRMVACUMQ__A 0x1C10075 +#define VSB_TOP_DCRMVACUMQ__W 10 +#define VSB_TOP_DCRMVACUMQ__M 0x3FF +#define VSB_TOP_DCRMVACUMQ__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__A 0x1C20000 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__A 0x1C20001 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__A 0x1C20002 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__A 0x1C20003 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__A 0x1C20004 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__A 0x1C20005 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__A 0x1C20006 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__A 0x1C20007 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__A 0x1C20008 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__A 0x1C20009 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__A 0x1C2000A +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__A 0x1C2000B +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__A 0x1C2000C +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__A 0x1C2000D +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__A 0x1C2000E +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__A 0x1C2000F +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__A 0x1C20010 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__A 0x1C20011 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__A 0x1C20012 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__A 0x1C20013 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__A 0x1C20014 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__A 0x1C20015 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__A 0x1C20016 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__A 0x1C20017 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__A 0x1C20018 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__A 0x1C20019 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__A 0x1C2001A +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__A 0x1C2001B +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__A 0x1C2001C +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__A 0x1C2001D +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__A 0x1C2001E +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__A 0x1C2001F +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__A 0x1C20020 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__A 0x1C20021 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__A 0x1C20022 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__A 0x1C20023 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__A 0x1C20024 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__A 0x1C20025 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__A 0x1C20026 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__A 0x1C20027 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__A 0x1C20028 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__A 0x1C20029 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__A 0x1C2002A +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__A 0x1C2002B +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__A 0x1C2002C +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__A 0x1C2002D +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__A 0x1C2002E +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__A 0x1C2002F +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__A 0x1C20030 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__A 0x1C20031 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__A 0x1C20032 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__A 0x1C20033 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__A 0x1C20034 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__A 0x1C20035 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__A 0x1C20036 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__A 0x1C20037 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__A 0x1C20038 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__A 0x1C20039 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__A 0x1C2003A +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__A 0x1C2003B +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__A 0x1C2003C +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__A 0x1C2003D +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__A 0x1C2003E +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__A 0x1C2003F +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__A 0x1C20040 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__A 0x1C20041 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__A 0x1C20042 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__A 0x1C20043 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__A 0x1C20044 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__A 0x1C20045 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__A 0x1C20046 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__A 0x1C20047 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__A 0x1C20048 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__A 0x1C20049 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__A 0x1C2004A +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__A 0x1C2004B +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__A 0x1C2004C +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__A 0x1C2004D +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__A 0x1C2004E +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__A 0x1C2004F +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__A 0x1C20050 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__A 0x1C20051 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__A 0x1C20052 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__A 0x1C20053 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__A 0x1C20054 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__A 0x1C20055 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__A 0x1C20056 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__A 0x1C20057 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__A 0x1C20058 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__A 0x1C20059 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__A 0x1C2005A +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__A 0x1C2005B +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__A 0x1C2005C +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__A 0x1C2005D +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__A 0x1C2005E +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__A 0x1C2005F +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__A 0x1C20060 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__A 0x1C20061 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__A 0x1C20062 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__A 0x1C20063 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__A 0x1C20064 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__A 0x1C20065 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__A 0x1C20066 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__A 0x1C20067 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__A 0x1C20068 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__A 0x1C20069 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__A 0x1C2006A +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__A 0x1C2006B +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__W 12 +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__M 0xFFF +#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__A 0x1C2006C +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__A 0x1C2006D +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__A 0x1C2006E +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__A 0x1C2006F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__A 0x1C20070 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__A 0x1C20071 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__A 0x1C20072 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__A 0x1C20073 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__A 0x1C20074 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__A 0x1C20075 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__A 0x1C20076 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__A 0x1C20077 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__W 7 +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__PRE 0x0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__A 0x1C20078 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__A 0x1C20079 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__A 0x1C2007A +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__A 0x1C2007B +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__A 0x1C2007C +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__A 0x1C2007D +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__A 0x1C2007E +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__A 0x1C2007F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__W 15 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__M 0x7FFF +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__B 0 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__M 0x7F +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__B 8 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__W 7 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__M 0x7F00 +#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__A 0x1C30000 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__A 0x1C30001 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__A 0x1C30002 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__A 0x1C30003 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__A 0x1C30004 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__A 0x1C30005 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__A 0x1C30006 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__A 0x1C30007 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__A 0x1C30008 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__A 0x1C30009 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__A 0x1C3000A +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__A 0x1C3000B +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__A 0x1C3000C +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__A 0x1C3000D +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__A 0x1C3000E +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__A 0x1C3000F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__W 15 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__B 0 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__B 8 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__A 0x1C30010 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__A 0x1C30011 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__A 0x1C30012 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__A 0x1C30013 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__A 0x1C30014 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__A 0x1C30015 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__A 0x1C30016 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__A 0x1C30017 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__A 0x1C30018 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__A 0x1C30019 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__A 0x1C3001A +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__A 0x1C3001B +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__A 0x1C3001C +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__A 0x1C3001D +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__A 0x1C3001E +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__A 0x1C3001F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__A 0x1C30020 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__A 0x1C30021 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__A 0x1C30022 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__A 0x1C30023 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__A 0x1C30024 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__A 0x1C30025 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__A 0x1C30026 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__A 0x1C30027 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__W 15 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__M 0x7FFF +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__B 0 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__M 0x7F +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__B 8 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__W 7 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__M 0x7F00 +#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__A 0x1C30028 +#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__A 0x1C30029 +#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__A 0x1C3002A +#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__A 0x1C3002B +#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__A 0x1C3002C +#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__A 0x1C3002D +#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__A 0x1C3002E +#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__A 0x1C3002F +#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__A 0x1C30030 +#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__W 12 +#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__M 0xFFF +#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__A 0x1C30031 +#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__M 0x7F +#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__PRE 0x0 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__A 0x1C30032 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__W 15 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__M 0x7FFF +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__B 0 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__M 0x7F +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__B 8 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__M 0x7F00 +#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__A 0x1C30033 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__W 15 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__M 0x7FFF +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__B 0 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__M 0x7F +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__B 8 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__M 0x7F00 +#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__A 0x1C30034 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__W 15 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__M 0x7FFF +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__B 0 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__M 0x7F +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__B 8 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__M 0x7F00 +#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__A 0x1C30035 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__W 15 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__M 0x7FFF +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__B 0 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__M 0x7F +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__PRE 0x0 + +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__B 8 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__W 7 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__M 0x7F00 +#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__PRE 0x0 + +#define VSB_TCMEQ_RAM__A 0x1C40000 + +#define VSB_TCMEQ_RAM_TCMEQ_RAM__B 0 +#define VSB_TCMEQ_RAM_TCMEQ_RAM__W 16 +#define VSB_TCMEQ_RAM_TCMEQ_RAM__M 0xFFFF +#define VSB_TCMEQ_RAM_TCMEQ_RAM__PRE 0x0 + +#define VSB_FCPRE_RAM__A 0x1C50000 + +#define VSB_FCPRE_RAM_FCPRE_RAM__B 0 +#define VSB_FCPRE_RAM_FCPRE_RAM__W 16 +#define VSB_FCPRE_RAM_FCPRE_RAM__M 0xFFFF +#define VSB_FCPRE_RAM_FCPRE_RAM__PRE 0x0 + +#define VSB_EQTAP_RAM__A 0x1C60000 + +#define VSB_EQTAP_RAM_EQTAP_RAM__B 0 +#define VSB_EQTAP_RAM_EQTAP_RAM__W 12 +#define VSB_EQTAP_RAM_EQTAP_RAM__M 0xFFF +#define VSB_EQTAP_RAM_EQTAP_RAM__PRE 0x0 + +#endif diff --git a/drivers/media/dvb-frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h new file mode 100644 index 0000000000..ba54fd9ada --- /dev/null +++ b/drivers/media/dvb-frontends/drxd.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drxd.h: DRXD DVB-T demodulator driver + * + * Copyright (C) 2005-2007 Micronas + */ + +#ifndef _DRXD_H_ +#define _DRXD_H_ + +#include <linux/types.h> +#include <linux/i2c.h> + +struct drxd_config { + u8 index; + + u8 pll_address; + u8 pll_type; +#define DRXD_PLL_NONE 0 +#define DRXD_PLL_DTT7520X 1 +#define DRXD_PLL_MT3X0823 2 + + u32 clock; + u8 insert_rs_byte; + + u8 demod_address; + u8 demoda_address; + u8 demod_revision; + + /* If the tuner is not behind an i2c gate, be sure to flip this bit + or else the i2c bus could get wedged */ + u8 disable_i2c_gate_ctrl; + + u32 IF; + s16(*osc_deviation) (void *priv, s16 dev, int flag); +}; + +#if IS_REACHABLE(CONFIG_DVB_DRXD) +extern +struct dvb_frontend *drxd_attach(const struct drxd_config *config, + void *priv, struct i2c_adapter *i2c, + struct device *dev); +#else +static inline +struct dvb_frontend *drxd_attach(const struct drxd_config *config, + void *priv, struct i2c_adapter *i2c, + struct device *dev) +{ + printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", + __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/drxd_firm.c b/drivers/media/dvb-frontends/drxd_firm.c new file mode 100644 index 0000000000..8aae3e0350 --- /dev/null +++ b/drivers/media/dvb-frontends/drxd_firm.c @@ -0,0 +1,913 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drxd_firm.c : DRXD firmware tables + * + * Copyright (C) 2006-2007 Micronas + */ + +/* TODO: generate this file with a script from a settings file */ + +/* Contains A2 firmware version: 1.4.2 + * Contains B1 firmware version: 3.3.33 + * Contains settings from driver 1.4.23 +*/ + +#include "drxd_firm.h" + +#define ADDRESS(x) ((x) & 0xFF), (((x)>>8) & 0xFF), (((x)>>16) & 0xFF), (((x)>>24) & 0xFF) +#define LENGTH(x) ((x) & 0xFF), (((x)>>8) & 0xFF) + +/* Is written via block write, must be little endian */ +#define DATA16(x) ((x) & 0xFF), (((x)>>8) & 0xFF) + +#define WRBLOCK(a, l) ADDRESS(a), LENGTH(l) +#define WR16(a, d) ADDRESS(a), LENGTH(1), DATA16(d) + +#define END_OF_TABLE 0xFF, 0xFF, 0xFF, 0xFF + +/* HI firmware patches */ + +#define HI_TR_FUNC_ADDR HI_IF_RAM_USR_BEGIN__A +#define HI_TR_FUNC_SIZE 9 /* size of this function in instruction words */ + +u8 DRXD_InitAtomicRead[] = { + WRBLOCK(HI_TR_FUNC_ADDR, HI_TR_FUNC_SIZE), + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0x60, 0x04, /* r0rami.dt -> ring.xba; */ + 0x61, 0x04, /* r0rami.dt -> ring.xad; */ + 0xE3, 0x07, /* HI_RA_RAM_USR_BEGIN -> ring.iad; */ + 0x40, 0x00, /* (long immediate) */ + 0x64, 0x04, /* r0rami.dt -> ring.len; */ + 0x65, 0x04, /* r0rami.dt -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0x38, 0x00, /* 0 -> jumps.ad; */ + END_OF_TABLE +}; + +/* Pins D0 and D1 of the parallel MPEG output can be used + to set the I2C address of a device. */ + +#define HI_RST_FUNC_ADDR (HI_IF_RAM_USR_BEGIN__A + HI_TR_FUNC_SIZE) +#define HI_RST_FUNC_SIZE 54 /* size of this function in instruction words */ + +/* D0 Version */ +u8 DRXD_HiI2cPatch_1[] = { + WRBLOCK(HI_RST_FUNC_ADDR, HI_RST_FUNC_SIZE), + 0xC8, 0x07, 0x01, 0x00, /* MASK -> reg0.dt; */ + 0xE0, 0x07, 0x15, 0x02, /* (EC__BLK << 6) + EC_OC_REG__BNK -> ring.xba; */ + 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ + 0xA2, 0x00, /* M_BNK_ID_DAT -> ring.iba; */ + 0x23, 0x00, /* &data -> ring.iad; */ + 0x24, 0x00, /* 0 -> ring.len; */ + 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0x42, 0x00, /* &data+1 -> w0ram.ad; */ + 0xC0, 0x07, 0xFF, 0x0F, /* -1 -> w0ram.dt; */ + 0x63, 0x00, /* &data+1 -> ring.iad; */ + 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0xE1, 0x07, 0x38, 0x00, /* EC_OC_REG_OCR_MPG_USR_DAT__A -> ring.xad; */ + 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ + 0x23, 0x00, /* &data -> ring.iad; */ + 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0x42, 0x00, /* &data+1 -> w0ram.ad; */ + 0x0F, 0x04, /* r0ram.dt -> and.op; */ + 0x1C, 0x06, /* reg0.dt -> and.tr; */ + 0xCF, 0x04, /* and.rs -> add.op; */ + 0xD0, 0x07, 0x70, 0x00, /* DEF_DEV_ID -> add.tr; */ + 0xD0, 0x04, /* add.rs -> add.tr; */ + 0xC8, 0x04, /* add.rs -> reg0.dt; */ + 0x60, 0x00, /* reg0.dt -> w0ram.dt; */ + 0xC2, 0x07, 0x10, 0x00, /* SLV0_BASE -> w0rami.ad; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ + 0xC2, 0x07, 0x20, 0x00, /* SLV1_BASE -> w0rami.ad; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ + 0xC2, 0x07, 0x30, 0x00, /* CMD_BASE -> w0rami.ad; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x68, 0x00, /* M_IC_SEL_PT1 -> i2c.sel; */ + 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ + 0x28, 0x00, /* M_IC_SEL_PT0 -> i2c.sel; */ + 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ + 0xF8, 0x07, 0x2F, 0x00, /* 0x2F -> jumps.ad; */ + + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 0) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 1) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 2) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 3) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + + /* Force quick and dirty reset */ + WR16(B_HI_CT_REG_COMM_STATE__A, 0), + END_OF_TABLE +}; + +/* D0,D1 Version */ +u8 DRXD_HiI2cPatch_3[] = { + WRBLOCK(HI_RST_FUNC_ADDR, HI_RST_FUNC_SIZE), + 0xC8, 0x07, 0x03, 0x00, /* MASK -> reg0.dt; */ + 0xE0, 0x07, 0x15, 0x02, /* (EC__BLK << 6) + EC_OC_REG__BNK -> ring.xba; */ + 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ + 0xA2, 0x00, /* M_BNK_ID_DAT -> ring.iba; */ + 0x23, 0x00, /* &data -> ring.iad; */ + 0x24, 0x00, /* 0 -> ring.len; */ + 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0x42, 0x00, /* &data+1 -> w0ram.ad; */ + 0xC0, 0x07, 0xFF, 0x0F, /* -1 -> w0ram.dt; */ + 0x63, 0x00, /* &data+1 -> ring.iad; */ + 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0xE1, 0x07, 0x38, 0x00, /* EC_OC_REG_OCR_MPG_USR_DAT__A -> ring.xad; */ + 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ + 0x23, 0x00, /* &data -> ring.iad; */ + 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ + 0x26, 0x00, /* 0 -> ring.rdy; */ + 0x42, 0x00, /* &data+1 -> w0ram.ad; */ + 0x0F, 0x04, /* r0ram.dt -> and.op; */ + 0x1C, 0x06, /* reg0.dt -> and.tr; */ + 0xCF, 0x04, /* and.rs -> add.op; */ + 0xD0, 0x07, 0x70, 0x00, /* DEF_DEV_ID -> add.tr; */ + 0xD0, 0x04, /* add.rs -> add.tr; */ + 0xC8, 0x04, /* add.rs -> reg0.dt; */ + 0x60, 0x00, /* reg0.dt -> w0ram.dt; */ + 0xC2, 0x07, 0x10, 0x00, /* SLV0_BASE -> w0rami.ad; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ + 0xC2, 0x07, 0x20, 0x00, /* SLV1_BASE -> w0rami.ad; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ + 0xC2, 0x07, 0x30, 0x00, /* CMD_BASE -> w0rami.ad; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x01, 0x00, /* 0 -> w0rami.dt; */ + 0x68, 0x00, /* M_IC_SEL_PT1 -> i2c.sel; */ + 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ + 0x28, 0x00, /* M_IC_SEL_PT0 -> i2c.sel; */ + 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ + 0xF8, 0x07, 0x2F, 0x00, /* 0x2F -> jumps.ad; */ + + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 0) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 1) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 2) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 3) + 1)), + (u16) (HI_RST_FUNC_ADDR & 0x3FF)), + + /* Force quick and dirty reset */ + WR16(B_HI_CT_REG_COMM_STATE__A, 0), + END_OF_TABLE +}; + +u8 DRXD_ResetCEFR[] = { + WRBLOCK(CE_REG_FR_TREAL00__A, 57), + 0x52, 0x00, /* CE_REG_FR_TREAL00__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG00__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL01__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG01__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL02__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG02__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL03__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG03__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL04__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG04__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL05__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG05__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL06__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG06__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL07__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG07__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL08__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG08__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL09__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG09__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL10__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG10__A */ + 0x52, 0x00, /* CE_REG_FR_TREAL11__A */ + 0x00, 0x00, /* CE_REG_FR_TIMAG11__A */ + + 0x52, 0x00, /* CE_REG_FR_MID_TAP__A */ + + 0x0B, 0x00, /* CE_REG_FR_SQS_G00__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G01__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G02__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G03__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G04__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G05__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G06__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G07__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G08__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G09__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G10__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G11__A */ + 0x0B, 0x00, /* CE_REG_FR_SQS_G12__A */ + + 0xFF, 0x01, /* CE_REG_FR_RIO_G00__A */ + 0x90, 0x01, /* CE_REG_FR_RIO_G01__A */ + 0x0B, 0x01, /* CE_REG_FR_RIO_G02__A */ + 0xC8, 0x00, /* CE_REG_FR_RIO_G03__A */ + 0xA0, 0x00, /* CE_REG_FR_RIO_G04__A */ + 0x85, 0x00, /* CE_REG_FR_RIO_G05__A */ + 0x72, 0x00, /* CE_REG_FR_RIO_G06__A */ + 0x64, 0x00, /* CE_REG_FR_RIO_G07__A */ + 0x59, 0x00, /* CE_REG_FR_RIO_G08__A */ + 0x50, 0x00, /* CE_REG_FR_RIO_G09__A */ + 0x49, 0x00, /* CE_REG_FR_RIO_G10__A */ + + 0x10, 0x00, /* CE_REG_FR_MODE__A */ + 0x78, 0x00, /* CE_REG_FR_SQS_TRH__A */ + 0x00, 0x00, /* CE_REG_FR_RIO_GAIN__A */ + 0x00, 0x02, /* CE_REG_FR_BYPASS__A */ + 0x0D, 0x00, /* CE_REG_FR_PM_SET__A */ + 0x07, 0x00, /* CE_REG_FR_ERR_SH__A */ + 0x04, 0x00, /* CE_REG_FR_MAN_SH__A */ + 0x06, 0x00, /* CE_REG_FR_TAP_SH__A */ + + END_OF_TABLE +}; + +u8 DRXD_InitFEA2_1[] = { + WRBLOCK(FE_AD_REG_PD__A, 3), + 0x00, 0x00, /* FE_AD_REG_PD__A */ + 0x01, 0x00, /* FE_AD_REG_INVEXT__A */ + 0x00, 0x00, /* FE_AD_REG_CLKNEG__A */ + + WRBLOCK(FE_AG_REG_DCE_AUR_CNT__A, 2), + 0x10, 0x00, /* FE_AG_REG_DCE_AUR_CNT__A */ + 0x10, 0x00, /* FE_AG_REG_DCE_RUR_CNT__A */ + + WRBLOCK(FE_AG_REG_ACE_AUR_CNT__A, 2), + 0x0E, 0x00, /* FE_AG_REG_ACE_AUR_CNT__A */ + 0x00, 0x00, /* FE_AG_REG_ACE_RUR_CNT__A */ + + WRBLOCK(FE_AG_REG_EGC_FLA_RGN__A, 5), + 0x04, 0x00, /* FE_AG_REG_EGC_FLA_RGN__A */ + 0x1F, 0x00, /* FE_AG_REG_EGC_SLO_RGN__A */ + 0x00, 0x00, /* FE_AG_REG_EGC_JMP_PSN__A */ + 0x00, 0x00, /* FE_AG_REG_EGC_FLA_INC__A */ + 0x00, 0x00, /* FE_AG_REG_EGC_FLA_DEC__A */ + + WRBLOCK(FE_AG_REG_GC1_AGC_MAX__A, 2), + 0xFF, 0x01, /* FE_AG_REG_GC1_AGC_MAX__A */ + 0x00, 0xFE, /* FE_AG_REG_GC1_AGC_MIN__A */ + + WRBLOCK(FE_AG_REG_IND_WIN__A, 29), + 0x00, 0x00, /* FE_AG_REG_IND_WIN__A */ + 0x05, 0x00, /* FE_AG_REG_IND_THD_LOL__A */ + 0x0F, 0x00, /* FE_AG_REG_IND_THD_HIL__A */ + 0x00, 0x00, /* FE_AG_REG_IND_DEL__A don't care */ + 0x1E, 0x00, /* FE_AG_REG_IND_PD1_WRI__A */ + 0x0C, 0x00, /* FE_AG_REG_PDA_AUR_CNT__A */ + 0x00, 0x00, /* FE_AG_REG_PDA_RUR_CNT__A */ + 0x00, 0x00, /* FE_AG_REG_PDA_AVE_DAT__A don't care */ + 0x00, 0x00, /* FE_AG_REG_PDC_RUR_CNT__A */ + 0x01, 0x00, /* FE_AG_REG_PDC_SET_LVL__A */ + 0x02, 0x00, /* FE_AG_REG_PDC_FLA_RGN__A */ + 0x00, 0x00, /* FE_AG_REG_PDC_JMP_PSN__A don't care */ + 0xFF, 0xFF, /* FE_AG_REG_PDC_FLA_STP__A */ + 0xFF, 0xFF, /* FE_AG_REG_PDC_SLO_STP__A */ + 0x00, 0x1F, /* FE_AG_REG_PDC_PD2_WRI__A don't care */ + 0x00, 0x00, /* FE_AG_REG_PDC_MAP_DAT__A don't care */ + 0x02, 0x00, /* FE_AG_REG_PDC_MAX__A */ + 0x0C, 0x00, /* FE_AG_REG_TGA_AUR_CNT__A */ + 0x00, 0x00, /* FE_AG_REG_TGA_RUR_CNT__A */ + 0x00, 0x00, /* FE_AG_REG_TGA_AVE_DAT__A don't care */ + 0x00, 0x00, /* FE_AG_REG_TGC_RUR_CNT__A */ + 0x22, 0x00, /* FE_AG_REG_TGC_SET_LVL__A */ + 0x15, 0x00, /* FE_AG_REG_TGC_FLA_RGN__A */ + 0x00, 0x00, /* FE_AG_REG_TGC_JMP_PSN__A don't care */ + 0x01, 0x00, /* FE_AG_REG_TGC_FLA_STP__A */ + 0x0A, 0x00, /* FE_AG_REG_TGC_SLO_STP__A */ + 0x00, 0x00, /* FE_AG_REG_TGC_MAP_DAT__A don't care */ + 0x10, 0x00, /* FE_AG_REG_FGA_AUR_CNT__A */ + 0x10, 0x00, /* FE_AG_REG_FGA_RUR_CNT__A */ + + WRBLOCK(FE_AG_REG_BGC_FGC_WRI__A, 2), + 0x00, 0x00, /* FE_AG_REG_BGC_FGC_WRI__A */ + 0x00, 0x00, /* FE_AG_REG_BGC_CGC_WRI__A */ + + WRBLOCK(FE_FD_REG_SCL__A, 3), + 0x05, 0x00, /* FE_FD_REG_SCL__A */ + 0x03, 0x00, /* FE_FD_REG_MAX_LEV__A */ + 0x05, 0x00, /* FE_FD_REG_NR__A */ + + WRBLOCK(FE_CF_REG_SCL__A, 5), + 0x16, 0x00, /* FE_CF_REG_SCL__A */ + 0x04, 0x00, /* FE_CF_REG_MAX_LEV__A */ + 0x06, 0x00, /* FE_CF_REG_NR__A */ + 0x00, 0x00, /* FE_CF_REG_IMP_VAL__A */ + 0x01, 0x00, /* FE_CF_REG_MEAS_VAL__A */ + + WRBLOCK(FE_CU_REG_FRM_CNT_RST__A, 2), + 0x00, 0x08, /* FE_CU_REG_FRM_CNT_RST__A */ + 0x00, 0x00, /* FE_CU_REG_FRM_CNT_STR__A */ + + END_OF_TABLE +}; + + /* with PGA */ +/* WR16COND( DRXD_WITH_PGA, FE_AG_REG_AG_PGA_MODE__A , 0x0004), */ + /* without PGA */ +/* WR16COND( DRXD_WITHOUT_PGA, FE_AG_REG_AG_PGA_MODE__A , 0x0001), */ +/* WR16(FE_AG_REG_AG_AGC_SIO__A, (extAttr -> FeAgRegAgAgcSio), 0x0000 );*/ +/* WR16(FE_AG_REG_AG_PWD__A ,(extAttr -> FeAgRegAgPwd), 0x0000 );*/ + +u8 DRXD_InitFEA2_2[] = { + WR16(FE_AG_REG_CDR_RUR_CNT__A, 0x0010), + WR16(FE_AG_REG_FGM_WRI__A, 48), + /* Activate measurement, activate scale */ + WR16(FE_FD_REG_MEAS_VAL__A, 0x0001), + + WR16(FE_CU_REG_COMM_EXEC__A, 0x0001), + WR16(FE_CF_REG_COMM_EXEC__A, 0x0001), + WR16(FE_IF_REG_COMM_EXEC__A, 0x0001), + WR16(FE_FD_REG_COMM_EXEC__A, 0x0001), + WR16(FE_FS_REG_COMM_EXEC__A, 0x0001), + WR16(FE_AD_REG_COMM_EXEC__A, 0x0001), + WR16(FE_AG_REG_COMM_EXEC__A, 0x0001), + WR16(FE_AG_REG_AG_MODE_LOP__A, 0x895E), + + END_OF_TABLE +}; + +u8 DRXD_InitFEB1_1[] = { + WR16(B_FE_AD_REG_PD__A, 0x0000), + WR16(B_FE_AD_REG_CLKNEG__A, 0x0000), + WR16(B_FE_AG_REG_BGC_FGC_WRI__A, 0x0000), + WR16(B_FE_AG_REG_BGC_CGC_WRI__A, 0x0000), + WR16(B_FE_AG_REG_AG_MODE_LOP__A, 0x000a), + WR16(B_FE_AG_REG_IND_PD1_WRI__A, 35), + WR16(B_FE_AG_REG_IND_WIN__A, 0), + WR16(B_FE_AG_REG_IND_THD_LOL__A, 8), + WR16(B_FE_AG_REG_IND_THD_HIL__A, 8), + WR16(B_FE_CF_REG_IMP_VAL__A, 1), + WR16(B_FE_AG_REG_EGC_FLA_RGN__A, 7), + END_OF_TABLE +}; + + /* with PGA */ +/* WR16(B_FE_AG_REG_AG_PGA_MODE__A , 0x0000, 0x0000); */ + /* without PGA */ +/* WR16(B_FE_AG_REG_AG_PGA_MODE__A , + B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, 0x0000);*/ + /* WR16(B_FE_AG_REG_AG_AGC_SIO__A,(extAttr -> FeAgRegAgAgcSio), 0x0000 );*//*added HS 23-05-2005 */ +/* WR16(B_FE_AG_REG_AG_PWD__A ,(extAttr -> FeAgRegAgPwd), 0x0000 );*/ + +u8 DRXD_InitFEB1_2[] = { + WR16(B_FE_COMM_EXEC__A, 0x0001), + + /* RF-AGC setup */ + WR16(B_FE_AG_REG_PDA_AUR_CNT__A, 0x0C), + WR16(B_FE_AG_REG_PDC_SET_LVL__A, 0x01), + WR16(B_FE_AG_REG_PDC_FLA_RGN__A, 0x02), + WR16(B_FE_AG_REG_PDC_FLA_STP__A, 0xFFFF), + WR16(B_FE_AG_REG_PDC_SLO_STP__A, 0xFFFF), + WR16(B_FE_AG_REG_PDC_MAX__A, 0x02), + WR16(B_FE_AG_REG_TGA_AUR_CNT__A, 0x0C), + WR16(B_FE_AG_REG_TGC_SET_LVL__A, 0x22), + WR16(B_FE_AG_REG_TGC_FLA_RGN__A, 0x15), + WR16(B_FE_AG_REG_TGC_FLA_STP__A, 0x01), + WR16(B_FE_AG_REG_TGC_SLO_STP__A, 0x0A), + + WR16(B_FE_CU_REG_DIV_NFC_CLP__A, 0), + WR16(B_FE_CU_REG_CTR_NFC_OCR__A, 25000), + WR16(B_FE_CU_REG_CTR_NFC_ICR__A, 1), + END_OF_TABLE +}; + +u8 DRXD_InitCPA2[] = { + WRBLOCK(CP_REG_BR_SPL_OFFSET__A, 2), + 0x07, 0x00, /* CP_REG_BR_SPL_OFFSET__A */ + 0x0A, 0x00, /* CP_REG_BR_STR_DEL__A */ + + WRBLOCK(CP_REG_RT_ANG_INC0__A, 4), + 0x00, 0x00, /* CP_REG_RT_ANG_INC0__A */ + 0x00, 0x00, /* CP_REG_RT_ANG_INC1__A */ + 0x03, 0x00, /* CP_REG_RT_DETECT_ENA__A */ + 0x03, 0x00, /* CP_REG_RT_DETECT_TRH__A */ + + WRBLOCK(CP_REG_AC_NEXP_OFFS__A, 5), + 0x32, 0x00, /* CP_REG_AC_NEXP_OFFS__A */ + 0x62, 0x00, /* CP_REG_AC_AVER_POW__A */ + 0x82, 0x00, /* CP_REG_AC_MAX_POW__A */ + 0x26, 0x00, /* CP_REG_AC_WEIGHT_MAN__A */ + 0x0F, 0x00, /* CP_REG_AC_WEIGHT_EXP__A */ + + WRBLOCK(CP_REG_AC_AMP_MODE__A, 2), + 0x02, 0x00, /* CP_REG_AC_AMP_MODE__A */ + 0x01, 0x00, /* CP_REG_AC_AMP_FIX__A */ + + WR16(CP_REG_INTERVAL__A, 0x0005), + WR16(CP_REG_RT_EXP_MARG__A, 0x0004), + WR16(CP_REG_AC_ANG_MODE__A, 0x0003), + + WR16(CP_REG_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_InitCPB1[] = { + WR16(B_CP_REG_BR_SPL_OFFSET__A, 0x0008), + WR16(B_CP_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_InitCEA2[] = { + WRBLOCK(CE_REG_AVG_POW__A, 4), + 0x62, 0x00, /* CE_REG_AVG_POW__A */ + 0x78, 0x00, /* CE_REG_MAX_POW__A */ + 0x62, 0x00, /* CE_REG_ATT__A */ + 0x17, 0x00, /* CE_REG_NRED__A */ + + WRBLOCK(CE_REG_NE_ERR_SELECT__A, 2), + 0x07, 0x00, /* CE_REG_NE_ERR_SELECT__A */ + 0xEB, 0xFF, /* CE_REG_NE_TD_CAL__A */ + + WRBLOCK(CE_REG_NE_MIXAVG__A, 2), + 0x06, 0x00, /* CE_REG_NE_MIXAVG__A */ + 0x00, 0x00, /* CE_REG_NE_NUPD_OFS__A */ + + WRBLOCK(CE_REG_PE_NEXP_OFFS__A, 2), + 0x00, 0x00, /* CE_REG_PE_NEXP_OFFS__A */ + 0x00, 0x00, /* CE_REG_PE_TIMESHIFT__A */ + + WRBLOCK(CE_REG_TP_A0_TAP_NEW__A, 3), + 0x00, 0x01, /* CE_REG_TP_A0_TAP_NEW__A */ + 0x01, 0x00, /* CE_REG_TP_A0_TAP_NEW_VALID__A */ + 0x0E, 0x00, /* CE_REG_TP_A0_MU_LMS_STEP__A */ + + WRBLOCK(CE_REG_TP_A1_TAP_NEW__A, 3), + 0x00, 0x00, /* CE_REG_TP_A1_TAP_NEW__A */ + 0x01, 0x00, /* CE_REG_TP_A1_TAP_NEW_VALID__A */ + 0x0A, 0x00, /* CE_REG_TP_A1_MU_LMS_STEP__A */ + + WRBLOCK(CE_REG_FI_SHT_INCR__A, 2), + 0x12, 0x00, /* CE_REG_FI_SHT_INCR__A */ + 0x0C, 0x00, /* CE_REG_FI_EXP_NORM__A */ + + WRBLOCK(CE_REG_IR_INPUTSEL__A, 3), + 0x00, 0x00, /* CE_REG_IR_INPUTSEL__A */ + 0x00, 0x00, /* CE_REG_IR_STARTPOS__A */ + 0xFF, 0x00, /* CE_REG_IR_NEXP_THRES__A */ + + WR16(CE_REG_TI_NEXP_OFFS__A, 0x0000), + + END_OF_TABLE +}; + +u8 DRXD_InitCEB1[] = { + WR16(B_CE_REG_TI_PHN_ENABLE__A, 0x0001), + WR16(B_CE_REG_FR_PM_SET__A, 0x000D), + + END_OF_TABLE +}; + +u8 DRXD_InitEQA2[] = { + WRBLOCK(EQ_REG_OT_QNT_THRES0__A, 4), + 0x1E, 0x00, /* EQ_REG_OT_QNT_THRES0__A */ + 0x1F, 0x00, /* EQ_REG_OT_QNT_THRES1__A */ + 0x06, 0x00, /* EQ_REG_OT_CSI_STEP__A */ + 0x02, 0x00, /* EQ_REG_OT_CSI_OFFSET__A */ + + WR16(EQ_REG_TD_REQ_SMB_CNT__A, 0x0200), + WR16(EQ_REG_IS_CLIP_EXP__A, 0x001F), + WR16(EQ_REG_SN_OFFSET__A, (u16) (-7)), + WR16(EQ_REG_RC_SEL_CAR__A, 0x0002), + WR16(EQ_REG_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_InitEQB1[] = { + WR16(B_EQ_REG_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_ResetECRAM[] = { + /* Reset packet sync bytes in EC_VD ram */ + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), + + /* Reset packet sync bytes in EC_RS ram */ + WR16(EC_RS_EC_RAM__A, 0x0000), + WR16(EC_RS_EC_RAM__A + 204, 0x0000), + END_OF_TABLE +}; + +u8 DRXD_InitECA2[] = { + WRBLOCK(EC_SB_REG_CSI_HI__A, 6), + 0x1F, 0x00, /* EC_SB_REG_CSI_HI__A */ + 0x1E, 0x00, /* EC_SB_REG_CSI_LO__A */ + 0x01, 0x00, /* EC_SB_REG_SMB_TGL__A */ + 0x7F, 0x00, /* EC_SB_REG_SNR_HI__A */ + 0x7F, 0x00, /* EC_SB_REG_SNR_MID__A */ + 0x7F, 0x00, /* EC_SB_REG_SNR_LO__A */ + + WRBLOCK(EC_RS_REG_REQ_PCK_CNT__A, 2), + 0x00, 0x10, /* EC_RS_REG_REQ_PCK_CNT__A */ + DATA16(EC_RS_REG_VAL_PCK), /* EC_RS_REG_VAL__A */ + + WRBLOCK(EC_OC_REG_TMD_TOP_MODE__A, 5), + 0x03, 0x00, /* EC_OC_REG_TMD_TOP_MODE__A */ + 0xF4, 0x01, /* EC_OC_REG_TMD_TOP_CNT__A */ + 0xC0, 0x03, /* EC_OC_REG_TMD_HIL_MAR__A */ + 0x40, 0x00, /* EC_OC_REG_TMD_LOL_MAR__A */ + 0x03, 0x00, /* EC_OC_REG_TMD_CUR_CNT__A */ + + WRBLOCK(EC_OC_REG_AVR_ASH_CNT__A, 2), + 0x06, 0x00, /* EC_OC_REG_AVR_ASH_CNT__A */ + 0x02, 0x00, /* EC_OC_REG_AVR_BSH_CNT__A */ + + WRBLOCK(EC_OC_REG_RCN_MODE__A, 7), + 0x07, 0x00, /* EC_OC_REG_RCN_MODE__A */ + 0x00, 0x00, /* EC_OC_REG_RCN_CRA_LOP__A */ + 0xc0, 0x00, /* EC_OC_REG_RCN_CRA_HIP__A */ + 0x00, 0x10, /* EC_OC_REG_RCN_CST_LOP__A */ + 0x00, 0x00, /* EC_OC_REG_RCN_CST_HIP__A */ + 0xFF, 0x01, /* EC_OC_REG_RCN_SET_LVL__A */ + 0x0D, 0x00, /* EC_OC_REG_RCN_GAI_LVL__A */ + + WRBLOCK(EC_OC_REG_RCN_CLP_LOP__A, 2), + 0x00, 0x00, /* EC_OC_REG_RCN_CLP_LOP__A */ + 0xC0, 0x00, /* EC_OC_REG_RCN_CLP_HIP__A */ + + WR16(EC_SB_REG_CSI_OFS__A, 0x0001), + WR16(EC_VD_REG_FORCE__A, 0x0002), + WR16(EC_VD_REG_REQ_SMB_CNT__A, 0x0001), + WR16(EC_VD_REG_RLK_ENA__A, 0x0001), + WR16(EC_OD_REG_SYNC__A, 0x0664), + WR16(EC_OC_REG_OC_MON_SIO__A, 0x0000), + WR16(EC_OC_REG_SNC_ISC_LVL__A, 0x0D0C), + /* Output zero on monitorbus pads, power saving */ + WR16(EC_OC_REG_OCR_MON_UOS__A, + (EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE | + EC_OC_REG_OCR_MON_UOS_VAL_ENABLE | + EC_OC_REG_OCR_MON_UOS_CLK_ENABLE)), + WR16(EC_OC_REG_OCR_MON_WRI__A, + EC_OC_REG_OCR_MON_WRI_INIT), + +/* CHK_ERROR(ResetECRAM(demod)); */ + /* Reset packet sync bytes in EC_VD ram */ + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), + + /* Reset packet sync bytes in EC_RS ram */ + WR16(EC_RS_EC_RAM__A, 0x0000), + WR16(EC_RS_EC_RAM__A + 204, 0x0000), + + WR16(EC_SB_REG_COMM_EXEC__A, 0x0001), + WR16(EC_VD_REG_COMM_EXEC__A, 0x0001), + WR16(EC_OD_REG_COMM_EXEC__A, 0x0001), + WR16(EC_RS_REG_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_InitECB1[] = { + WR16(B_EC_SB_REG_CSI_OFS0__A, 0x0001), + WR16(B_EC_SB_REG_CSI_OFS1__A, 0x0001), + WR16(B_EC_SB_REG_CSI_OFS2__A, 0x0001), + WR16(B_EC_SB_REG_CSI_LO__A, 0x000c), + WR16(B_EC_SB_REG_CSI_HI__A, 0x0018), + WR16(B_EC_SB_REG_SNR_HI__A, 0x007f), + WR16(B_EC_SB_REG_SNR_MID__A, 0x007f), + WR16(B_EC_SB_REG_SNR_LO__A, 0x007f), + + WR16(B_EC_OC_REG_DTO_CLKMODE__A, 0x0002), + WR16(B_EC_OC_REG_DTO_PER__A, 0x0006), + WR16(B_EC_OC_REG_DTO_BUR__A, 0x0001), + WR16(B_EC_OC_REG_RCR_CLKMODE__A, 0x0000), + WR16(B_EC_OC_REG_RCN_GAI_LVL__A, 0x000D), + WR16(B_EC_OC_REG_OC_MPG_SIO__A, 0x0000), + + /* Needed because shadow registers do not have correct default value */ + WR16(B_EC_OC_REG_RCN_CST_LOP__A, 0x1000), + WR16(B_EC_OC_REG_RCN_CST_HIP__A, 0x0000), + WR16(B_EC_OC_REG_RCN_CRA_LOP__A, 0x0000), + WR16(B_EC_OC_REG_RCN_CRA_HIP__A, 0x00C0), + WR16(B_EC_OC_REG_RCN_CLP_LOP__A, 0x0000), + WR16(B_EC_OC_REG_RCN_CLP_HIP__A, 0x00C0), + WR16(B_EC_OC_REG_DTO_INC_LOP__A, 0x0000), + WR16(B_EC_OC_REG_DTO_INC_HIP__A, 0x00C0), + + WR16(B_EC_OD_REG_SYNC__A, 0x0664), + WR16(B_EC_RS_REG_REQ_PCK_CNT__A, 0x1000), + +/* CHK_ERROR(ResetECRAM(demod)); */ + /* Reset packet sync bytes in EC_VD ram */ + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), + + /* Reset packet sync bytes in EC_RS ram */ + WR16(EC_RS_EC_RAM__A, 0x0000), + WR16(EC_RS_EC_RAM__A + 204, 0x0000), + + WR16(B_EC_SB_REG_COMM_EXEC__A, 0x0001), + WR16(B_EC_VD_REG_COMM_EXEC__A, 0x0001), + WR16(B_EC_OD_REG_COMM_EXEC__A, 0x0001), + WR16(B_EC_RS_REG_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_ResetECA2[] = { + + WR16(EC_OC_REG_COMM_EXEC__A, 0x0000), + WR16(EC_OD_REG_COMM_EXEC__A, 0x0000), + + WRBLOCK(EC_OC_REG_TMD_TOP_MODE__A, 5), + 0x03, 0x00, /* EC_OC_REG_TMD_TOP_MODE__A */ + 0xF4, 0x01, /* EC_OC_REG_TMD_TOP_CNT__A */ + 0xC0, 0x03, /* EC_OC_REG_TMD_HIL_MAR__A */ + 0x40, 0x00, /* EC_OC_REG_TMD_LOL_MAR__A */ + 0x03, 0x00, /* EC_OC_REG_TMD_CUR_CNT__A */ + + WRBLOCK(EC_OC_REG_AVR_ASH_CNT__A, 2), + 0x06, 0x00, /* EC_OC_REG_AVR_ASH_CNT__A */ + 0x02, 0x00, /* EC_OC_REG_AVR_BSH_CNT__A */ + + WRBLOCK(EC_OC_REG_RCN_MODE__A, 7), + 0x07, 0x00, /* EC_OC_REG_RCN_MODE__A */ + 0x00, 0x00, /* EC_OC_REG_RCN_CRA_LOP__A */ + 0xc0, 0x00, /* EC_OC_REG_RCN_CRA_HIP__A */ + 0x00, 0x10, /* EC_OC_REG_RCN_CST_LOP__A */ + 0x00, 0x00, /* EC_OC_REG_RCN_CST_HIP__A */ + 0xFF, 0x01, /* EC_OC_REG_RCN_SET_LVL__A */ + 0x0D, 0x00, /* EC_OC_REG_RCN_GAI_LVL__A */ + + WRBLOCK(EC_OC_REG_RCN_CLP_LOP__A, 2), + 0x00, 0x00, /* EC_OC_REG_RCN_CLP_LOP__A */ + 0xC0, 0x00, /* EC_OC_REG_RCN_CLP_HIP__A */ + + WR16(EC_OD_REG_SYNC__A, 0x0664), + WR16(EC_OC_REG_OC_MON_SIO__A, 0x0000), + WR16(EC_OC_REG_SNC_ISC_LVL__A, 0x0D0C), + /* Output zero on monitorbus pads, power saving */ + WR16(EC_OC_REG_OCR_MON_UOS__A, + (EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE | + EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE | + EC_OC_REG_OCR_MON_UOS_VAL_ENABLE | + EC_OC_REG_OCR_MON_UOS_CLK_ENABLE)), + WR16(EC_OC_REG_OCR_MON_WRI__A, + EC_OC_REG_OCR_MON_WRI_INIT), + +/* CHK_ERROR(ResetECRAM(demod)); */ + /* Reset packet sync bytes in EC_VD ram */ + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), + WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), + + /* Reset packet sync bytes in EC_RS ram */ + WR16(EC_RS_EC_RAM__A, 0x0000), + WR16(EC_RS_EC_RAM__A + 204, 0x0000), + + WR16(EC_OD_REG_COMM_EXEC__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_InitSC[] = { + WR16(SC_COMM_EXEC__A, 0), + WR16(SC_COMM_STATE__A, 0), + +#ifdef COMPILE_FOR_QT + WR16(SC_RA_RAM_BE_OPT_DELAY__A, 0x100), +#endif + + /* SC is not started, this is done in SetChannels() */ + END_OF_TABLE +}; + +/* Diversity settings */ + +u8 DRXD_InitDiversityFront[] = { + /* Start demod ********* RF in , diversity out **************************** */ + WR16(B_SC_RA_RAM_CONFIG__A, B_SC_RA_RAM_CONFIG_FR_ENABLE__M | + B_SC_RA_RAM_CONFIG_FREQSCAN__M), + + WR16(B_SC_RA_RAM_LC_ABS_2K__A, 0x7), + WR16(B_SC_RA_RAM_LC_ABS_8K__A, 0x7), + WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, IRLEN_COARSE_8K), + WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, 1 << (11 - IRLEN_COARSE_8K)), + WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, 1 << (17 - IRLEN_COARSE_8K)), + WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, IRLEN_FINE_8K), + WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, 1 << (11 - IRLEN_FINE_8K)), + WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, 1 << (17 - IRLEN_FINE_8K)), + + WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, IRLEN_COARSE_2K), + WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, 1 << (11 - IRLEN_COARSE_2K)), + WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, 1 << (17 - IRLEN_COARSE_2K)), + WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, IRLEN_FINE_2K), + WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, 1 << (11 - IRLEN_FINE_2K)), + WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, 1 << (17 - IRLEN_FINE_2K)), + + WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, 7), + WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, 4), + WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, 7), + WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, 4), + WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, 500), + + WR16(B_CC_REG_DIVERSITY__A, 0x0001), + WR16(B_EC_OC_REG_OC_MODE_HIP__A, 0x0010), + WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_PASS_B_CE | + B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE | B_EQ_REG_RC_SEL_CAR_MEAS_B_CE), + + /* 0x2a ), *//* CE to PASS mux */ + + END_OF_TABLE +}; + +u8 DRXD_InitDiversityEnd[] = { + /* End demod *********** combining RF in and diversity in, MPEG TS out **** */ + /* disable near/far; switch on timing slave mode */ + WR16(B_SC_RA_RAM_CONFIG__A, B_SC_RA_RAM_CONFIG_FR_ENABLE__M | + B_SC_RA_RAM_CONFIG_FREQSCAN__M | + B_SC_RA_RAM_CONFIG_DIV_ECHO_ENABLE__M | + B_SC_RA_RAM_CONFIG_SLAVE__M | + B_SC_RA_RAM_CONFIG_DIV_BLANK_ENABLE__M +/* MV from CtrlDiversity */ + ), +#ifdef DRXDDIV_SRMM_SLAVING + WR16(SC_RA_RAM_LC_ABS_2K__A, 0x3c7), + WR16(SC_RA_RAM_LC_ABS_8K__A, 0x3c7), +#else + WR16(SC_RA_RAM_LC_ABS_2K__A, 0x7), + WR16(SC_RA_RAM_LC_ABS_8K__A, 0x7), +#endif + + WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, IRLEN_COARSE_8K), + WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, 1 << (11 - IRLEN_COARSE_8K)), + WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, 1 << (17 - IRLEN_COARSE_8K)), + WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, IRLEN_FINE_8K), + WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, 1 << (11 - IRLEN_FINE_8K)), + WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, 1 << (17 - IRLEN_FINE_8K)), + + WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, IRLEN_COARSE_2K), + WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, 1 << (11 - IRLEN_COARSE_2K)), + WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, 1 << (17 - IRLEN_COARSE_2K)), + WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, IRLEN_FINE_2K), + WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, 1 << (11 - IRLEN_FINE_2K)), + WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, 1 << (17 - IRLEN_FINE_2K)), + + WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, 7), + WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, 4), + WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, 7), + WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, 4), + WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, 500), + + WR16(B_CC_REG_DIVERSITY__A, 0x0001), + END_OF_TABLE +}; + +u8 DRXD_DisableDiversity[] = { + WR16(B_SC_RA_RAM_LC_ABS_2K__A, B_SC_RA_RAM_LC_ABS_2K__PRE), + WR16(B_SC_RA_RAM_LC_ABS_8K__A, B_SC_RA_RAM_LC_ABS_8K__PRE), + WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, + B_SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE), + WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, + B_SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE), + WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, + B_SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE), + WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, + B_SC_RA_RAM_IR_FINE_8K_LENGTH__PRE), + WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, + B_SC_RA_RAM_IR_FINE_8K_FREQINC__PRE), + WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, + B_SC_RA_RAM_IR_FINE_8K_KAISINC__PRE), + + WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, + B_SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE), + WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, + B_SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE), + WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, + B_SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE), + WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, + B_SC_RA_RAM_IR_FINE_2K_LENGTH__PRE), + WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, + B_SC_RA_RAM_IR_FINE_2K_FREQINC__PRE), + WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, + B_SC_RA_RAM_IR_FINE_2K_KAISINC__PRE), + + WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, B_LC_RA_RAM_FILTER_CRMM_A__PRE), + WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, B_LC_RA_RAM_FILTER_CRMM_B__PRE), + WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, B_LC_RA_RAM_FILTER_SRMM_A__PRE), + WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, B_LC_RA_RAM_FILTER_SRMM_B__PRE), + WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, B_LC_RA_RAM_FILTER_SYM_SET__PRE), + + WR16(B_CC_REG_DIVERSITY__A, 0x0000), + WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_INIT), /* combining disabled */ + + END_OF_TABLE +}; + +u8 DRXD_StartDiversityFront[] = { + /* Start demod, RF in and diversity out, no combining */ + WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), + WR16(B_FE_AD_REG_FDB_IN__A, 0x0), + WR16(B_FE_AD_REG_INVEXT__A, 0x0), + WR16(B_EQ_REG_COMM_MB__A, 0x12), /* EQ to MB out */ + WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_PASS_B_CE | /* CE to PASS mux */ + B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE | B_EQ_REG_RC_SEL_CAR_MEAS_B_CE), + + WR16(SC_RA_RAM_ECHO_SHIFT_LIM__A, 2), + + END_OF_TABLE +}; + +u8 DRXD_StartDiversityEnd[] = { + /* End demod, combining RF in and diversity in, MPEG TS out */ + WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), /* disable impulse noise cruncher */ + WR16(B_FE_AD_REG_INVEXT__A, 0x0), /* clock inversion (for sohard board) */ + WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apparently no mb delay matching is best */ + + WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_DIV_ON | /* org = 0x81 combining enabled */ + B_EQ_REG_RC_SEL_CAR_MEAS_A_CC | + B_EQ_REG_RC_SEL_CAR_PASS_A_CC | B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC), + + END_OF_TABLE +}; + +u8 DRXD_DiversityDelay8MHZ[] = { + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A, 1150 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A, 1100 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A, 1000 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A, 800 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A, 5420 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A, 5200 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A, 4800 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A, 4000 - 50), + END_OF_TABLE +}; + +u8 DRXD_DiversityDelay6MHZ[] = /* also used ok for 7 MHz */ +{ + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A, 1100 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A, 1000 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A, 900 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A, 600 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A, 5300 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A, 5000 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A, 4500 - 50), + WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A, 3500 - 50), + END_OF_TABLE +}; diff --git a/drivers/media/dvb-frontends/drxd_firm.h b/drivers/media/dvb-frontends/drxd_firm.h new file mode 100644 index 0000000000..b3f04dfe24 --- /dev/null +++ b/drivers/media/dvb-frontends/drxd_firm.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drxd_firm.h + * + * Copyright (C) 2006-2007 Micronas + */ + +#ifndef _DRXD_FIRM_H_ +#define _DRXD_FIRM_H_ + +#include <linux/types.h> +#include "drxd_map_firm.h" + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 4 +#define VERSION_PATCH 23 + +#define HI_TR_FUNC_ADDR HI_IF_RAM_USR_BEGIN__A + +#define DRXD_MAX_RETRIES (1000) +#define HI_I2C_DELAY 84 +#define HI_I2C_BRIDGE_DELAY 750 + +#define EQ_TD_TPS_PWR_UNKNOWN 0x00C0 /* Unknown configurations */ +#define EQ_TD_TPS_PWR_QPSK 0x016a +#define EQ_TD_TPS_PWR_QAM16_ALPHAN 0x0195 +#define EQ_TD_TPS_PWR_QAM16_ALPHA1 0x0195 +#define EQ_TD_TPS_PWR_QAM16_ALPHA2 0x011E +#define EQ_TD_TPS_PWR_QAM16_ALPHA4 0x01CE +#define EQ_TD_TPS_PWR_QAM64_ALPHAN 0x019F +#define EQ_TD_TPS_PWR_QAM64_ALPHA1 0x019F +#define EQ_TD_TPS_PWR_QAM64_ALPHA2 0x00F8 +#define EQ_TD_TPS_PWR_QAM64_ALPHA4 0x014D + +#define DRXD_DEF_AG_PWD_CONSUMER 0x000E +#define DRXD_DEF_AG_PWD_PRO 0x0000 +#define DRXD_DEF_AG_AGC_SIO 0x0000 + +#define DRXD_FE_CTRL_MAX 1023 + +#define DRXD_OSCDEV_DO_SCAN (16) + +#define DRXD_OSCDEV_DONT_SCAN (0) + +#define DRXD_OSCDEV_STEP (275) + +#define DRXD_SCAN_TIMEOUT (650) + +#define DRXD_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L) +#define DRXD_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L) +#define DRXD_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L) + +#define IRLEN_COARSE_8K (10) +#define IRLEN_FINE_8K (10) +#define IRLEN_COARSE_2K (7) +#define IRLEN_FINE_2K (9) +#define DIFF_INVALID (511) +#define DIFF_TARGET (4) +#define DIFF_MARGIN (1) + +extern u8 DRXD_InitAtomicRead[]; +extern u8 DRXD_HiI2cPatch_1[]; +extern u8 DRXD_HiI2cPatch_3[]; + +extern u8 DRXD_InitSC[]; + +extern u8 DRXD_ResetCEFR[]; +extern u8 DRXD_InitFEA2_1[]; +extern u8 DRXD_InitFEA2_2[]; +extern u8 DRXD_InitCPA2[]; +extern u8 DRXD_InitCEA2[]; +extern u8 DRXD_InitEQA2[]; +extern u8 DRXD_InitECA2[]; +extern u8 DRXD_ResetECA2[]; +extern u8 DRXD_ResetECRAM[]; + +extern u8 DRXD_A2_microcode[]; +extern u32 DRXD_A2_microcode_length; + +extern u8 DRXD_InitFEB1_1[]; +extern u8 DRXD_InitFEB1_2[]; +extern u8 DRXD_InitCPB1[]; +extern u8 DRXD_InitCEB1[]; +extern u8 DRXD_InitEQB1[]; +extern u8 DRXD_InitECB1[]; + +extern u8 DRXD_InitDiversityFront[]; +extern u8 DRXD_InitDiversityEnd[]; +extern u8 DRXD_DisableDiversity[]; +extern u8 DRXD_StartDiversityFront[]; +extern u8 DRXD_StartDiversityEnd[]; + +extern u8 DRXD_DiversityDelay8MHZ[]; +extern u8 DRXD_DiversityDelay6MHZ[]; + +extern u8 DRXD_B1_microcode[]; +extern u32 DRXD_B1_microcode_length; + +#endif diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c new file mode 100644 index 0000000000..6a531937f4 --- /dev/null +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -0,0 +1,2946 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drxd_hard.c: DVB-T Demodulator Micronas DRX3975D-A2,DRX397xD-B1 + * + * Copyright (C) 2003-2007 Micronas + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "drxd.h" +#include "drxd_firm.h" + +#define DRX_FW_FILENAME_A2 "drxd-a2-1.1.fw" +#define DRX_FW_FILENAME_B1 "drxd-b1-1.1.fw" + +#define CHUNK_SIZE 48 + +#define DRX_I2C_RMW 0x10 +#define DRX_I2C_BROADCAST 0x20 +#define DRX_I2C_CLEARCRC 0x80 +#define DRX_I2C_SINGLE_MASTER 0xC0 +#define DRX_I2C_MODEFLAGS 0xC0 +#define DRX_I2C_FLAGS 0xF0 + +#define DEFAULT_LOCK_TIMEOUT 1100 + +#define DRX_CHANNEL_AUTO 0 +#define DRX_CHANNEL_HIGH 1 +#define DRX_CHANNEL_LOW 2 + +#define DRX_LOCK_MPEG 1 +#define DRX_LOCK_FEC 2 +#define DRX_LOCK_DEMOD 4 + +/****************************************************************************/ + +enum CSCDState { + CSCD_INIT = 0, + CSCD_SET, + CSCD_SAVED +}; + +enum CDrxdState { + DRXD_UNINITIALIZED = 0, + DRXD_STOPPED, + DRXD_STARTED +}; + +enum AGC_CTRL_MODE { + AGC_CTRL_AUTO = 0, + AGC_CTRL_USER, + AGC_CTRL_OFF +}; + +enum OperationMode { + OM_Default, + OM_DVBT_Diversity_Front, + OM_DVBT_Diversity_End +}; + +struct SCfgAgc { + enum AGC_CTRL_MODE ctrlMode; + u16 outputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ + u16 settleLevel; /* range [0, ... , 1023], 1/n of fullscale range */ + u16 minOutputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ + u16 maxOutputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ + u16 speed; /* range [0, ... , 1023], 1/n of fullscale range */ + + u16 R1; + u16 R2; + u16 R3; +}; + +struct SNoiseCal { + int cpOpt; + short cpNexpOfs; + short tdCal2k; + short tdCal8k; +}; + +enum app_env { + APPENV_STATIC = 0, + APPENV_PORTABLE = 1, + APPENV_MOBILE = 2 +}; + +enum EIFFilter { + IFFILTER_SAW = 0, + IFFILTER_DISCRETE = 1 +}; + +struct drxd_state { + struct dvb_frontend frontend; + struct dvb_frontend_ops ops; + struct dtv_frontend_properties props; + + const struct firmware *fw; + struct device *dev; + + struct i2c_adapter *i2c; + void *priv; + struct drxd_config config; + + int i2c_access; + int init_done; + struct mutex mutex; + + u8 chip_adr; + u16 hi_cfg_timing_div; + u16 hi_cfg_bridge_delay; + u16 hi_cfg_wakeup_key; + u16 hi_cfg_ctrl; + + u16 intermediate_freq; + u16 osc_clock_freq; + + enum CSCDState cscd_state; + enum CDrxdState drxd_state; + + u16 sys_clock_freq; + s16 osc_clock_deviation; + u16 expected_sys_clock_freq; + + u16 insert_rs_byte; + u16 enable_parallel; + + int operation_mode; + + struct SCfgAgc if_agc_cfg; + struct SCfgAgc rf_agc_cfg; + + struct SNoiseCal noise_cal; + + u32 fe_fs_add_incr; + u32 org_fe_fs_add_incr; + u16 current_fe_if_incr; + + u16 m_FeAgRegAgPwd; + u16 m_FeAgRegAgAgcSio; + + u16 m_EcOcRegOcModeLop; + u16 m_EcOcRegSncSncLvl; + u8 *m_InitAtomicRead; + u8 *m_HiI2cPatch; + + u8 *m_ResetCEFR; + u8 *m_InitFE_1; + u8 *m_InitFE_2; + u8 *m_InitCP; + u8 *m_InitCE; + u8 *m_InitEQ; + u8 *m_InitSC; + u8 *m_InitEC; + u8 *m_ResetECRAM; + u8 *m_InitDiversityFront; + u8 *m_InitDiversityEnd; + u8 *m_DisableDiversity; + u8 *m_StartDiversityFront; + u8 *m_StartDiversityEnd; + + u8 *m_DiversityDelay8MHZ; + u8 *m_DiversityDelay6MHZ; + + u8 *microcode; + u32 microcode_length; + + int type_A; + int PGA; + int diversity; + int tuner_mirrors; + + enum app_env app_env_default; + enum app_env app_env_diversity; + +}; + +/****************************************************************************/ +/* I2C **********************************************************************/ +/****************************************************************************/ + +static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 * data, int len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len }; + + if (i2c_transfer(adap, &msg, 1) != 1) + return -1; + return 0; +} + +static int i2c_read(struct i2c_adapter *adap, + u8 adr, u8 *msg, int len, u8 *answ, int alen) +{ + struct i2c_msg msgs[2] = { + { + .addr = adr, .flags = 0, + .buf = msg, .len = len + }, { + .addr = adr, .flags = I2C_M_RD, + .buf = answ, .len = alen + } + }; + if (i2c_transfer(adap, msgs, 2) != 2) + return -1; + return 0; +} + +static inline u32 MulDiv32(u32 a, u32 b, u32 c) +{ + u64 tmp64; + + tmp64 = (u64)a * (u64)b; + do_div(tmp64, c); + + return (u32) tmp64; +} + +static int Read16(struct drxd_state *state, u32 reg, u16 *data, u8 flags) +{ + u8 adr = state->config.demod_address; + u8 mm1[4] = { reg & 0xff, (reg >> 16) & 0xff, + flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff + }; + u8 mm2[2]; + if (i2c_read(state->i2c, adr, mm1, 4, mm2, 2) < 0) + return -1; + if (data) + *data = mm2[0] | (mm2[1] << 8); + return mm2[0] | (mm2[1] << 8); +} + +static int Read32(struct drxd_state *state, u32 reg, u32 *data, u8 flags) +{ + u8 adr = state->config.demod_address; + u8 mm1[4] = { reg & 0xff, (reg >> 16) & 0xff, + flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff + }; + u8 mm2[4]; + + if (i2c_read(state->i2c, adr, mm1, 4, mm2, 4) < 0) + return -1; + if (data) + *data = + mm2[0] | (mm2[1] << 8) | (mm2[2] << 16) | (mm2[3] << 24); + return 0; +} + +static int Write16(struct drxd_state *state, u32 reg, u16 data, u8 flags) +{ + u8 adr = state->config.demod_address; + u8 mm[6] = { reg & 0xff, (reg >> 16) & 0xff, + flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff, + data & 0xff, (data >> 8) & 0xff + }; + + if (i2c_write(state->i2c, adr, mm, 6) < 0) + return -1; + return 0; +} + +static int Write32(struct drxd_state *state, u32 reg, u32 data, u8 flags) +{ + u8 adr = state->config.demod_address; + u8 mm[8] = { reg & 0xff, (reg >> 16) & 0xff, + flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff, + data & 0xff, (data >> 8) & 0xff, + (data >> 16) & 0xff, (data >> 24) & 0xff + }; + + if (i2c_write(state->i2c, adr, mm, 8) < 0) + return -1; + return 0; +} + +static int write_chunk(struct drxd_state *state, + u32 reg, u8 *data, u32 len, u8 flags) +{ + u8 adr = state->config.demod_address; + u8 mm[CHUNK_SIZE + 4] = { reg & 0xff, (reg >> 16) & 0xff, + flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff + }; + int i; + + for (i = 0; i < len; i++) + mm[4 + i] = data[i]; + if (i2c_write(state->i2c, adr, mm, 4 + len) < 0) { + printk(KERN_ERR "error in write_chunk\n"); + return -1; + } + return 0; +} + +static int WriteBlock(struct drxd_state *state, + u32 Address, u16 BlockSize, u8 *pBlock, u8 Flags) +{ + while (BlockSize > 0) { + u16 Chunk = BlockSize > CHUNK_SIZE ? CHUNK_SIZE : BlockSize; + + if (write_chunk(state, Address, pBlock, Chunk, Flags) < 0) + return -1; + pBlock += Chunk; + Address += (Chunk >> 1); + BlockSize -= Chunk; + } + return 0; +} + +static int WriteTable(struct drxd_state *state, u8 * pTable) +{ + int status = 0; + + if (!pTable) + return 0; + + while (!status) { + u16 Length; + u32 Address = pTable[0] | (pTable[1] << 8) | + (pTable[2] << 16) | (pTable[3] << 24); + + if (Address == 0xFFFFFFFF) + break; + pTable += sizeof(u32); + + Length = pTable[0] | (pTable[1] << 8); + pTable += sizeof(u16); + if (!Length) + break; + status = WriteBlock(state, Address, Length * 2, pTable, 0); + pTable += (Length * 2); + } + return status; +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static int ResetCEFR(struct drxd_state *state) +{ + return WriteTable(state, state->m_ResetCEFR); +} + +static int InitCP(struct drxd_state *state) +{ + return WriteTable(state, state->m_InitCP); +} + +static int InitCE(struct drxd_state *state) +{ + int status; + enum app_env AppEnv = state->app_env_default; + + do { + status = WriteTable(state, state->m_InitCE); + if (status < 0) + break; + + if (state->operation_mode == OM_DVBT_Diversity_Front || + state->operation_mode == OM_DVBT_Diversity_End) { + AppEnv = state->app_env_diversity; + } + if (AppEnv == APPENV_STATIC) { + status = Write16(state, CE_REG_TAPSET__A, 0x0000, 0); + if (status < 0) + break; + } else if (AppEnv == APPENV_PORTABLE) { + status = Write16(state, CE_REG_TAPSET__A, 0x0001, 0); + if (status < 0) + break; + } else if (AppEnv == APPENV_MOBILE && state->type_A) { + status = Write16(state, CE_REG_TAPSET__A, 0x0002, 0); + if (status < 0) + break; + } else if (AppEnv == APPENV_MOBILE && !state->type_A) { + status = Write16(state, CE_REG_TAPSET__A, 0x0006, 0); + if (status < 0) + break; + } + + /* start ce */ + status = Write16(state, B_CE_REG_COMM_EXEC__A, 0x0001, 0); + if (status < 0) + break; + } while (0); + return status; +} + +static int StopOC(struct drxd_state *state) +{ + int status = 0; + u16 ocSyncLvl = 0; + u16 ocModeLop = state->m_EcOcRegOcModeLop; + u16 dtoIncLop = 0; + u16 dtoIncHip = 0; + + do { + /* Store output configuration */ + status = Read16(state, EC_OC_REG_SNC_ISC_LVL__A, &ocSyncLvl, 0); + if (status < 0) + break; + /* CHK_ERROR(Read16(EC_OC_REG_OC_MODE_LOP__A, &ocModeLop)); */ + state->m_EcOcRegSncSncLvl = ocSyncLvl; + /* m_EcOcRegOcModeLop = ocModeLop; */ + + /* Flush FIFO (byte-boundary) at fixed rate */ + status = Read16(state, EC_OC_REG_RCN_MAP_LOP__A, &dtoIncLop, 0); + if (status < 0) + break; + status = Read16(state, EC_OC_REG_RCN_MAP_HIP__A, &dtoIncHip, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_DTO_INC_LOP__A, dtoIncLop, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_DTO_INC_HIP__A, dtoIncHip, 0); + if (status < 0) + break; + ocModeLop &= ~(EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M); + ocModeLop |= EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC; + status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, ocModeLop, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_HOLD, 0); + if (status < 0) + break; + + msleep(1); + /* Output pins to '0' */ + status = Write16(state, EC_OC_REG_OCR_MPG_UOS__A, EC_OC_REG_OCR_MPG_UOS__M, 0); + if (status < 0) + break; + + /* Force the OC out of sync */ + ocSyncLvl &= ~(EC_OC_REG_SNC_ISC_LVL_OSC__M); + status = Write16(state, EC_OC_REG_SNC_ISC_LVL__A, ocSyncLvl, 0); + if (status < 0) + break; + ocModeLop &= ~(EC_OC_REG_OC_MODE_LOP_PAR_ENA__M); + ocModeLop |= EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE; + ocModeLop |= 0x2; /* Magically-out-of-sync */ + status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, ocModeLop, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_COMM_INT_STA__A, 0x0, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_ACTIVE, 0); + if (status < 0) + break; + } while (0); + + return status; +} + +static int StartOC(struct drxd_state *state) +{ + int status = 0; + + do { + /* Stop OC */ + status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_HOLD, 0); + if (status < 0) + break; + + /* Restore output configuration */ + status = Write16(state, EC_OC_REG_SNC_ISC_LVL__A, state->m_EcOcRegSncSncLvl, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, state->m_EcOcRegOcModeLop, 0); + if (status < 0) + break; + + /* Output pins active again */ + status = Write16(state, EC_OC_REG_OCR_MPG_UOS__A, EC_OC_REG_OCR_MPG_UOS_INIT, 0); + if (status < 0) + break; + + /* Start OC */ + status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_ACTIVE, 0); + if (status < 0) + break; + } while (0); + return status; +} + +static int InitEQ(struct drxd_state *state) +{ + return WriteTable(state, state->m_InitEQ); +} + +static int InitEC(struct drxd_state *state) +{ + return WriteTable(state, state->m_InitEC); +} + +static int InitSC(struct drxd_state *state) +{ + return WriteTable(state, state->m_InitSC); +} + +static int InitAtomicRead(struct drxd_state *state) +{ + return WriteTable(state, state->m_InitAtomicRead); +} + +static int CorrectSysClockDeviation(struct drxd_state *state); + +static int DRX_GetLockStatus(struct drxd_state *state, u32 * pLockStatus) +{ + u16 ScRaRamLock = 0; + const u16 mpeg_lock_mask = (SC_RA_RAM_LOCK_MPEG__M | + SC_RA_RAM_LOCK_FEC__M | + SC_RA_RAM_LOCK_DEMOD__M); + const u16 fec_lock_mask = (SC_RA_RAM_LOCK_FEC__M | + SC_RA_RAM_LOCK_DEMOD__M); + const u16 demod_lock_mask = SC_RA_RAM_LOCK_DEMOD__M; + + int status; + + *pLockStatus = 0; + + status = Read16(state, SC_RA_RAM_LOCK__A, &ScRaRamLock, 0x0000); + if (status < 0) { + printk(KERN_ERR "Can't read SC_RA_RAM_LOCK__A status = %08x\n", status); + return status; + } + + if (state->drxd_state != DRXD_STARTED) + return 0; + + if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask) { + *pLockStatus |= DRX_LOCK_MPEG; + CorrectSysClockDeviation(state); + } + + if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask) + *pLockStatus |= DRX_LOCK_FEC; + + if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask) + *pLockStatus |= DRX_LOCK_DEMOD; + return 0; +} + +/****************************************************************************/ + +static int SetCfgIfAgc(struct drxd_state *state, struct SCfgAgc *cfg) +{ + int status; + + if (cfg->outputLevel > DRXD_FE_CTRL_MAX) + return -1; + + if (cfg->ctrlMode == AGC_CTRL_USER) { + do { + u16 FeAgRegPm1AgcWri; + u16 FeAgRegAgModeLop; + + status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &FeAgRegAgModeLop, 0); + if (status < 0) + break; + FeAgRegAgModeLop &= (~FE_AG_REG_AG_MODE_LOP_MODE_4__M); + FeAgRegAgModeLop |= FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC; + status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, FeAgRegAgModeLop, 0); + if (status < 0) + break; + + FeAgRegPm1AgcWri = (u16) (cfg->outputLevel & + FE_AG_REG_PM1_AGC_WRI__M); + status = Write16(state, FE_AG_REG_PM1_AGC_WRI__A, FeAgRegPm1AgcWri, 0); + if (status < 0) + break; + } while (0); + } else if (cfg->ctrlMode == AGC_CTRL_AUTO) { + if (((cfg->maxOutputLevel) < (cfg->minOutputLevel)) || + ((cfg->maxOutputLevel) > DRXD_FE_CTRL_MAX) || + ((cfg->speed) > DRXD_FE_CTRL_MAX) || + ((cfg->settleLevel) > DRXD_FE_CTRL_MAX) + ) + return -1; + do { + u16 FeAgRegAgModeLop; + u16 FeAgRegEgcSetLvl; + u16 slope, offset; + + /* == Mode == */ + + status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &FeAgRegAgModeLop, 0); + if (status < 0) + break; + FeAgRegAgModeLop &= (~FE_AG_REG_AG_MODE_LOP_MODE_4__M); + FeAgRegAgModeLop |= + FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC; + status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, FeAgRegAgModeLop, 0); + if (status < 0) + break; + + /* == Settle level == */ + + FeAgRegEgcSetLvl = (u16) ((cfg->settleLevel >> 1) & + FE_AG_REG_EGC_SET_LVL__M); + status = Write16(state, FE_AG_REG_EGC_SET_LVL__A, FeAgRegEgcSetLvl, 0); + if (status < 0) + break; + + /* == Min/Max == */ + + slope = (u16) ((cfg->maxOutputLevel - + cfg->minOutputLevel) / 2); + offset = (u16) ((cfg->maxOutputLevel + + cfg->minOutputLevel) / 2 - 511); + + status = Write16(state, FE_AG_REG_GC1_AGC_RIC__A, slope, 0); + if (status < 0) + break; + status = Write16(state, FE_AG_REG_GC1_AGC_OFF__A, offset, 0); + if (status < 0) + break; + + /* == Speed == */ + { + const u16 maxRur = 8; + static const u16 slowIncrDecLUT[] = { + 3, 4, 4, 5, 6 }; + static const u16 fastIncrDecLUT[] = { + 14, 15, 15, 16, + 17, 18, 18, 19, + 20, 21, 22, 23, + 24, 26, 27, 28, + 29, 31 + }; + + u16 fineSteps = (DRXD_FE_CTRL_MAX + 1) / + (maxRur + 1); + u16 fineSpeed = (u16) (cfg->speed - + ((cfg->speed / + fineSteps) * + fineSteps)); + u16 invRurCount = (u16) (cfg->speed / + fineSteps); + u16 rurCount; + if (invRurCount > maxRur) { + rurCount = 0; + fineSpeed += fineSteps; + } else { + rurCount = maxRur - invRurCount; + } + + /* + fastInc = default * + (2^(fineSpeed/fineSteps)) + => range[default...2*default> + slowInc = default * + (2^(fineSpeed/fineSteps)) + */ + { + u16 fastIncrDec = + fastIncrDecLUT[fineSpeed / + ((fineSteps / + (14 + 1)) + 1)]; + u16 slowIncrDec = + slowIncrDecLUT[fineSpeed / + (fineSteps / + (3 + 1))]; + + status = Write16(state, FE_AG_REG_EGC_RUR_CNT__A, rurCount, 0); + if (status < 0) + break; + status = Write16(state, FE_AG_REG_EGC_FAS_INC__A, fastIncrDec, 0); + if (status < 0) + break; + status = Write16(state, FE_AG_REG_EGC_FAS_DEC__A, fastIncrDec, 0); + if (status < 0) + break; + status = Write16(state, FE_AG_REG_EGC_SLO_INC__A, slowIncrDec, 0); + if (status < 0) + break; + status = Write16(state, FE_AG_REG_EGC_SLO_DEC__A, slowIncrDec, 0); + if (status < 0) + break; + } + } + } while (0); + + } else { + /* No OFF mode for IF control */ + return -1; + } + return status; +} + +static int SetCfgRfAgc(struct drxd_state *state, struct SCfgAgc *cfg) +{ + int status = 0; + + if (cfg->outputLevel > DRXD_FE_CTRL_MAX) + return -1; + + if (cfg->ctrlMode == AGC_CTRL_USER) { + do { + u16 AgModeLop = 0; + u16 level = (cfg->outputLevel); + + if (level == DRXD_FE_CTRL_MAX) + level++; + + status = Write16(state, FE_AG_REG_PM2_AGC_WRI__A, level, 0x0000); + if (status < 0) + break; + + /*==== Mode ====*/ + + /* Powerdown PD2, WRI source */ + state->m_FeAgRegAgPwd &= ~(FE_AG_REG_AG_PWD_PWD_PD2__M); + state->m_FeAgRegAgPwd |= + FE_AG_REG_AG_PWD_PWD_PD2_DISABLE; + status = Write16(state, FE_AG_REG_AG_PWD__A, state->m_FeAgRegAgPwd, 0x0000); + if (status < 0) + break; + + status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); + if (status < 0) + break; + AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | + FE_AG_REG_AG_MODE_LOP_MODE_E__M)); + AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | + FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC); + status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); + if (status < 0) + break; + + /* enable AGC2 pin */ + { + u16 FeAgRegAgAgcSio = 0; + status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + FeAgRegAgAgcSio &= + ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); + FeAgRegAgAgcSio |= + FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT; + status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + } + + } while (0); + } else if (cfg->ctrlMode == AGC_CTRL_AUTO) { + u16 AgModeLop = 0; + + do { + u16 level; + /* Automatic control */ + /* Powerup PD2, AGC2 as output, TGC source */ + (state->m_FeAgRegAgPwd) &= + ~(FE_AG_REG_AG_PWD_PWD_PD2__M); + (state->m_FeAgRegAgPwd) |= + FE_AG_REG_AG_PWD_PWD_PD2_DISABLE; + status = Write16(state, FE_AG_REG_AG_PWD__A, (state->m_FeAgRegAgPwd), 0x0000); + if (status < 0) + break; + + status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); + if (status < 0) + break; + AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | + FE_AG_REG_AG_MODE_LOP_MODE_E__M)); + AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | + FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC); + status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); + if (status < 0) + break; + /* Settle level */ + level = (((cfg->settleLevel) >> 4) & + FE_AG_REG_TGC_SET_LVL__M); + status = Write16(state, FE_AG_REG_TGC_SET_LVL__A, level, 0x0000); + if (status < 0) + break; + + /* Min/max: don't care */ + + /* Speed: TODO */ + + /* enable AGC2 pin */ + { + u16 FeAgRegAgAgcSio = 0; + status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + FeAgRegAgAgcSio &= + ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); + FeAgRegAgAgcSio |= + FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT; + status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + } + + } while (0); + } else { + u16 AgModeLop = 0; + + do { + /* No RF AGC control */ + /* Powerdown PD2, AGC2 as output, WRI source */ + (state->m_FeAgRegAgPwd) &= + ~(FE_AG_REG_AG_PWD_PWD_PD2__M); + (state->m_FeAgRegAgPwd) |= + FE_AG_REG_AG_PWD_PWD_PD2_ENABLE; + status = Write16(state, FE_AG_REG_AG_PWD__A, (state->m_FeAgRegAgPwd), 0x0000); + if (status < 0) + break; + + status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); + if (status < 0) + break; + AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | + FE_AG_REG_AG_MODE_LOP_MODE_E__M)); + AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | + FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC); + status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); + if (status < 0) + break; + + /* set FeAgRegAgAgcSio AGC2 (RF) as input */ + { + u16 FeAgRegAgAgcSio = 0; + status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + FeAgRegAgAgcSio &= + ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); + FeAgRegAgAgcSio |= + FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT; + status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + } + } while (0); + } + return status; +} + +static int ReadIFAgc(struct drxd_state *state, u32 * pValue) +{ + int status = 0; + + *pValue = 0; + if (state->if_agc_cfg.ctrlMode != AGC_CTRL_OFF) { + u16 Value; + status = Read16(state, FE_AG_REG_GC1_AGC_DAT__A, &Value, 0); + Value &= FE_AG_REG_GC1_AGC_DAT__M; + if (status >= 0) { + /* 3.3V + | + R1 + | + Vin - R3 - * -- Vout + | + R2 + | + GND + */ + u32 R1 = state->if_agc_cfg.R1; + u32 R2 = state->if_agc_cfg.R2; + u32 R3 = state->if_agc_cfg.R3; + + u32 Vmax, Rpar, Vmin, Vout; + + if (R2 == 0 && (R1 == 0 || R3 == 0)) + return 0; + + Vmax = (3300 * R2) / (R1 + R2); + Rpar = (R2 * R3) / (R3 + R2); + Vmin = (3300 * Rpar) / (R1 + Rpar); + Vout = Vmin + ((Vmax - Vmin) * Value) / 1024; + + *pValue = Vout; + } + } + return status; +} + +static int load_firmware(struct drxd_state *state, const char *fw_name) +{ + const struct firmware *fw; + + if (request_firmware(&fw, fw_name, state->dev) < 0) { + printk(KERN_ERR "drxd: firmware load failure [%s]\n", fw_name); + return -EIO; + } + + state->microcode = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (!state->microcode) { + release_firmware(fw); + return -ENOMEM; + } + + state->microcode_length = fw->size; + release_firmware(fw); + return 0; +} + +static int DownloadMicrocode(struct drxd_state *state, + const u8 *pMCImage, u32 Length) +{ + u8 *pSrc; + u32 Address; + u16 nBlocks; + u16 BlockSize; + int i, status = 0; + + pSrc = (u8 *) pMCImage; + /* We're not using Flags */ + /* Flags = (pSrc[0] << 8) | pSrc[1]; */ + pSrc += sizeof(u16); + nBlocks = (pSrc[0] << 8) | pSrc[1]; + pSrc += sizeof(u16); + + for (i = 0; i < nBlocks; i++) { + Address = (pSrc[0] << 24) | (pSrc[1] << 16) | + (pSrc[2] << 8) | pSrc[3]; + pSrc += sizeof(u32); + + BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16); + pSrc += sizeof(u16); + + /* We're not using Flags */ + /* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */ + pSrc += sizeof(u16); + + /* We're not using BlockCRC */ + /* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */ + pSrc += sizeof(u16); + + status = WriteBlock(state, Address, BlockSize, + pSrc, DRX_I2C_CLEARCRC); + if (status < 0) + break; + pSrc += BlockSize; + } + + return status; +} + +static int HI_Command(struct drxd_state *state, u16 cmd, u16 * pResult) +{ + u32 nrRetries = 0; + int status; + + status = Write16(state, HI_RA_RAM_SRV_CMD__A, cmd, 0); + if (status < 0) + return status; + + do { + nrRetries += 1; + if (nrRetries > DRXD_MAX_RETRIES) { + status = -1; + break; + } + status = Read16(state, HI_RA_RAM_SRV_CMD__A, NULL, 0); + } while (status != 0); + + if (status >= 0) + status = Read16(state, HI_RA_RAM_SRV_RES__A, pResult, 0); + return status; +} + +static int HI_CfgCommand(struct drxd_state *state) +{ + int status = 0; + + mutex_lock(&state->mutex); + Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, HI_RA_RAM_SRV_RST_KEY_ACT, 0); + Write16(state, HI_RA_RAM_SRV_CFG_DIV__A, state->hi_cfg_timing_div, 0); + Write16(state, HI_RA_RAM_SRV_CFG_BDL__A, state->hi_cfg_bridge_delay, 0); + Write16(state, HI_RA_RAM_SRV_CFG_WUP__A, state->hi_cfg_wakeup_key, 0); + Write16(state, HI_RA_RAM_SRV_CFG_ACT__A, state->hi_cfg_ctrl, 0); + + Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, HI_RA_RAM_SRV_RST_KEY_ACT, 0); + + if ((state->hi_cfg_ctrl & HI_RA_RAM_SRV_CFG_ACT_PWD_EXE) == + HI_RA_RAM_SRV_CFG_ACT_PWD_EXE) + status = Write16(state, HI_RA_RAM_SRV_CMD__A, + HI_RA_RAM_SRV_CMD_CONFIG, 0); + else + status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, NULL); + mutex_unlock(&state->mutex); + return status; +} + +static int InitHI(struct drxd_state *state) +{ + state->hi_cfg_wakeup_key = (state->chip_adr); + /* port/bridge/power down ctrl */ + state->hi_cfg_ctrl = HI_RA_RAM_SRV_CFG_ACT_SLV0_ON; + return HI_CfgCommand(state); +} + +static int HI_ResetCommand(struct drxd_state *state) +{ + int status; + + mutex_lock(&state->mutex); + status = Write16(state, HI_RA_RAM_SRV_RST_KEY__A, + HI_RA_RAM_SRV_RST_KEY_ACT, 0); + if (status == 0) + status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, NULL); + mutex_unlock(&state->mutex); + msleep(1); + return status; +} + +static int DRX_ConfigureI2CBridge(struct drxd_state *state, int bEnableBridge) +{ + state->hi_cfg_ctrl &= (~HI_RA_RAM_SRV_CFG_ACT_BRD__M); + if (bEnableBridge) + state->hi_cfg_ctrl |= HI_RA_RAM_SRV_CFG_ACT_BRD_ON; + else + state->hi_cfg_ctrl |= HI_RA_RAM_SRV_CFG_ACT_BRD_OFF; + + return HI_CfgCommand(state); +} + +#define HI_TR_WRITE 0x9 +#define HI_TR_READ 0xA +#define HI_TR_READ_WRITE 0xB +#define HI_TR_BROADCAST 0x4 + +#if 0 +static int AtomicReadBlock(struct drxd_state *state, + u32 Addr, u16 DataSize, u8 *pData, u8 Flags) +{ + int status; + int i = 0; + + /* Parameter check */ + if ((!pData) || ((DataSize & 1) != 0)) + return -1; + + mutex_lock(&state->mutex); + + do { + /* Instruct HI to read n bytes */ + /* TODO use proper names forthese egisters */ + status = Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, (HI_TR_FUNC_ADDR & 0xFFFF), 0); + if (status < 0) + break; + status = Write16(state, HI_RA_RAM_SRV_CFG_DIV__A, (u16) (Addr >> 16), 0); + if (status < 0) + break; + status = Write16(state, HI_RA_RAM_SRV_CFG_BDL__A, (u16) (Addr & 0xFFFF), 0); + if (status < 0) + break; + status = Write16(state, HI_RA_RAM_SRV_CFG_WUP__A, (u16) ((DataSize / 2) - 1), 0); + if (status < 0) + break; + status = Write16(state, HI_RA_RAM_SRV_CFG_ACT__A, HI_TR_READ, 0); + if (status < 0) + break; + + status = HI_Command(state, HI_RA_RAM_SRV_CMD_EXECUTE, 0); + if (status < 0) + break; + + } while (0); + + if (status >= 0) { + for (i = 0; i < (DataSize / 2); i += 1) { + u16 word; + + status = Read16(state, (HI_RA_RAM_USR_BEGIN__A + i), + &word, 0); + if (status < 0) + break; + pData[2 * i] = (u8) (word & 0xFF); + pData[(2 * i) + 1] = (u8) (word >> 8); + } + } + mutex_unlock(&state->mutex); + return status; +} + +static int AtomicReadReg32(struct drxd_state *state, + u32 Addr, u32 *pData, u8 Flags) +{ + u8 buf[sizeof(u32)]; + int status; + + if (!pData) + return -1; + status = AtomicReadBlock(state, Addr, sizeof(u32), buf, Flags); + *pData = (((u32) buf[0]) << 0) + + (((u32) buf[1]) << 8) + + (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24); + return status; +} +#endif + +static int StopAllProcessors(struct drxd_state *state) +{ + return Write16(state, HI_COMM_EXEC__A, + SC_COMM_EXEC_CTL_STOP, DRX_I2C_BROADCAST); +} + +static int EnableAndResetMB(struct drxd_state *state) +{ + if (state->type_A) { + /* disable? monitor bus observe @ EC_OC */ + Write16(state, EC_OC_REG_OC_MON_SIO__A, 0x0000, 0x0000); + } + + /* do inverse broadcast, followed by explicit write to HI */ + Write16(state, HI_COMM_MB__A, 0x0000, DRX_I2C_BROADCAST); + Write16(state, HI_COMM_MB__A, 0x0000, 0x0000); + return 0; +} + +static int InitCC(struct drxd_state *state) +{ + int status = 0; + + if (state->osc_clock_freq == 0 || + state->osc_clock_freq > 20000 || + (state->osc_clock_freq % 4000) != 0) { + printk(KERN_ERR "invalid osc frequency %d\n", state->osc_clock_freq); + return -1; + } + + status |= Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); + status |= Write16(state, CC_REG_PLL_MODE__A, + CC_REG_PLL_MODE_BYPASS_PLL | + CC_REG_PLL_MODE_PUMP_CUR_12, 0); + status |= Write16(state, CC_REG_REF_DIVIDE__A, + state->osc_clock_freq / 4000, 0); + status |= Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, + 0); + status |= Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); + + return status; +} + +static int ResetECOD(struct drxd_state *state) +{ + int status = 0; + + if (state->type_A) + status = Write16(state, EC_OD_REG_SYNC__A, 0x0664, 0); + else + status = Write16(state, B_EC_OD_REG_SYNC__A, 0x0664, 0); + + if (!(status < 0)) + status = WriteTable(state, state->m_ResetECRAM); + if (!(status < 0)) + status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0001, 0); + return status; +} + +/* Configure PGA switch */ + +static int SetCfgPga(struct drxd_state *state, int pgaSwitch) +{ + int status; + u16 AgModeLop = 0; + u16 AgModeHip = 0; + do { + if (pgaSwitch) { + /* PGA on */ + /* fine gain */ + status = Read16(state, B_FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); + if (status < 0) + break; + AgModeLop &= (~(B_FE_AG_REG_AG_MODE_LOP_MODE_C__M)); + AgModeLop |= B_FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC; + status = Write16(state, B_FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); + if (status < 0) + break; + + /* coarse gain */ + status = Read16(state, B_FE_AG_REG_AG_MODE_HIP__A, &AgModeHip, 0x0000); + if (status < 0) + break; + AgModeHip &= (~(B_FE_AG_REG_AG_MODE_HIP_MODE_J__M)); + AgModeHip |= B_FE_AG_REG_AG_MODE_HIP_MODE_J_DYNAMIC; + status = Write16(state, B_FE_AG_REG_AG_MODE_HIP__A, AgModeHip, 0x0000); + if (status < 0) + break; + + /* enable fine and coarse gain, enable AAF, + no ext resistor */ + status = Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, B_FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN, 0x0000); + if (status < 0) + break; + } else { + /* PGA off, bypass */ + + /* fine gain */ + status = Read16(state, B_FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); + if (status < 0) + break; + AgModeLop &= (~(B_FE_AG_REG_AG_MODE_LOP_MODE_C__M)); + AgModeLop |= B_FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC; + status = Write16(state, B_FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); + if (status < 0) + break; + + /* coarse gain */ + status = Read16(state, B_FE_AG_REG_AG_MODE_HIP__A, &AgModeHip, 0x0000); + if (status < 0) + break; + AgModeHip &= (~(B_FE_AG_REG_AG_MODE_HIP_MODE_J__M)); + AgModeHip |= B_FE_AG_REG_AG_MODE_HIP_MODE_J_STATIC; + status = Write16(state, B_FE_AG_REG_AG_MODE_HIP__A, AgModeHip, 0x0000); + if (status < 0) + break; + + /* disable fine and coarse gain, enable AAF, + no ext resistor */ + status = Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, 0x0000); + if (status < 0) + break; + } + } while (0); + return status; +} + +static int InitFE(struct drxd_state *state) +{ + int status; + + do { + status = WriteTable(state, state->m_InitFE_1); + if (status < 0) + break; + + if (state->type_A) { + status = Write16(state, FE_AG_REG_AG_PGA_MODE__A, + FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, + 0); + } else { + if (state->PGA) + status = SetCfgPga(state, 0); + else + status = + Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, + B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, + 0); + } + + if (status < 0) + break; + status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, state->m_FeAgRegAgAgcSio, 0x0000); + if (status < 0) + break; + status = Write16(state, FE_AG_REG_AG_PWD__A, state->m_FeAgRegAgPwd, 0x0000); + if (status < 0) + break; + + status = WriteTable(state, state->m_InitFE_2); + if (status < 0) + break; + + } while (0); + + return status; +} + +static int InitFT(struct drxd_state *state) +{ + /* + norm OFFSET, MB says =2 voor 8K en =3 voor 2K waarschijnlijk + SC stuff + */ + return Write16(state, FT_REG_COMM_EXEC__A, 0x0001, 0x0000); +} + +static int SC_WaitForReady(struct drxd_state *state) +{ + int i; + + for (i = 0; i < DRXD_MAX_RETRIES; i += 1) { + int status = Read16(state, SC_RA_RAM_CMD__A, NULL, 0); + if (status == 0) + return status; + } + return -1; +} + +static int SC_SendCommand(struct drxd_state *state, u16 cmd) +{ + int status = 0, ret; + u16 errCode; + + status = Write16(state, SC_RA_RAM_CMD__A, cmd, 0); + if (status < 0) + return status; + + SC_WaitForReady(state); + + ret = Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0); + + if (ret < 0 || errCode == 0xFFFF) { + printk(KERN_ERR "Command Error\n"); + status = -1; + } + + return status; +} + +static int SC_ProcStartCommand(struct drxd_state *state, + u16 subCmd, u16 param0, u16 param1) +{ + int ret, status = 0; + u16 scExec; + + mutex_lock(&state->mutex); + do { + ret = Read16(state, SC_COMM_EXEC__A, &scExec, 0); + if (ret < 0 || scExec != 1) { + status = -1; + break; + } + SC_WaitForReady(state); + status |= Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); + status |= Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); + status |= Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); + + SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START); + } while (0); + mutex_unlock(&state->mutex); + return status; +} + +static int SC_SetPrefParamCommand(struct drxd_state *state, + u16 subCmd, u16 param0, u16 param1) +{ + int status; + + mutex_lock(&state->mutex); + do { + status = SC_WaitForReady(state); + if (status < 0) + break; + status = Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); + if (status < 0) + break; + status = Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); + if (status < 0) + break; + status = Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); + if (status < 0) + break; + + status = SC_SendCommand(state, SC_RA_RAM_CMD_SET_PREF_PARAM); + if (status < 0) + break; + } while (0); + mutex_unlock(&state->mutex); + return status; +} + +#if 0 +static int SC_GetOpParamCommand(struct drxd_state *state, u16 * result) +{ + int status = 0; + + mutex_lock(&state->mutex); + do { + status = SC_WaitForReady(state); + if (status < 0) + break; + status = SC_SendCommand(state, SC_RA_RAM_CMD_GET_OP_PARAM); + if (status < 0) + break; + status = Read16(state, SC_RA_RAM_PARAM0__A, result, 0); + if (status < 0) + break; + } while (0); + mutex_unlock(&state->mutex); + return status; +} +#endif + +static int ConfigureMPEGOutput(struct drxd_state *state, int bEnableOutput) +{ + int status; + + do { + u16 EcOcRegIprInvMpg = 0; + u16 EcOcRegOcModeLop = 0; + u16 EcOcRegOcModeHip = 0; + u16 EcOcRegOcMpgSio = 0; + + /*CHK_ERROR(Read16(state, EC_OC_REG_OC_MODE_LOP__A, &EcOcRegOcModeLop, 0)); */ + + if (state->operation_mode == OM_DVBT_Diversity_Front) { + if (bEnableOutput) { + EcOcRegOcModeHip |= + B_EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR; + } else + EcOcRegOcMpgSio |= EC_OC_REG_OC_MPG_SIO__M; + EcOcRegOcModeLop |= + EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE; + } else { + EcOcRegOcModeLop = state->m_EcOcRegOcModeLop; + + if (bEnableOutput) + EcOcRegOcMpgSio &= (~(EC_OC_REG_OC_MPG_SIO__M)); + else + EcOcRegOcMpgSio |= EC_OC_REG_OC_MPG_SIO__M; + + /* Don't Insert RS Byte */ + if (state->insert_rs_byte) { + EcOcRegOcModeLop &= + (~(EC_OC_REG_OC_MODE_LOP_PAR_ENA__M)); + EcOcRegOcModeHip &= + (~EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M); + EcOcRegOcModeHip |= + EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE; + } else { + EcOcRegOcModeLop |= + EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE; + EcOcRegOcModeHip &= + (~EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M); + EcOcRegOcModeHip |= + EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE; + } + + /* Mode = Parallel */ + if (state->enable_parallel) + EcOcRegOcModeLop &= + (~(EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M)); + else + EcOcRegOcModeLop |= + EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL; + } + /* Invert Data */ + /* EcOcRegIprInvMpg |= 0x00FF; */ + EcOcRegIprInvMpg &= (~(0x00FF)); + + /* Invert Error ( we don't use the pin ) */ + /* EcOcRegIprInvMpg |= 0x0100; */ + EcOcRegIprInvMpg &= (~(0x0100)); + + /* Invert Start ( we don't use the pin ) */ + /* EcOcRegIprInvMpg |= 0x0200; */ + EcOcRegIprInvMpg &= (~(0x0200)); + + /* Invert Valid ( we don't use the pin ) */ + /* EcOcRegIprInvMpg |= 0x0400; */ + EcOcRegIprInvMpg &= (~(0x0400)); + + /* Invert Clock */ + /* EcOcRegIprInvMpg |= 0x0800; */ + EcOcRegIprInvMpg &= (~(0x0800)); + + /* EcOcRegOcModeLop =0x05; */ + status = Write16(state, EC_OC_REG_IPR_INV_MPG__A, EcOcRegIprInvMpg, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, EcOcRegOcModeLop, 0); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_OC_MODE_HIP__A, EcOcRegOcModeHip, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_OC_REG_OC_MPG_SIO__A, EcOcRegOcMpgSio, 0); + if (status < 0) + break; + } while (0); + return status; +} + +static int SetDeviceTypeId(struct drxd_state *state) +{ + int status = 0; + u16 deviceId = 0; + + do { + status = Read16(state, CC_REG_JTAGID_L__A, &deviceId, 0); + if (status < 0) + break; + /* TODO: why twice? */ + status = Read16(state, CC_REG_JTAGID_L__A, &deviceId, 0); + if (status < 0) + break; + printk(KERN_INFO "drxd: deviceId = %04x\n", deviceId); + + state->type_A = 0; + state->PGA = 0; + state->diversity = 0; + if (deviceId == 0) { /* on A2 only 3975 available */ + state->type_A = 1; + printk(KERN_INFO "DRX3975D-A2\n"); + } else { + deviceId >>= 12; + printk(KERN_INFO "DRX397%dD-B1\n", deviceId); + switch (deviceId) { + case 4: + state->diversity = 1; + fallthrough; + case 3: + case 7: + state->PGA = 1; + break; + case 6: + state->diversity = 1; + fallthrough; + case 5: + case 8: + break; + default: + status = -1; + break; + } + } + } while (0); + + if (status < 0) + return status; + + /* Init Table selection */ + state->m_InitAtomicRead = DRXD_InitAtomicRead; + state->m_InitSC = DRXD_InitSC; + state->m_ResetECRAM = DRXD_ResetECRAM; + if (state->type_A) { + state->m_ResetCEFR = DRXD_ResetCEFR; + state->m_InitFE_1 = DRXD_InitFEA2_1; + state->m_InitFE_2 = DRXD_InitFEA2_2; + state->m_InitCP = DRXD_InitCPA2; + state->m_InitCE = DRXD_InitCEA2; + state->m_InitEQ = DRXD_InitEQA2; + state->m_InitEC = DRXD_InitECA2; + if (load_firmware(state, DRX_FW_FILENAME_A2)) + return -EIO; + } else { + state->m_ResetCEFR = NULL; + state->m_InitFE_1 = DRXD_InitFEB1_1; + state->m_InitFE_2 = DRXD_InitFEB1_2; + state->m_InitCP = DRXD_InitCPB1; + state->m_InitCE = DRXD_InitCEB1; + state->m_InitEQ = DRXD_InitEQB1; + state->m_InitEC = DRXD_InitECB1; + if (load_firmware(state, DRX_FW_FILENAME_B1)) + return -EIO; + } + if (state->diversity) { + state->m_InitDiversityFront = DRXD_InitDiversityFront; + state->m_InitDiversityEnd = DRXD_InitDiversityEnd; + state->m_DisableDiversity = DRXD_DisableDiversity; + state->m_StartDiversityFront = DRXD_StartDiversityFront; + state->m_StartDiversityEnd = DRXD_StartDiversityEnd; + state->m_DiversityDelay8MHZ = DRXD_DiversityDelay8MHZ; + state->m_DiversityDelay6MHZ = DRXD_DiversityDelay6MHZ; + } else { + state->m_InitDiversityFront = NULL; + state->m_InitDiversityEnd = NULL; + state->m_DisableDiversity = NULL; + state->m_StartDiversityFront = NULL; + state->m_StartDiversityEnd = NULL; + state->m_DiversityDelay8MHZ = NULL; + state->m_DiversityDelay6MHZ = NULL; + } + + return status; +} + +static int CorrectSysClockDeviation(struct drxd_state *state) +{ + int status; + s32 incr = 0; + s32 nomincr = 0; + u32 bandwidth = 0; + u32 sysClockInHz = 0; + u32 sysClockFreq = 0; /* in kHz */ + s16 oscClockDeviation; + s16 Diff; + + do { + /* Retrieve bandwidth and incr, sanity check */ + + /* These accesses should be AtomicReadReg32, but that + causes trouble (at least for diversity */ + status = Read32(state, LC_RA_RAM_IFINCR_NOM_L__A, ((u32 *) &nomincr), 0); + if (status < 0) + break; + status = Read32(state, FE_IF_REG_INCR0__A, (u32 *) &incr, 0); + if (status < 0) + break; + + if (state->type_A) { + if ((nomincr - incr < -500) || (nomincr - incr > 500)) + break; + } else { + if ((nomincr - incr < -2000) || (nomincr - incr > 2000)) + break; + } + + switch (state->props.bandwidth_hz) { + case 8000000: + bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ; + break; + case 7000000: + bandwidth = DRXD_BANDWIDTH_7MHZ_IN_HZ; + break; + case 6000000: + bandwidth = DRXD_BANDWIDTH_6MHZ_IN_HZ; + break; + default: + return -1; + } + + /* Compute new sysclock value + sysClockFreq = (((incr + 2^23)*bandwidth)/2^21)/1000 */ + incr += (1 << 23); + sysClockInHz = MulDiv32(incr, bandwidth, 1 << 21); + sysClockFreq = (u32) (sysClockInHz / 1000); + /* rounding */ + if ((sysClockInHz % 1000) > 500) + sysClockFreq++; + + /* Compute clock deviation in ppm */ + oscClockDeviation = (u16) ((((s32) (sysClockFreq) - + (s32) + (state->expected_sys_clock_freq)) * + 1000000L) / + (s32) + (state->expected_sys_clock_freq)); + + Diff = oscClockDeviation - state->osc_clock_deviation; + /*printk(KERN_INFO "sysclockdiff=%d\n", Diff); */ + if (Diff >= -200 && Diff <= 200) { + state->sys_clock_freq = (u16) sysClockFreq; + if (oscClockDeviation != state->osc_clock_deviation) { + if (state->config.osc_deviation) { + state->config.osc_deviation(state->priv, + oscClockDeviation, + 1); + state->osc_clock_deviation = + oscClockDeviation; + } + } + /* switch OFF SRMM scan in SC */ + status = Write16(state, SC_RA_RAM_SAMPLE_RATE_COUNT__A, DRXD_OSCDEV_DONT_SCAN, 0); + if (status < 0) + break; + /* overrule FE_IF internal value for + proper re-locking */ + status = Write16(state, SC_RA_RAM_IF_SAVE__AX, state->current_fe_if_incr, 0); + if (status < 0) + break; + state->cscd_state = CSCD_SAVED; + } + } while (0); + + return status; +} + +static int DRX_Stop(struct drxd_state *state) +{ + int status; + + if (state->drxd_state != DRXD_STARTED) + return 0; + + do { + if (state->cscd_state != CSCD_SAVED) { + u32 lock; + status = DRX_GetLockStatus(state, &lock); + if (status < 0) + break; + } + + status = StopOC(state); + if (status < 0) + break; + + state->drxd_state = DRXD_STOPPED; + + status = ConfigureMPEGOutput(state, 0); + if (status < 0) + break; + + if (state->type_A) { + /* Stop relevant processors off the device */ + status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0000, 0x0000); + if (status < 0) + break; + + status = Write16(state, SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + } else { + /* Stop all processors except HI & CC & FE */ + status = Write16(state, B_SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, B_LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, B_FT_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, B_CP_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, B_CE_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, B_EQ_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0000, 0); + if (status < 0) + break; + } + + } while (0); + return status; +} + +#if 0 /* Currently unused */ +static int SetOperationMode(struct drxd_state *state, int oMode) +{ + int status; + + do { + if (state->drxd_state != DRXD_STOPPED) { + status = -1; + break; + } + + if (oMode == state->operation_mode) { + status = 0; + break; + } + + if (oMode != OM_Default && !state->diversity) { + status = -1; + break; + } + + switch (oMode) { + case OM_DVBT_Diversity_Front: + status = WriteTable(state, state->m_InitDiversityFront); + break; + case OM_DVBT_Diversity_End: + status = WriteTable(state, state->m_InitDiversityEnd); + break; + case OM_Default: + /* We need to check how to + get DRXD out of diversity */ + default: + status = WriteTable(state, state->m_DisableDiversity); + break; + } + } while (0); + + if (!status) + state->operation_mode = oMode; + return status; +} +#endif + +static int StartDiversity(struct drxd_state *state) +{ + int status = 0; + u16 rcControl; + + do { + if (state->operation_mode == OM_DVBT_Diversity_Front) { + status = WriteTable(state, state->m_StartDiversityFront); + if (status < 0) + break; + } else if (state->operation_mode == OM_DVBT_Diversity_End) { + status = WriteTable(state, state->m_StartDiversityEnd); + if (status < 0) + break; + if (state->props.bandwidth_hz == 8000000) { + status = WriteTable(state, state->m_DiversityDelay8MHZ); + if (status < 0) + break; + } else { + status = WriteTable(state, state->m_DiversityDelay6MHZ); + if (status < 0) + break; + } + + status = Read16(state, B_EQ_REG_RC_SEL_CAR__A, &rcControl, 0); + if (status < 0) + break; + rcControl &= ~(B_EQ_REG_RC_SEL_CAR_FFTMODE__M); + rcControl |= B_EQ_REG_RC_SEL_CAR_DIV_ON | + /* combining enabled */ + B_EQ_REG_RC_SEL_CAR_MEAS_A_CC | + B_EQ_REG_RC_SEL_CAR_PASS_A_CC | + B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC; + status = Write16(state, B_EQ_REG_RC_SEL_CAR__A, rcControl, 0); + if (status < 0) + break; + } + } while (0); + return status; +} + +static int SetFrequencyShift(struct drxd_state *state, + u32 offsetFreq, int channelMirrored) +{ + int negativeShift = (state->tuner_mirrors == channelMirrored); + + /* Handle all mirroring + * + * Note: ADC mirroring (aliasing) is implictly handled by limiting + * feFsRegAddInc to 28 bits below + * (if the result before masking is more than 28 bits, this means + * that the ADC is mirroring. + * The masking is in fact the aliasing of the ADC) + * + */ + + /* Compute register value, unsigned computation */ + state->fe_fs_add_incr = MulDiv32(state->intermediate_freq + + offsetFreq, + 1 << 28, state->sys_clock_freq); + /* Remove integer part */ + state->fe_fs_add_incr &= 0x0FFFFFFFL; + if (negativeShift) + state->fe_fs_add_incr = ((1 << 28) - state->fe_fs_add_incr); + + /* Save the frequency shift without tunerOffset compensation + for CtrlGetChannel. */ + state->org_fe_fs_add_incr = MulDiv32(state->intermediate_freq, + 1 << 28, state->sys_clock_freq); + /* Remove integer part */ + state->org_fe_fs_add_incr &= 0x0FFFFFFFL; + if (negativeShift) + state->org_fe_fs_add_incr = ((1L << 28) - + state->org_fe_fs_add_incr); + + return Write32(state, FE_FS_REG_ADD_INC_LOP__A, + state->fe_fs_add_incr, 0); +} + +static int SetCfgNoiseCalibration(struct drxd_state *state, + struct SNoiseCal *noiseCal) +{ + u16 beOptEna; + int status = 0; + + do { + status = Read16(state, SC_RA_RAM_BE_OPT_ENA__A, &beOptEna, 0); + if (status < 0) + break; + if (noiseCal->cpOpt) { + beOptEna |= (1 << SC_RA_RAM_BE_OPT_ENA_CP_OPT); + } else { + beOptEna &= ~(1 << SC_RA_RAM_BE_OPT_ENA_CP_OPT); + status = Write16(state, CP_REG_AC_NEXP_OFFS__A, noiseCal->cpNexpOfs, 0); + if (status < 0) + break; + } + status = Write16(state, SC_RA_RAM_BE_OPT_ENA__A, beOptEna, 0); + if (status < 0) + break; + + if (!state->type_A) { + status = Write16(state, B_SC_RA_RAM_CO_TD_CAL_2K__A, noiseCal->tdCal2k, 0); + if (status < 0) + break; + status = Write16(state, B_SC_RA_RAM_CO_TD_CAL_8K__A, noiseCal->tdCal8k, 0); + if (status < 0) + break; + } + } while (0); + + return status; +} + +static int DRX_Start(struct drxd_state *state, s32 off) +{ + struct dtv_frontend_properties *p = &state->props; + int status; + + u16 transmissionParams = 0; + u16 operationMode = 0; + u16 qpskTdTpsPwr = 0; + u16 qam16TdTpsPwr = 0; + u16 qam64TdTpsPwr = 0; + u32 feIfIncr = 0; + u32 bandwidth = 0; + int mirrorFreqSpect; + + u16 qpskSnCeGain = 0; + u16 qam16SnCeGain = 0; + u16 qam64SnCeGain = 0; + u16 qpskIsGainMan = 0; + u16 qam16IsGainMan = 0; + u16 qam64IsGainMan = 0; + u16 qpskIsGainExp = 0; + u16 qam16IsGainExp = 0; + u16 qam64IsGainExp = 0; + u16 bandwidthParam = 0; + + if (off < 0) + off = (off - 500) / 1000; + else + off = (off + 500) / 1000; + + do { + if (state->drxd_state != DRXD_STOPPED) + return -1; + status = ResetECOD(state); + if (status < 0) + break; + if (state->type_A) { + status = InitSC(state); + if (status < 0) + break; + } else { + status = InitFT(state); + if (status < 0) + break; + status = InitCP(state); + if (status < 0) + break; + status = InitCE(state); + if (status < 0) + break; + status = InitEQ(state); + if (status < 0) + break; + status = InitSC(state); + if (status < 0) + break; + } + + /* Restore current IF & RF AGC settings */ + + status = SetCfgIfAgc(state, &state->if_agc_cfg); + if (status < 0) + break; + status = SetCfgRfAgc(state, &state->rf_agc_cfg); + if (status < 0) + break; + + mirrorFreqSpect = (state->props.inversion == INVERSION_ON); + + switch (p->transmission_mode) { + default: /* Not set, detect it automatically */ + operationMode |= SC_RA_RAM_OP_AUTO_MODE__M; + fallthrough; /* try first guess DRX_FFTMODE_8K */ + case TRANSMISSION_MODE_8K: + transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K; + if (state->type_A) { + status = Write16(state, EC_SB_REG_TR_MODE__A, EC_SB_REG_TR_MODE_8K, 0x0000); + if (status < 0) + break; + qpskSnCeGain = 99; + qam16SnCeGain = 83; + qam64SnCeGain = 67; + } + break; + case TRANSMISSION_MODE_2K: + transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_2K; + if (state->type_A) { + status = Write16(state, EC_SB_REG_TR_MODE__A, EC_SB_REG_TR_MODE_2K, 0x0000); + if (status < 0) + break; + qpskSnCeGain = 97; + qam16SnCeGain = 71; + qam64SnCeGain = 65; + } + break; + } + + switch (p->guard_interval) { + case GUARD_INTERVAL_1_4: + transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_4; + break; + case GUARD_INTERVAL_1_8: + transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_8; + break; + case GUARD_INTERVAL_1_16: + transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_16; + break; + case GUARD_INTERVAL_1_32: + transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_32; + break; + default: /* Not set, detect it automatically */ + operationMode |= SC_RA_RAM_OP_AUTO_GUARD__M; + /* try first guess 1/4 */ + transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_4; + break; + } + + switch (p->hierarchy) { + case HIERARCHY_1: + transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A1; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0001, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_ALPHA__A, 0x0001, 0x0000); + if (status < 0) + break; + + qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; + qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA1; + qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA1; + + qpskIsGainMan = + SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; + qam16IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE; + qam64IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE; + + qpskIsGainExp = + SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; + qam16IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE; + qam64IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE; + } + break; + + case HIERARCHY_2: + transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A2; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0002, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_ALPHA__A, 0x0002, 0x0000); + if (status < 0) + break; + + qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; + qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA2; + qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA2; + + qpskIsGainMan = + SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; + qam16IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE; + qam64IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE; + + qpskIsGainExp = + SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; + qam16IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE; + qam64IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE; + } + break; + case HIERARCHY_4: + transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A4; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0003, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_ALPHA__A, 0x0003, 0x0000); + if (status < 0) + break; + + qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; + qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA4; + qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA4; + + qpskIsGainMan = + SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; + qam16IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE; + qam64IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE; + + qpskIsGainExp = + SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; + qam16IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE; + qam64IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE; + } + break; + case HIERARCHY_AUTO: + default: + /* Not set, detect it automatically, start with none */ + operationMode |= SC_RA_RAM_OP_AUTO_HIER__M; + transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_NO; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0000, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_ALPHA__A, 0x0000, 0x0000); + if (status < 0) + break; + + qpskTdTpsPwr = EQ_TD_TPS_PWR_QPSK; + qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHAN; + qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHAN; + + qpskIsGainMan = + SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE; + qam16IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE; + qam64IsGainMan = + SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE; + + qpskIsGainExp = + SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE; + qam16IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE; + qam64IsGainExp = + SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE; + } + break; + } + if (status < 0) + break; + + switch (p->modulation) { + default: + operationMode |= SC_RA_RAM_OP_AUTO_CONST__M; + fallthrough; /* try first guess DRX_CONSTELLATION_QAM64 */ + case QAM_64: + transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_CONST__A, 0x0002, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_64QAM, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0020, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0008, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0002, 0x0000); + if (status < 0) + break; + + status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qam64TdTpsPwr, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_SN_CEGAIN__A, qam64SnCeGain, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qam64IsGainMan, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qam64IsGainExp, 0x0000); + if (status < 0) + break; + } + break; + case QPSK: + transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QPSK; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_CONST__A, 0x0000, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_QPSK, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0010, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0000, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0000, 0x0000); + if (status < 0) + break; + + status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qpskTdTpsPwr, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_SN_CEGAIN__A, qpskSnCeGain, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qpskIsGainMan, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qpskIsGainExp, 0x0000); + if (status < 0) + break; + } + break; + + case QAM_16: + transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM16; + if (state->type_A) { + status = Write16(state, EQ_REG_OT_CONST__A, 0x0001, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_16QAM, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0010, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0004, 0x0000); + if (status < 0) + break; + status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0000, 0x0000); + if (status < 0) + break; + + status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qam16TdTpsPwr, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_SN_CEGAIN__A, qam16SnCeGain, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qam16IsGainMan, 0x0000); + if (status < 0) + break; + status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qam16IsGainExp, 0x0000); + if (status < 0) + break; + } + break; + + } + if (status < 0) + break; + + switch (DRX_CHANNEL_HIGH) { + default: + case DRX_CHANNEL_AUTO: + case DRX_CHANNEL_LOW: + transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_LO; + status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_LO, 0x0000); + break; + case DRX_CHANNEL_HIGH: + transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_HI; + status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_HI, 0x0000); + break; + } + + switch (p->code_rate_HP) { + case FEC_1_2: + transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_1_2; + if (state->type_A) + status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C1_2, 0x0000); + break; + default: + operationMode |= SC_RA_RAM_OP_AUTO_RATE__M; + fallthrough; + case FEC_2_3: + transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_2_3; + if (state->type_A) + status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C2_3, 0x0000); + break; + case FEC_3_4: + transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_3_4; + if (state->type_A) + status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C3_4, 0x0000); + break; + case FEC_5_6: + transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_5_6; + if (state->type_A) + status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C5_6, 0x0000); + break; + case FEC_7_8: + transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_7_8; + if (state->type_A) + status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C7_8, 0x0000); + break; + } + if (status < 0) + break; + + /* First determine real bandwidth (Hz) */ + /* Also set delay for impulse noise cruncher (only A2) */ + /* Also set parameters for EC_OC fix, note + EC_OC_REG_TMD_HIL_MAR is changed + by SC for fix for some 8K,1/8 guard but is restored by + InitEC and ResetEC + functions */ + switch (p->bandwidth_hz) { + case 0: + p->bandwidth_hz = 8000000; + fallthrough; + case 8000000: + /* (64/7)*(8/8)*1000000 */ + bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ; + + bandwidthParam = 0; + status = Write16(state, + FE_AG_REG_IND_DEL__A, 50, 0x0000); + break; + case 7000000: + /* (64/7)*(7/8)*1000000 */ + bandwidth = DRXD_BANDWIDTH_7MHZ_IN_HZ; + bandwidthParam = 0x4807; /*binary:0100 1000 0000 0111 */ + status = Write16(state, + FE_AG_REG_IND_DEL__A, 59, 0x0000); + break; + case 6000000: + /* (64/7)*(6/8)*1000000 */ + bandwidth = DRXD_BANDWIDTH_6MHZ_IN_HZ; + bandwidthParam = 0x0F07; /*binary: 0000 1111 0000 0111 */ + status = Write16(state, + FE_AG_REG_IND_DEL__A, 71, 0x0000); + break; + default: + status = -EINVAL; + } + if (status < 0) + break; + + status = Write16(state, SC_RA_RAM_BAND__A, bandwidthParam, 0x0000); + if (status < 0) + break; + + { + u16 sc_config; + status = Read16(state, SC_RA_RAM_CONFIG__A, &sc_config, 0); + if (status < 0) + break; + + /* enable SLAVE mode in 2k 1/32 to + prevent timing change glitches */ + if ((p->transmission_mode == TRANSMISSION_MODE_2K) && + (p->guard_interval == GUARD_INTERVAL_1_32)) { + /* enable slave */ + sc_config |= SC_RA_RAM_CONFIG_SLAVE__M; + } else { + /* disable slave */ + sc_config &= ~SC_RA_RAM_CONFIG_SLAVE__M; + } + status = Write16(state, SC_RA_RAM_CONFIG__A, sc_config, 0); + if (status < 0) + break; + } + + status = SetCfgNoiseCalibration(state, &state->noise_cal); + if (status < 0) + break; + + if (state->cscd_state == CSCD_INIT) { + /* switch on SRMM scan in SC */ + status = Write16(state, SC_RA_RAM_SAMPLE_RATE_COUNT__A, DRXD_OSCDEV_DO_SCAN, 0x0000); + if (status < 0) + break; +/* CHK_ERROR(Write16(SC_RA_RAM_SAMPLE_RATE_STEP__A, DRXD_OSCDEV_STEP, 0x0000));*/ + state->cscd_state = CSCD_SET; + } + + /* Now compute FE_IF_REG_INCR */ + /*((( SysFreq/BandWidth)/2)/2) -1) * 2^23) => + ((SysFreq / BandWidth) * (2^21) ) - (2^23) */ + feIfIncr = MulDiv32(state->sys_clock_freq * 1000, + (1ULL << 21), bandwidth) - (1 << 23); + status = Write16(state, FE_IF_REG_INCR0__A, (u16) (feIfIncr & FE_IF_REG_INCR0__M), 0x0000); + if (status < 0) + break; + status = Write16(state, FE_IF_REG_INCR1__A, (u16) ((feIfIncr >> FE_IF_REG_INCR0__W) & FE_IF_REG_INCR1__M), 0x0000); + if (status < 0) + break; + /* Bandwidth setting done */ + + /* Mirror & frequency offset */ + SetFrequencyShift(state, off, mirrorFreqSpect); + + /* Start SC, write channel settings to SC */ + + /* Enable SC after setting all other parameters */ + status = Write16(state, SC_COMM_STATE__A, 0, 0x0000); + if (status < 0) + break; + status = Write16(state, SC_COMM_EXEC__A, 1, 0x0000); + if (status < 0) + break; + + /* Write SC parameter registers, operation mode */ +#if 1 + operationMode = (SC_RA_RAM_OP_AUTO_MODE__M | + SC_RA_RAM_OP_AUTO_GUARD__M | + SC_RA_RAM_OP_AUTO_CONST__M | + SC_RA_RAM_OP_AUTO_HIER__M | + SC_RA_RAM_OP_AUTO_RATE__M); +#endif + status = SC_SetPrefParamCommand(state, 0x0000, transmissionParams, operationMode); + if (status < 0) + break; + + /* Start correct processes to get in lock */ + status = SC_ProcStartCommand(state, SC_RA_RAM_PROC_LOCKTRACK, SC_RA_RAM_SW_EVENT_RUN_NMASK__M, SC_RA_RAM_LOCKTRACK_MIN); + if (status < 0) + break; + + status = StartOC(state); + if (status < 0) + break; + + if (state->operation_mode != OM_Default) { + status = StartDiversity(state); + if (status < 0) + break; + } + + state->drxd_state = DRXD_STARTED; + } while (0); + + return status; +} + +static int CDRXD(struct drxd_state *state, u32 IntermediateFrequency) +{ + u32 ulRfAgcOutputLevel = 0xffffffff; + u32 ulRfAgcSettleLevel = 528; /* Optimum value for MT2060 */ + u32 ulRfAgcMinLevel = 0; /* Currently unused */ + u32 ulRfAgcMaxLevel = DRXD_FE_CTRL_MAX; /* Currently unused */ + u32 ulRfAgcSpeed = 0; /* Currently unused */ + u32 ulRfAgcMode = 0; /*2; Off */ + u32 ulRfAgcR1 = 820; + u32 ulRfAgcR2 = 2200; + u32 ulRfAgcR3 = 150; + u32 ulIfAgcMode = 0; /* Auto */ + u32 ulIfAgcOutputLevel = 0xffffffff; + u32 ulIfAgcSettleLevel = 0xffffffff; + u32 ulIfAgcMinLevel = 0xffffffff; + u32 ulIfAgcMaxLevel = 0xffffffff; + u32 ulIfAgcSpeed = 0xffffffff; + u32 ulIfAgcR1 = 820; + u32 ulIfAgcR2 = 2200; + u32 ulIfAgcR3 = 150; + u32 ulClock = state->config.clock; + u32 ulSerialMode = 0; + u32 ulEcOcRegOcModeLop = 4; /* Dynamic DTO source */ + u32 ulHiI2cDelay = HI_I2C_DELAY; + u32 ulHiI2cBridgeDelay = HI_I2C_BRIDGE_DELAY; + u32 ulHiI2cPatch = 0; + u32 ulEnvironment = APPENV_PORTABLE; + u32 ulEnvironmentDiversity = APPENV_MOBILE; + u32 ulIFFilter = IFFILTER_SAW; + + state->if_agc_cfg.ctrlMode = AGC_CTRL_AUTO; + state->if_agc_cfg.outputLevel = 0; + state->if_agc_cfg.settleLevel = 140; + state->if_agc_cfg.minOutputLevel = 0; + state->if_agc_cfg.maxOutputLevel = 1023; + state->if_agc_cfg.speed = 904; + + if (ulIfAgcMode == 1 && ulIfAgcOutputLevel <= DRXD_FE_CTRL_MAX) { + state->if_agc_cfg.ctrlMode = AGC_CTRL_USER; + state->if_agc_cfg.outputLevel = (u16) (ulIfAgcOutputLevel); + } + + if (ulIfAgcMode == 0 && + ulIfAgcSettleLevel <= DRXD_FE_CTRL_MAX && + ulIfAgcMinLevel <= DRXD_FE_CTRL_MAX && + ulIfAgcMaxLevel <= DRXD_FE_CTRL_MAX && + ulIfAgcSpeed <= DRXD_FE_CTRL_MAX) { + state->if_agc_cfg.ctrlMode = AGC_CTRL_AUTO; + state->if_agc_cfg.settleLevel = (u16) (ulIfAgcSettleLevel); + state->if_agc_cfg.minOutputLevel = (u16) (ulIfAgcMinLevel); + state->if_agc_cfg.maxOutputLevel = (u16) (ulIfAgcMaxLevel); + state->if_agc_cfg.speed = (u16) (ulIfAgcSpeed); + } + + state->if_agc_cfg.R1 = (u16) (ulIfAgcR1); + state->if_agc_cfg.R2 = (u16) (ulIfAgcR2); + state->if_agc_cfg.R3 = (u16) (ulIfAgcR3); + + state->rf_agc_cfg.R1 = (u16) (ulRfAgcR1); + state->rf_agc_cfg.R2 = (u16) (ulRfAgcR2); + state->rf_agc_cfg.R3 = (u16) (ulRfAgcR3); + + state->rf_agc_cfg.ctrlMode = AGC_CTRL_AUTO; + /* rest of the RFAgcCfg structure currently unused */ + if (ulRfAgcMode == 1 && ulRfAgcOutputLevel <= DRXD_FE_CTRL_MAX) { + state->rf_agc_cfg.ctrlMode = AGC_CTRL_USER; + state->rf_agc_cfg.outputLevel = (u16) (ulRfAgcOutputLevel); + } + + if (ulRfAgcMode == 0 && + ulRfAgcSettleLevel <= DRXD_FE_CTRL_MAX && + ulRfAgcMinLevel <= DRXD_FE_CTRL_MAX && + ulRfAgcMaxLevel <= DRXD_FE_CTRL_MAX && + ulRfAgcSpeed <= DRXD_FE_CTRL_MAX) { + state->rf_agc_cfg.ctrlMode = AGC_CTRL_AUTO; + state->rf_agc_cfg.settleLevel = (u16) (ulRfAgcSettleLevel); + state->rf_agc_cfg.minOutputLevel = (u16) (ulRfAgcMinLevel); + state->rf_agc_cfg.maxOutputLevel = (u16) (ulRfAgcMaxLevel); + state->rf_agc_cfg.speed = (u16) (ulRfAgcSpeed); + } + + if (ulRfAgcMode == 2) + state->rf_agc_cfg.ctrlMode = AGC_CTRL_OFF; + + if (ulEnvironment <= 2) + state->app_env_default = (enum app_env) + (ulEnvironment); + if (ulEnvironmentDiversity <= 2) + state->app_env_diversity = (enum app_env) + (ulEnvironmentDiversity); + + if (ulIFFilter == IFFILTER_DISCRETE) { + /* discrete filter */ + state->noise_cal.cpOpt = 0; + state->noise_cal.cpNexpOfs = 40; + state->noise_cal.tdCal2k = -40; + state->noise_cal.tdCal8k = -24; + } else { + /* SAW filter */ + state->noise_cal.cpOpt = 1; + state->noise_cal.cpNexpOfs = 0; + state->noise_cal.tdCal2k = -21; + state->noise_cal.tdCal8k = -24; + } + state->m_EcOcRegOcModeLop = (u16) (ulEcOcRegOcModeLop); + + state->chip_adr = (state->config.demod_address << 1) | 1; + switch (ulHiI2cPatch) { + case 1: + state->m_HiI2cPatch = DRXD_HiI2cPatch_1; + break; + case 3: + state->m_HiI2cPatch = DRXD_HiI2cPatch_3; + break; + default: + state->m_HiI2cPatch = NULL; + } + + /* modify tuner and clock attributes */ + state->intermediate_freq = (u16) (IntermediateFrequency / 1000); + /* expected system clock frequency in kHz */ + state->expected_sys_clock_freq = 48000; + /* real system clock frequency in kHz */ + state->sys_clock_freq = 48000; + state->osc_clock_freq = (u16) ulClock; + state->osc_clock_deviation = 0; + state->cscd_state = CSCD_INIT; + state->drxd_state = DRXD_UNINITIALIZED; + + state->PGA = 0; + state->type_A = 0; + state->tuner_mirrors = 0; + + /* modify MPEG output attributes */ + state->insert_rs_byte = state->config.insert_rs_byte; + state->enable_parallel = (ulSerialMode != 1); + + /* Timing div, 250ns/Psys */ + /* Timing div, = ( delay (nano seconds) * sysclk (kHz) )/ 1000 */ + + state->hi_cfg_timing_div = (u16) ((state->sys_clock_freq / 1000) * + ulHiI2cDelay) / 1000; + /* Bridge delay, uses oscilator clock */ + /* Delay = ( delay (nano seconds) * oscclk (kHz) )/ 1000 */ + state->hi_cfg_bridge_delay = (u16) ((state->osc_clock_freq / 1000) * + ulHiI2cBridgeDelay) / 1000; + + state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_CONSUMER; + /* state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_PRO; */ + state->m_FeAgRegAgAgcSio = DRXD_DEF_AG_AGC_SIO; + return 0; +} + +static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size) +{ + int status = 0; + u32 driverVersion; + + if (state->init_done) + return 0; + + CDRXD(state, state->config.IF ? state->config.IF : 36000000); + + do { + state->operation_mode = OM_Default; + + status = SetDeviceTypeId(state); + if (status < 0) + break; + + /* Apply I2c address patch to B1 */ + if (!state->type_A && state->m_HiI2cPatch) { + status = WriteTable(state, state->m_HiI2cPatch); + if (status < 0) + break; + } + + if (state->type_A) { + /* HI firmware patch for UIO readout, + avoid clearing of result register */ + status = Write16(state, 0x43012D, 0x047f, 0); + if (status < 0) + break; + } + + status = HI_ResetCommand(state); + if (status < 0) + break; + + status = StopAllProcessors(state); + if (status < 0) + break; + status = InitCC(state); + if (status < 0) + break; + + state->osc_clock_deviation = 0; + + if (state->config.osc_deviation) + state->osc_clock_deviation = + state->config.osc_deviation(state->priv, 0, 0); + { + /* Handle clock deviation */ + s32 devB; + s32 devA = (s32) (state->osc_clock_deviation) * + (s32) (state->expected_sys_clock_freq); + /* deviation in kHz */ + s32 deviation = (devA / (1000000L)); + /* rounding, signed */ + if (devA > 0) + devB = (2); + else + devB = (-2); + if ((devB * (devA % 1000000L) > 1000000L)) { + /* add +1 or -1 */ + deviation += (devB / 2); + } + + state->sys_clock_freq = + (u16) ((state->expected_sys_clock_freq) + + deviation); + } + status = InitHI(state); + if (status < 0) + break; + status = InitAtomicRead(state); + if (status < 0) + break; + + status = EnableAndResetMB(state); + if (status < 0) + break; + if (state->type_A) { + status = ResetCEFR(state); + if (status < 0) + break; + } + if (fw) { + status = DownloadMicrocode(state, fw, fw_size); + if (status < 0) + break; + } else { + status = DownloadMicrocode(state, state->microcode, state->microcode_length); + if (status < 0) + break; + } + + if (state->PGA) { + state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_PRO; + SetCfgPga(state, 0); /* PGA = 0 dB */ + } else { + state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_CONSUMER; + } + + state->m_FeAgRegAgAgcSio = DRXD_DEF_AG_AGC_SIO; + + status = InitFE(state); + if (status < 0) + break; + status = InitFT(state); + if (status < 0) + break; + status = InitCP(state); + if (status < 0) + break; + status = InitCE(state); + if (status < 0) + break; + status = InitEQ(state); + if (status < 0) + break; + status = InitEC(state); + if (status < 0) + break; + status = InitSC(state); + if (status < 0) + break; + + status = SetCfgIfAgc(state, &state->if_agc_cfg); + if (status < 0) + break; + status = SetCfgRfAgc(state, &state->rf_agc_cfg); + if (status < 0) + break; + + state->cscd_state = CSCD_INIT; + status = Write16(state, SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + status = Write16(state, LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); + if (status < 0) + break; + + driverVersion = (((VERSION_MAJOR / 10) << 4) + + (VERSION_MAJOR % 10)) << 24; + driverVersion += (((VERSION_MINOR / 10) << 4) + + (VERSION_MINOR % 10)) << 16; + driverVersion += ((VERSION_PATCH / 1000) << 12) + + ((VERSION_PATCH / 100) << 8) + + ((VERSION_PATCH / 10) << 4) + (VERSION_PATCH % 10); + + status = Write32(state, SC_RA_RAM_DRIVER_VERSION__AX, driverVersion, 0); + if (status < 0) + break; + + status = StopOC(state); + if (status < 0) + break; + + state->drxd_state = DRXD_STOPPED; + state->init_done = 1; + status = 0; + } while (0); + return status; +} + +static int DRXD_status(struct drxd_state *state, u32 *pLockStatus) +{ + DRX_GetLockStatus(state, pLockStatus); + + /*if (*pLockStatus&DRX_LOCK_MPEG) */ + if (*pLockStatus & DRX_LOCK_FEC) { + ConfigureMPEGOutput(state, 1); + /* Get status again, in case we have MPEG lock now */ + /*DRX_GetLockStatus(state, pLockStatus); */ + } + + return 0; +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static int drxd_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct drxd_state *state = fe->demodulator_priv; + u32 value; + int res; + + res = ReadIFAgc(state, &value); + if (res < 0) + *strength = 0; + else + *strength = 0xffff - (value << 4); + return 0; +} + +static int drxd_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct drxd_state *state = fe->demodulator_priv; + u32 lock; + + DRXD_status(state, &lock); + *status = 0; + /* No MPEG lock in V255 firmware, bug ? */ +#if 1 + if (lock & DRX_LOCK_MPEG) + *status |= FE_HAS_LOCK; +#else + if (lock & DRX_LOCK_FEC) + *status |= FE_HAS_LOCK; +#endif + if (lock & DRX_LOCK_FEC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC; + if (lock & DRX_LOCK_DEMOD) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + + return 0; +} + +static int drxd_init(struct dvb_frontend *fe) +{ + struct drxd_state *state = fe->demodulator_priv; + + return DRXD_init(state, NULL, 0); +} + +static int drxd_config_i2c(struct dvb_frontend *fe, int onoff) +{ + struct drxd_state *state = fe->demodulator_priv; + + if (state->config.disable_i2c_gate_ctrl == 1) + return 0; + + return DRX_ConfigureI2CBridge(state, onoff); +} + +static int drxd_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *sets) +{ + sets->min_delay_ms = 10000; + sets->max_drift = 0; + sets->step_size = 0; + return 0; +} + +static int drxd_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + *ber = 0; + return 0; +} + +static int drxd_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + *snr = 0; + return 0; +} + +static int drxd_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int drxd_sleep(struct dvb_frontend *fe) +{ + struct drxd_state *state = fe->demodulator_priv; + + ConfigureMPEGOutput(state, 0); + return 0; +} + +static int drxd_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + return drxd_config_i2c(fe, enable); +} + +static int drxd_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct drxd_state *state = fe->demodulator_priv; + s32 off = 0; + + state->props = *p; + DRX_Stop(state); + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + msleep(200); + + return DRX_Start(state, off); +} + +static void drxd_release(struct dvb_frontend *fe) +{ + struct drxd_state *state = fe->demodulator_priv; + + kfree(state); +} + +static const struct dvb_frontend_ops drxd_ops = { + .delsys = { SYS_DVBT}, + .info = { + .name = "Micronas DRXD DVB-T", + .frequency_min_hz = 47125 * kHz, + .frequency_max_hz = 855250 * kHz, + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS}, + + .release = drxd_release, + .init = drxd_init, + .sleep = drxd_sleep, + .i2c_gate_ctrl = drxd_i2c_gate_ctrl, + + .set_frontend = drxd_set_frontend, + .get_tune_settings = drxd_get_tune_settings, + + .read_status = drxd_read_status, + .read_ber = drxd_read_ber, + .read_signal_strength = drxd_read_signal_strength, + .read_snr = drxd_read_snr, + .read_ucblocks = drxd_read_ucblocks, +}; + +struct dvb_frontend *drxd_attach(const struct drxd_config *config, + void *priv, struct i2c_adapter *i2c, + struct device *dev) +{ + struct drxd_state *state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + state->ops = drxd_ops; + state->dev = dev; + state->config = *config; + state->i2c = i2c; + state->priv = priv; + + mutex_init(&state->mutex); + + if (Read16(state, 0, NULL, 0) < 0) + goto error; + + state->frontend.ops = drxd_ops; + state->frontend.demodulator_priv = state; + ConfigureMPEGOutput(state, 0); + /* add few initialization to allow gate control */ + CDRXD(state, state->config.IF ? state->config.IF : 36000000); + InitHI(state); + + return &state->frontend; + +error: + printk(KERN_ERR "drxd: not found\n"); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(drxd_attach); + +MODULE_DESCRIPTION("DRXD driver"); +MODULE_AUTHOR("Micronas"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/drxd_map_firm.h b/drivers/media/dvb-frontends/drxd_map_firm.h new file mode 100644 index 0000000000..bdcc63576d --- /dev/null +++ b/drivers/media/dvb-frontends/drxd_map_firm.h @@ -0,0 +1,997 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drx3973d_map_firm.h + * + * Copyright (C) 2006-2007 Micronas + */ + +#ifndef __DRX3973D_MAP__H__ +#define __DRX3973D_MAP__H__ + +/* + * Note: originally, this file contained 12000+ lines of data + * Probably a few lines for every firwmare assembler instruction. However, + * only a few defines were actually used. So, removed all uneeded lines. + * If ever needed, the other lines can be easily obtained via git history. + */ + +#define HI_COMM_EXEC__A 0x400000 +#define HI_COMM_MB__A 0x400002 +#define HI_CT_REG_COMM_STATE__A 0x410001 +#define HI_RA_RAM_SRV_RES__A 0x420031 +#define HI_RA_RAM_SRV_CMD__A 0x420032 +#define HI_RA_RAM_SRV_CMD_RESET 0x2 +#define HI_RA_RAM_SRV_CMD_CONFIG 0x3 +#define HI_RA_RAM_SRV_CMD_EXECUTE 0x6 +#define HI_RA_RAM_SRV_RST_KEY__A 0x420033 +#define HI_RA_RAM_SRV_RST_KEY_ACT 0x3973 +#define HI_RA_RAM_SRV_CFG_KEY__A 0x420033 +#define HI_RA_RAM_SRV_CFG_DIV__A 0x420034 +#define HI_RA_RAM_SRV_CFG_BDL__A 0x420035 +#define HI_RA_RAM_SRV_CFG_WUP__A 0x420036 +#define HI_RA_RAM_SRV_CFG_ACT__A 0x420037 +#define HI_RA_RAM_SRV_CFG_ACT_SLV0_ON 0x1 +#define HI_RA_RAM_SRV_CFG_ACT_BRD__M 0x4 +#define HI_RA_RAM_SRV_CFG_ACT_BRD_OFF 0x0 +#define HI_RA_RAM_SRV_CFG_ACT_BRD_ON 0x4 +#define HI_RA_RAM_SRV_CFG_ACT_PWD_EXE 0x8 +#define HI_RA_RAM_USR_BEGIN__A 0x420040 +#define HI_IF_RAM_TRP_BPT0__AX 0x430000 +#define HI_IF_RAM_USR_BEGIN__A 0x430200 +#define SC_COMM_EXEC__A 0x800000 +#define SC_COMM_EXEC_CTL_STOP 0x0 +#define SC_COMM_STATE__A 0x800001 +#define SC_RA_RAM_PARAM0__A 0x820040 +#define SC_RA_RAM_PARAM1__A 0x820041 +#define SC_RA_RAM_CMD_ADDR__A 0x820042 +#define SC_RA_RAM_CMD__A 0x820043 +#define SC_RA_RAM_CMD_PROC_START 0x1 +#define SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 +#define SC_RA_RAM_CMD_GET_OP_PARAM 0x5 +#define SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 +#define SC_RA_RAM_LOCKTRACK_MIN 0x1 +#define SC_RA_RAM_OP_PARAM_MODE_2K 0x0 +#define SC_RA_RAM_OP_PARAM_MODE_8K 0x1 +#define SC_RA_RAM_OP_PARAM_GUARD_32 0x0 +#define SC_RA_RAM_OP_PARAM_GUARD_16 0x4 +#define SC_RA_RAM_OP_PARAM_GUARD_8 0x8 +#define SC_RA_RAM_OP_PARAM_GUARD_4 0xC +#define SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 +#define SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 +#define SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 +#define SC_RA_RAM_OP_PARAM_HIER_NO 0x0 +#define SC_RA_RAM_OP_PARAM_HIER_A1 0x40 +#define SC_RA_RAM_OP_PARAM_HIER_A2 0x80 +#define SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 +#define SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 +#define SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 +#define SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 +#define SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 +#define SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 +#define SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 +#define SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 +#define SC_RA_RAM_OP_AUTO_MODE__M 0x1 +#define SC_RA_RAM_OP_AUTO_GUARD__M 0x2 +#define SC_RA_RAM_OP_AUTO_CONST__M 0x4 +#define SC_RA_RAM_OP_AUTO_HIER__M 0x8 +#define SC_RA_RAM_OP_AUTO_RATE__M 0x10 +#define SC_RA_RAM_LOCK__A 0x82004B +#define SC_RA_RAM_LOCK_DEMOD__M 0x1 +#define SC_RA_RAM_LOCK_FEC__M 0x2 +#define SC_RA_RAM_LOCK_MPEG__M 0x4 +#define SC_RA_RAM_BE_OPT_ENA__A 0x82004C +#define SC_RA_RAM_BE_OPT_ENA_CP_OPT 0x1 +#define SC_RA_RAM_BE_OPT_DELAY__A 0x82004D +#define SC_RA_RAM_CONFIG__A 0x820050 +#define SC_RA_RAM_CONFIG_FR_ENABLE__M 0x4 +#define SC_RA_RAM_CONFIG_FREQSCAN__M 0x10 +#define SC_RA_RAM_CONFIG_SLAVE__M 0x20 +#define SC_RA_RAM_IF_SAVE__AX 0x82008E +#define SC_RA_RAM_IR_COARSE_2K_LENGTH__A 0x8200D1 +#define SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE 0x9 +#define SC_RA_RAM_IR_COARSE_2K_FREQINC__A 0x8200D2 +#define SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE 0x4 +#define SC_RA_RAM_IR_COARSE_2K_KAISINC__A 0x8200D3 +#define SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE 0x100 +#define SC_RA_RAM_IR_COARSE_8K_LENGTH__A 0x8200D4 +#define SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE 0x8 +#define SC_RA_RAM_IR_COARSE_8K_FREQINC__A 0x8200D5 +#define SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE 0x8 +#define SC_RA_RAM_IR_COARSE_8K_KAISINC__A 0x8200D6 +#define SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE 0x200 +#define SC_RA_RAM_IR_FINE_2K_LENGTH__A 0x8200D7 +#define SC_RA_RAM_IR_FINE_2K_LENGTH__PRE 0x9 +#define SC_RA_RAM_IR_FINE_2K_FREQINC__A 0x8200D8 +#define SC_RA_RAM_IR_FINE_2K_FREQINC__PRE 0x4 +#define SC_RA_RAM_IR_FINE_2K_KAISINC__A 0x8200D9 +#define SC_RA_RAM_IR_FINE_2K_KAISINC__PRE 0x100 +#define SC_RA_RAM_IR_FINE_8K_LENGTH__A 0x8200DA +#define SC_RA_RAM_IR_FINE_8K_LENGTH__PRE 0xB +#define SC_RA_RAM_IR_FINE_8K_FREQINC__A 0x8200DB +#define SC_RA_RAM_IR_FINE_8K_FREQINC__PRE 0x1 +#define SC_RA_RAM_IR_FINE_8K_KAISINC__A 0x8200DC +#define SC_RA_RAM_IR_FINE_8K_KAISINC__PRE 0x40 +#define SC_RA_RAM_ECHO_SHIFT_LIM__A 0x8200DD +#define SC_RA_RAM_SAMPLE_RATE_COUNT__A 0x8200E8 +#define SC_RA_RAM_SAMPLE_RATE_STEP__A 0x8200E9 +#define SC_RA_RAM_BAND__A 0x8200EC +#define SC_RA_RAM_LC_ABS_2K__A 0x8200F4 +#define SC_RA_RAM_LC_ABS_2K__PRE 0x1F +#define SC_RA_RAM_LC_ABS_8K__A 0x8200F5 +#define SC_RA_RAM_LC_ABS_8K__PRE 0x1F +#define SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE 0x1D6 +#define SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE 0x4 +#define SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE 0x1BB +#define SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE 0x5 +#define SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE 0x1EF +#define SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE 0x5 +#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE 0x15E +#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE 0x5 +#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE 0x11A +#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE 0x6 +#define SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE 0x1FB +#define SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE 0x5 +#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE 0x12F +#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE 0x5 +#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE 0x197 +#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE 0x5 +#define SC_RA_RAM_DRIVER_VERSION__AX 0x8201FE +#define SC_RA_RAM_PROC_LOCKTRACK 0x0 +#define FE_COMM_EXEC__A 0xC00000 +#define FE_AD_REG_COMM_EXEC__A 0xC10000 +#define FE_AD_REG_FDB_IN__A 0xC10012 +#define FE_AD_REG_PD__A 0xC10013 +#define FE_AD_REG_INVEXT__A 0xC10014 +#define FE_AD_REG_CLKNEG__A 0xC10015 +#define FE_AG_REG_COMM_EXEC__A 0xC20000 +#define FE_AG_REG_AG_MODE_LOP__A 0xC20010 +#define FE_AG_REG_AG_MODE_LOP_MODE_4__M 0x10 +#define FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC 0x0 +#define FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC 0x10 +#define FE_AG_REG_AG_MODE_LOP_MODE_5__M 0x20 +#define FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC 0x0 +#define FE_AG_REG_AG_MODE_LOP_MODE_C__M 0x1000 +#define FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC 0x0 +#define FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC 0x1000 +#define FE_AG_REG_AG_MODE_LOP_MODE_E__M 0x4000 +#define FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC 0x0 +#define FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC 0x4000 +#define FE_AG_REG_AG_MODE_HIP__A 0xC20011 +#define FE_AG_REG_AG_PGA_MODE__A 0xC20012 +#define FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN 0x0 +#define FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN 0x1 +#define FE_AG_REG_AG_AGC_SIO__A 0xC20013 +#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M 0x2 +#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT 0x0 +#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT 0x2 +#define FE_AG_REG_AG_PWD__A 0xC20015 +#define FE_AG_REG_AG_PWD_PWD_PD2__M 0x2 +#define FE_AG_REG_AG_PWD_PWD_PD2_DISABLE 0x0 +#define FE_AG_REG_AG_PWD_PWD_PD2_ENABLE 0x2 +#define FE_AG_REG_DCE_AUR_CNT__A 0xC20016 +#define FE_AG_REG_DCE_RUR_CNT__A 0xC20017 +#define FE_AG_REG_ACE_AUR_CNT__A 0xC2001A +#define FE_AG_REG_ACE_RUR_CNT__A 0xC2001B +#define FE_AG_REG_CDR_RUR_CNT__A 0xC20020 +#define FE_AG_REG_EGC_RUR_CNT__A 0xC20024 +#define FE_AG_REG_EGC_SET_LVL__A 0xC20025 +#define FE_AG_REG_EGC_SET_LVL__M 0x1FF +#define FE_AG_REG_EGC_FLA_RGN__A 0xC20026 +#define FE_AG_REG_EGC_SLO_RGN__A 0xC20027 +#define FE_AG_REG_EGC_JMP_PSN__A 0xC20028 +#define FE_AG_REG_EGC_FLA_INC__A 0xC20029 +#define FE_AG_REG_EGC_FLA_DEC__A 0xC2002A +#define FE_AG_REG_EGC_SLO_INC__A 0xC2002B +#define FE_AG_REG_EGC_SLO_DEC__A 0xC2002C +#define FE_AG_REG_EGC_FAS_INC__A 0xC2002D +#define FE_AG_REG_EGC_FAS_DEC__A 0xC2002E +#define FE_AG_REG_PM1_AGC_WRI__A 0xC20030 +#define FE_AG_REG_PM1_AGC_WRI__M 0x7FF +#define FE_AG_REG_GC1_AGC_RIC__A 0xC20031 +#define FE_AG_REG_GC1_AGC_OFF__A 0xC20032 +#define FE_AG_REG_GC1_AGC_MAX__A 0xC20033 +#define FE_AG_REG_GC1_AGC_MIN__A 0xC20034 +#define FE_AG_REG_GC1_AGC_DAT__A 0xC20035 +#define FE_AG_REG_GC1_AGC_DAT__M 0x3FF +#define FE_AG_REG_PM2_AGC_WRI__A 0xC20036 +#define FE_AG_REG_IND_WIN__A 0xC2003C +#define FE_AG_REG_IND_THD_LOL__A 0xC2003D +#define FE_AG_REG_IND_THD_HIL__A 0xC2003E +#define FE_AG_REG_IND_DEL__A 0xC2003F +#define FE_AG_REG_IND_PD1_WRI__A 0xC20040 +#define FE_AG_REG_PDA_AUR_CNT__A 0xC20041 +#define FE_AG_REG_PDA_RUR_CNT__A 0xC20042 +#define FE_AG_REG_PDA_AVE_DAT__A 0xC20043 +#define FE_AG_REG_PDC_RUR_CNT__A 0xC20044 +#define FE_AG_REG_PDC_SET_LVL__A 0xC20045 +#define FE_AG_REG_PDC_FLA_RGN__A 0xC20046 +#define FE_AG_REG_PDC_JMP_PSN__A 0xC20047 +#define FE_AG_REG_PDC_FLA_STP__A 0xC20048 +#define FE_AG_REG_PDC_SLO_STP__A 0xC20049 +#define FE_AG_REG_PDC_PD2_WRI__A 0xC2004A +#define FE_AG_REG_PDC_MAP_DAT__A 0xC2004B +#define FE_AG_REG_PDC_MAX__A 0xC2004C +#define FE_AG_REG_TGA_AUR_CNT__A 0xC2004D +#define FE_AG_REG_TGA_RUR_CNT__A 0xC2004E +#define FE_AG_REG_TGA_AVE_DAT__A 0xC2004F +#define FE_AG_REG_TGC_RUR_CNT__A 0xC20050 +#define FE_AG_REG_TGC_SET_LVL__A 0xC20051 +#define FE_AG_REG_TGC_SET_LVL__M 0x3F +#define FE_AG_REG_TGC_FLA_RGN__A 0xC20052 +#define FE_AG_REG_TGC_JMP_PSN__A 0xC20053 +#define FE_AG_REG_TGC_FLA_STP__A 0xC20054 +#define FE_AG_REG_TGC_SLO_STP__A 0xC20055 +#define FE_AG_REG_TGC_MAP_DAT__A 0xC20056 +#define FE_AG_REG_FGA_AUR_CNT__A 0xC20057 +#define FE_AG_REG_FGA_RUR_CNT__A 0xC20058 +#define FE_AG_REG_FGM_WRI__A 0xC20061 +#define FE_AG_REG_BGC_FGC_WRI__A 0xC20068 +#define FE_AG_REG_BGC_CGC_WRI__A 0xC20069 +#define FE_FS_REG_COMM_EXEC__A 0xC30000 +#define FE_FS_REG_ADD_INC_LOP__A 0xC30010 +#define FE_FD_REG_COMM_EXEC__A 0xC40000 +#define FE_FD_REG_SCL__A 0xC40010 +#define FE_FD_REG_MAX_LEV__A 0xC40011 +#define FE_FD_REG_NR__A 0xC40012 +#define FE_FD_REG_MEAS_VAL__A 0xC40014 +#define FE_IF_REG_COMM_EXEC__A 0xC50000 +#define FE_IF_REG_INCR0__A 0xC50010 +#define FE_IF_REG_INCR0__W 16 +#define FE_IF_REG_INCR0__M 0xFFFF +#define FE_IF_REG_INCR1__A 0xC50011 +#define FE_IF_REG_INCR1__M 0xFF +#define FE_CF_REG_COMM_EXEC__A 0xC60000 +#define FE_CF_REG_SCL__A 0xC60010 +#define FE_CF_REG_MAX_LEV__A 0xC60011 +#define FE_CF_REG_NR__A 0xC60012 +#define FE_CF_REG_IMP_VAL__A 0xC60013 +#define FE_CF_REG_MEAS_VAL__A 0xC60014 +#define FE_CU_REG_COMM_EXEC__A 0xC70000 +#define FE_CU_REG_FRM_CNT_RST__A 0xC70011 +#define FE_CU_REG_FRM_CNT_STR__A 0xC70012 +#define FT_COMM_EXEC__A 0x1000000 +#define FT_REG_COMM_EXEC__A 0x1010000 +#define CP_COMM_EXEC__A 0x1400000 +#define CP_REG_COMM_EXEC__A 0x1410000 +#define CP_REG_INTERVAL__A 0x1410011 +#define CP_REG_BR_SPL_OFFSET__A 0x1410023 +#define CP_REG_BR_STR_DEL__A 0x1410024 +#define CP_REG_RT_ANG_INC0__A 0x1410030 +#define CP_REG_RT_ANG_INC1__A 0x1410031 +#define CP_REG_RT_DETECT_ENA__A 0x1410032 +#define CP_REG_RT_DETECT_TRH__A 0x1410033 +#define CP_REG_RT_EXP_MARG__A 0x141003E +#define CP_REG_AC_NEXP_OFFS__A 0x1410040 +#define CP_REG_AC_AVER_POW__A 0x1410041 +#define CP_REG_AC_MAX_POW__A 0x1410042 +#define CP_REG_AC_WEIGHT_MAN__A 0x1410043 +#define CP_REG_AC_WEIGHT_EXP__A 0x1410044 +#define CP_REG_AC_AMP_MODE__A 0x1410047 +#define CP_REG_AC_AMP_FIX__A 0x1410048 +#define CP_REG_AC_ANG_MODE__A 0x141004A +#define CE_COMM_EXEC__A 0x1800000 +#define CE_REG_COMM_EXEC__A 0x1810000 +#define CE_REG_TAPSET__A 0x1810011 +#define CE_REG_AVG_POW__A 0x1810012 +#define CE_REG_MAX_POW__A 0x1810013 +#define CE_REG_ATT__A 0x1810014 +#define CE_REG_NRED__A 0x1810015 +#define CE_REG_NE_ERR_SELECT__A 0x1810043 +#define CE_REG_NE_TD_CAL__A 0x1810044 +#define CE_REG_NE_MIXAVG__A 0x1810046 +#define CE_REG_NE_NUPD_OFS__A 0x1810047 +#define CE_REG_PE_NEXP_OFFS__A 0x1810050 +#define CE_REG_PE_TIMESHIFT__A 0x1810051 +#define CE_REG_TP_A0_TAP_NEW__A 0x1810064 +#define CE_REG_TP_A0_TAP_NEW_VALID__A 0x1810065 +#define CE_REG_TP_A0_MU_LMS_STEP__A 0x1810066 +#define CE_REG_TP_A1_TAP_NEW__A 0x1810068 +#define CE_REG_TP_A1_TAP_NEW_VALID__A 0x1810069 +#define CE_REG_TP_A1_MU_LMS_STEP__A 0x181006A +#define CE_REG_TI_NEXP_OFFS__A 0x1810070 +#define CE_REG_FI_SHT_INCR__A 0x1810090 +#define CE_REG_FI_EXP_NORM__A 0x1810091 +#define CE_REG_IR_INPUTSEL__A 0x18100A0 +#define CE_REG_IR_STARTPOS__A 0x18100A1 +#define CE_REG_IR_NEXP_THRES__A 0x18100A2 +#define CE_REG_FR_TREAL00__A 0x1820010 +#define CE_REG_FR_TIMAG00__A 0x1820011 +#define CE_REG_FR_TREAL01__A 0x1820012 +#define CE_REG_FR_TIMAG01__A 0x1820013 +#define CE_REG_FR_TREAL02__A 0x1820014 +#define CE_REG_FR_TIMAG02__A 0x1820015 +#define CE_REG_FR_TREAL03__A 0x1820016 +#define CE_REG_FR_TIMAG03__A 0x1820017 +#define CE_REG_FR_TREAL04__A 0x1820018 +#define CE_REG_FR_TIMAG04__A 0x1820019 +#define CE_REG_FR_TREAL05__A 0x182001A +#define CE_REG_FR_TIMAG05__A 0x182001B +#define CE_REG_FR_TREAL06__A 0x182001C +#define CE_REG_FR_TIMAG06__A 0x182001D +#define CE_REG_FR_TREAL07__A 0x182001E +#define CE_REG_FR_TIMAG07__A 0x182001F +#define CE_REG_FR_TREAL08__A 0x1820020 +#define CE_REG_FR_TIMAG08__A 0x1820021 +#define CE_REG_FR_TREAL09__A 0x1820022 +#define CE_REG_FR_TIMAG09__A 0x1820023 +#define CE_REG_FR_TREAL10__A 0x1820024 +#define CE_REG_FR_TIMAG10__A 0x1820025 +#define CE_REG_FR_TREAL11__A 0x1820026 +#define CE_REG_FR_TIMAG11__A 0x1820027 +#define CE_REG_FR_MID_TAP__A 0x1820028 +#define CE_REG_FR_SQS_G00__A 0x1820029 +#define CE_REG_FR_SQS_G01__A 0x182002A +#define CE_REG_FR_SQS_G02__A 0x182002B +#define CE_REG_FR_SQS_G03__A 0x182002C +#define CE_REG_FR_SQS_G04__A 0x182002D +#define CE_REG_FR_SQS_G05__A 0x182002E +#define CE_REG_FR_SQS_G06__A 0x182002F +#define CE_REG_FR_SQS_G07__A 0x1820030 +#define CE_REG_FR_SQS_G08__A 0x1820031 +#define CE_REG_FR_SQS_G09__A 0x1820032 +#define CE_REG_FR_SQS_G10__A 0x1820033 +#define CE_REG_FR_SQS_G11__A 0x1820034 +#define CE_REG_FR_SQS_G12__A 0x1820035 +#define CE_REG_FR_RIO_G00__A 0x1820036 +#define CE_REG_FR_RIO_G01__A 0x1820037 +#define CE_REG_FR_RIO_G02__A 0x1820038 +#define CE_REG_FR_RIO_G03__A 0x1820039 +#define CE_REG_FR_RIO_G04__A 0x182003A +#define CE_REG_FR_RIO_G05__A 0x182003B +#define CE_REG_FR_RIO_G06__A 0x182003C +#define CE_REG_FR_RIO_G07__A 0x182003D +#define CE_REG_FR_RIO_G08__A 0x182003E +#define CE_REG_FR_RIO_G09__A 0x182003F +#define CE_REG_FR_RIO_G10__A 0x1820040 +#define CE_REG_FR_MODE__A 0x1820041 +#define CE_REG_FR_SQS_TRH__A 0x1820042 +#define CE_REG_FR_RIO_GAIN__A 0x1820043 +#define CE_REG_FR_BYPASS__A 0x1820044 +#define CE_REG_FR_PM_SET__A 0x1820045 +#define CE_REG_FR_ERR_SH__A 0x1820046 +#define CE_REG_FR_MAN_SH__A 0x1820047 +#define CE_REG_FR_TAP_SH__A 0x1820048 +#define EQ_COMM_EXEC__A 0x1C00000 +#define EQ_REG_COMM_EXEC__A 0x1C10000 +#define EQ_REG_COMM_MB__A 0x1C10002 +#define EQ_REG_IS_GAIN_MAN__A 0x1C10015 +#define EQ_REG_IS_GAIN_EXP__A 0x1C10016 +#define EQ_REG_IS_CLIP_EXP__A 0x1C10017 +#define EQ_REG_SN_CEGAIN__A 0x1C1002A +#define EQ_REG_SN_OFFSET__A 0x1C1002B +#define EQ_REG_RC_SEL_CAR__A 0x1C10032 +#define EQ_REG_RC_SEL_CAR_INIT 0x0 +#define EQ_REG_RC_SEL_CAR_DIV_ON 0x1 +#define EQ_REG_RC_SEL_CAR_PASS_A_CC 0x0 +#define EQ_REG_RC_SEL_CAR_PASS_B_CE 0x2 +#define EQ_REG_RC_SEL_CAR_LOCAL_A_CC 0x0 +#define EQ_REG_RC_SEL_CAR_LOCAL_B_CE 0x8 +#define EQ_REG_RC_SEL_CAR_MEAS_A_CC 0x0 +#define EQ_REG_RC_SEL_CAR_MEAS_B_CE 0x20 +#define EQ_REG_OT_CONST__A 0x1C10046 +#define EQ_REG_OT_ALPHA__A 0x1C10047 +#define EQ_REG_OT_QNT_THRES0__A 0x1C10048 +#define EQ_REG_OT_QNT_THRES1__A 0x1C10049 +#define EQ_REG_OT_CSI_STEP__A 0x1C1004A +#define EQ_REG_OT_CSI_OFFSET__A 0x1C1004B +#define EQ_REG_TD_REQ_SMB_CNT__A 0x1C10061 +#define EQ_REG_TD_TPS_PWR_OFS__A 0x1C10062 +#define EC_SB_REG_COMM_EXEC__A 0x2010000 +#define EC_SB_REG_TR_MODE__A 0x2010010 +#define EC_SB_REG_TR_MODE_8K 0x0 +#define EC_SB_REG_TR_MODE_2K 0x1 +#define EC_SB_REG_CONST__A 0x2010011 +#define EC_SB_REG_CONST_QPSK 0x0 +#define EC_SB_REG_CONST_16QAM 0x1 +#define EC_SB_REG_CONST_64QAM 0x2 +#define EC_SB_REG_ALPHA__A 0x2010012 +#define EC_SB_REG_PRIOR__A 0x2010013 +#define EC_SB_REG_PRIOR_HI 0x0 +#define EC_SB_REG_PRIOR_LO 0x1 +#define EC_SB_REG_CSI_HI__A 0x2010014 +#define EC_SB_REG_CSI_LO__A 0x2010015 +#define EC_SB_REG_SMB_TGL__A 0x2010016 +#define EC_SB_REG_SNR_HI__A 0x2010017 +#define EC_SB_REG_SNR_MID__A 0x2010018 +#define EC_SB_REG_SNR_LO__A 0x2010019 +#define EC_SB_REG_SCALE_MSB__A 0x201001A +#define EC_SB_REG_SCALE_BIT2__A 0x201001B +#define EC_SB_REG_SCALE_LSB__A 0x201001C +#define EC_SB_REG_CSI_OFS__A 0x201001D +#define EC_VD_REG_COMM_EXEC__A 0x2090000 +#define EC_VD_REG_FORCE__A 0x2090010 +#define EC_VD_REG_SET_CODERATE__A 0x2090011 +#define EC_VD_REG_SET_CODERATE_C1_2 0x0 +#define EC_VD_REG_SET_CODERATE_C2_3 0x1 +#define EC_VD_REG_SET_CODERATE_C3_4 0x2 +#define EC_VD_REG_SET_CODERATE_C5_6 0x3 +#define EC_VD_REG_SET_CODERATE_C7_8 0x4 +#define EC_VD_REG_REQ_SMB_CNT__A 0x2090012 +#define EC_VD_REG_RLK_ENA__A 0x2090014 +#define EC_OD_REG_COMM_EXEC__A 0x2110000 +#define EC_OD_REG_SYNC__A 0x2110010 +#define EC_OD_DEINT_RAM__A 0x2120000 +#define EC_RS_REG_COMM_EXEC__A 0x2130000 +#define EC_RS_REG_REQ_PCK_CNT__A 0x2130010 +#define EC_RS_REG_VAL__A 0x2130011 +#define EC_RS_REG_VAL_PCK 0x1 +#define EC_RS_EC_RAM__A 0x2140000 +#define EC_OC_REG_COMM_EXEC__A 0x2150000 +#define EC_OC_REG_COMM_EXEC_CTL_ACTIVE 0x1 +#define EC_OC_REG_COMM_EXEC_CTL_HOLD 0x2 +#define EC_OC_REG_COMM_INT_STA__A 0x2150007 +#define EC_OC_REG_OC_MODE_LOP__A 0x2150010 +#define EC_OC_REG_OC_MODE_LOP_PAR_ENA__M 0x1 +#define EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE 0x0 +#define EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE 0x1 +#define EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M 0x4 +#define EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC 0x0 +#define EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M 0x80 +#define EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL 0x80 +#define EC_OC_REG_OC_MODE_HIP__A 0x2150011 +#define EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR 0x10 +#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M 0x200 +#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE 0x0 +#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE 0x200 +#define EC_OC_REG_OC_MPG_SIO__A 0x2150012 +#define EC_OC_REG_OC_MPG_SIO__M 0xFFF +#define EC_OC_REG_OC_MON_SIO__A 0x2150013 +#define EC_OC_REG_DTO_INC_LOP__A 0x2150014 +#define EC_OC_REG_DTO_INC_HIP__A 0x2150015 +#define EC_OC_REG_SNC_ISC_LVL__A 0x2150016 +#define EC_OC_REG_SNC_ISC_LVL_OSC__M 0xF0 +#define EC_OC_REG_TMD_TOP_MODE__A 0x215001D +#define EC_OC_REG_TMD_TOP_CNT__A 0x215001E +#define EC_OC_REG_TMD_HIL_MAR__A 0x215001F +#define EC_OC_REG_TMD_LOL_MAR__A 0x2150020 +#define EC_OC_REG_TMD_CUR_CNT__A 0x2150021 +#define EC_OC_REG_AVR_ASH_CNT__A 0x2150023 +#define EC_OC_REG_AVR_BSH_CNT__A 0x2150024 +#define EC_OC_REG_RCN_MODE__A 0x2150027 +#define EC_OC_REG_RCN_CRA_LOP__A 0x2150028 +#define EC_OC_REG_RCN_CRA_HIP__A 0x2150029 +#define EC_OC_REG_RCN_CST_LOP__A 0x215002A +#define EC_OC_REG_RCN_CST_HIP__A 0x215002B +#define EC_OC_REG_RCN_SET_LVL__A 0x215002C +#define EC_OC_REG_RCN_GAI_LVL__A 0x215002D +#define EC_OC_REG_RCN_CLP_LOP__A 0x2150032 +#define EC_OC_REG_RCN_CLP_HIP__A 0x2150033 +#define EC_OC_REG_RCN_MAP_LOP__A 0x2150034 +#define EC_OC_REG_RCN_MAP_HIP__A 0x2150035 +#define EC_OC_REG_OCR_MPG_UOS__A 0x2150036 +#define EC_OC_REG_OCR_MPG_UOS__M 0xFFF +#define EC_OC_REG_OCR_MPG_UOS_INIT 0x0 +#define EC_OC_REG_OCR_MPG_USR_DAT__A 0x2150038 +#define EC_OC_REG_OCR_MON_UOS__A 0x2150039 +#define EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE 0x1 +#define EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE 0x2 +#define EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE 0x4 +#define EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE 0x8 +#define EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE 0x10 +#define EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE 0x20 +#define EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE 0x40 +#define EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE 0x80 +#define EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE 0x100 +#define EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE 0x200 +#define EC_OC_REG_OCR_MON_UOS_VAL_ENABLE 0x400 +#define EC_OC_REG_OCR_MON_UOS_CLK_ENABLE 0x800 +#define EC_OC_REG_OCR_MON_WRI__A 0x215003A +#define EC_OC_REG_OCR_MON_WRI_INIT 0x0 +#define EC_OC_REG_IPR_INV_MPG__A 0x2150045 +#define CC_REG_OSC_MODE__A 0x2410010 +#define CC_REG_OSC_MODE_M20 0x1 +#define CC_REG_PLL_MODE__A 0x2410011 +#define CC_REG_PLL_MODE_BYPASS_PLL 0x1 +#define CC_REG_PLL_MODE_PUMP_CUR_12 0x14 +#define CC_REG_REF_DIVIDE__A 0x2410012 +#define CC_REG_PWD_MODE__A 0x2410015 +#define CC_REG_PWD_MODE_DOWN_PLL 0x2 +#define CC_REG_UPDATE__A 0x2410017 +#define CC_REG_UPDATE_KEY 0x3973 +#define CC_REG_JTAGID_L__A 0x2410019 +#define LC_COMM_EXEC__A 0x2800000 +#define LC_RA_RAM_IFINCR_NOM_L__A 0x282000C +#define LC_RA_RAM_FILTER_SYM_SET__A 0x282001A +#define LC_RA_RAM_FILTER_SYM_SET__PRE 0x3E8 +#define LC_RA_RAM_FILTER_CRMM_A__A 0x2820060 +#define LC_RA_RAM_FILTER_CRMM_A__PRE 0x4 +#define LC_RA_RAM_FILTER_CRMM_B__A 0x2820061 +#define LC_RA_RAM_FILTER_CRMM_B__PRE 0x1 +#define LC_RA_RAM_FILTER_SRMM_A__A 0x2820068 +#define LC_RA_RAM_FILTER_SRMM_A__PRE 0x4 +#define LC_RA_RAM_FILTER_SRMM_B__A 0x2820069 +#define LC_RA_RAM_FILTER_SRMM_B__PRE 0x1 +#define B_HI_COMM_EXEC__A 0x400000 +#define B_HI_COMM_MB__A 0x400002 +#define B_HI_CT_REG_COMM_STATE__A 0x410001 +#define B_HI_RA_RAM_SRV_RES__A 0x420031 +#define B_HI_RA_RAM_SRV_CMD__A 0x420032 +#define B_HI_RA_RAM_SRV_CMD_RESET 0x2 +#define B_HI_RA_RAM_SRV_CMD_CONFIG 0x3 +#define B_HI_RA_RAM_SRV_CMD_EXECUTE 0x6 +#define B_HI_RA_RAM_SRV_RST_KEY__A 0x420033 +#define B_HI_RA_RAM_SRV_RST_KEY_ACT 0x3973 +#define B_HI_RA_RAM_SRV_CFG_KEY__A 0x420033 +#define B_HI_RA_RAM_SRV_CFG_DIV__A 0x420034 +#define B_HI_RA_RAM_SRV_CFG_BDL__A 0x420035 +#define B_HI_RA_RAM_SRV_CFG_WUP__A 0x420036 +#define B_HI_RA_RAM_SRV_CFG_ACT__A 0x420037 +#define B_HI_RA_RAM_SRV_CFG_ACT_SLV0_ON 0x1 +#define B_HI_RA_RAM_SRV_CFG_ACT_BRD__M 0x4 +#define B_HI_RA_RAM_SRV_CFG_ACT_BRD_OFF 0x0 +#define B_HI_RA_RAM_SRV_CFG_ACT_BRD_ON 0x4 +#define B_HI_RA_RAM_SRV_CFG_ACT_PWD_EXE 0x8 +#define B_HI_RA_RAM_USR_BEGIN__A 0x420040 +#define B_HI_IF_RAM_TRP_BPT0__AX 0x430000 +#define B_HI_IF_RAM_USR_BEGIN__A 0x430200 +#define B_SC_COMM_EXEC__A 0x800000 +#define B_SC_COMM_EXEC_CTL_STOP 0x0 +#define B_SC_COMM_STATE__A 0x800001 +#define B_SC_RA_RAM_PARAM0__A 0x820040 +#define B_SC_RA_RAM_PARAM1__A 0x820041 +#define B_SC_RA_RAM_CMD_ADDR__A 0x820042 +#define B_SC_RA_RAM_CMD__A 0x820043 +#define B_SC_RA_RAM_CMD_PROC_START 0x1 +#define B_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 +#define B_SC_RA_RAM_CMD_GET_OP_PARAM 0x5 +#define B_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 +#define B_SC_RA_RAM_LOCKTRACK_MIN 0x1 +#define B_SC_RA_RAM_OP_PARAM_MODE_2K 0x0 +#define B_SC_RA_RAM_OP_PARAM_MODE_8K 0x1 +#define B_SC_RA_RAM_OP_PARAM_GUARD_32 0x0 +#define B_SC_RA_RAM_OP_PARAM_GUARD_16 0x4 +#define B_SC_RA_RAM_OP_PARAM_GUARD_8 0x8 +#define B_SC_RA_RAM_OP_PARAM_GUARD_4 0xC +#define B_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 +#define B_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 +#define B_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 +#define B_SC_RA_RAM_OP_PARAM_HIER_NO 0x0 +#define B_SC_RA_RAM_OP_PARAM_HIER_A1 0x40 +#define B_SC_RA_RAM_OP_PARAM_HIER_A2 0x80 +#define B_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 +#define B_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 +#define B_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 +#define B_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 +#define B_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 +#define B_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 +#define B_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 +#define B_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 +#define B_SC_RA_RAM_OP_AUTO_MODE__M 0x1 +#define B_SC_RA_RAM_OP_AUTO_GUARD__M 0x2 +#define B_SC_RA_RAM_OP_AUTO_CONST__M 0x4 +#define B_SC_RA_RAM_OP_AUTO_HIER__M 0x8 +#define B_SC_RA_RAM_OP_AUTO_RATE__M 0x10 +#define B_SC_RA_RAM_LOCK__A 0x82004B +#define B_SC_RA_RAM_LOCK_DEMOD__M 0x1 +#define B_SC_RA_RAM_LOCK_FEC__M 0x2 +#define B_SC_RA_RAM_LOCK_MPEG__M 0x4 +#define B_SC_RA_RAM_BE_OPT_ENA__A 0x82004C +#define B_SC_RA_RAM_BE_OPT_ENA_CP_OPT 0x1 +#define B_SC_RA_RAM_BE_OPT_DELAY__A 0x82004D +#define B_SC_RA_RAM_CONFIG__A 0x820050 +#define B_SC_RA_RAM_CONFIG_FR_ENABLE__M 0x4 +#define B_SC_RA_RAM_CONFIG_FREQSCAN__M 0x10 +#define B_SC_RA_RAM_CONFIG_SLAVE__M 0x20 +#define B_SC_RA_RAM_CONFIG_DIV_BLANK_ENABLE__M 0x200 +#define B_SC_RA_RAM_CONFIG_DIV_ECHO_ENABLE__M 0x400 +#define B_SC_RA_RAM_CO_TD_CAL_2K__A 0x82005D +#define B_SC_RA_RAM_CO_TD_CAL_8K__A 0x82005E +#define B_SC_RA_RAM_IF_SAVE__AX 0x82008E +#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A 0x820098 +#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A 0x820099 +#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A 0x82009A +#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A 0x82009B +#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A 0x82009C +#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A 0x82009D +#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A 0x82009E +#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A 0x82009F +#define B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A 0x8200D1 +#define B_SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE 0x9 +#define B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A 0x8200D2 +#define B_SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE 0x4 +#define B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A 0x8200D3 +#define B_SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE 0x100 +#define B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A 0x8200D4 +#define B_SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE 0x8 +#define B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A 0x8200D5 +#define B_SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE 0x8 +#define B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A 0x8200D6 +#define B_SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE 0x200 +#define B_SC_RA_RAM_IR_FINE_2K_LENGTH__A 0x8200D7 +#define B_SC_RA_RAM_IR_FINE_2K_LENGTH__PRE 0x9 +#define B_SC_RA_RAM_IR_FINE_2K_FREQINC__A 0x8200D8 +#define B_SC_RA_RAM_IR_FINE_2K_FREQINC__PRE 0x4 +#define B_SC_RA_RAM_IR_FINE_2K_KAISINC__A 0x8200D9 +#define B_SC_RA_RAM_IR_FINE_2K_KAISINC__PRE 0x100 +#define B_SC_RA_RAM_IR_FINE_8K_LENGTH__A 0x8200DA +#define B_SC_RA_RAM_IR_FINE_8K_LENGTH__PRE 0xB +#define B_SC_RA_RAM_IR_FINE_8K_FREQINC__A 0x8200DB +#define B_SC_RA_RAM_IR_FINE_8K_FREQINC__PRE 0x1 +#define B_SC_RA_RAM_IR_FINE_8K_KAISINC__A 0x8200DC +#define B_SC_RA_RAM_IR_FINE_8K_KAISINC__PRE 0x40 +#define B_SC_RA_RAM_ECHO_SHIFT_LIM__A 0x8200DD +#define B_SC_RA_RAM_SAMPLE_RATE_COUNT__A 0x8200E8 +#define B_SC_RA_RAM_SAMPLE_RATE_STEP__A 0x8200E9 +#define B_SC_RA_RAM_BAND__A 0x8200EC +#define B_SC_RA_RAM_LC_ABS_2K__A 0x8200F4 +#define B_SC_RA_RAM_LC_ABS_2K__PRE 0x1F +#define B_SC_RA_RAM_LC_ABS_8K__A 0x8200F5 +#define B_SC_RA_RAM_LC_ABS_8K__PRE 0x1F +#define B_SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE 0x100 +#define B_SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE 0x4 +#define B_SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE 0x1E2 +#define B_SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE 0x4 +#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE 0x10D +#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE 0x5 +#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE 0x17D +#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE 0x4 +#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE 0x133 +#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE 0x5 +#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE 0x114 +#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE 0x5 +#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE 0x14A +#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE 0x4 +#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE 0x1BB +#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE 0x4 +#define B_SC_RA_RAM_DRIVER_VERSION__AX 0x8201FE +#define B_SC_RA_RAM_PROC_LOCKTRACK 0x0 +#define B_FE_COMM_EXEC__A 0xC00000 +#define B_FE_AD_REG_COMM_EXEC__A 0xC10000 +#define B_FE_AD_REG_FDB_IN__A 0xC10012 +#define B_FE_AD_REG_PD__A 0xC10013 +#define B_FE_AD_REG_INVEXT__A 0xC10014 +#define B_FE_AD_REG_CLKNEG__A 0xC10015 +#define B_FE_AG_REG_COMM_EXEC__A 0xC20000 +#define B_FE_AG_REG_AG_MODE_LOP__A 0xC20010 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_4__M 0x10 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC 0x0 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC 0x10 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_5__M 0x20 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC 0x0 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_C__M 0x1000 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC 0x0 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC 0x1000 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_E__M 0x4000 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC 0x0 +#define B_FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC 0x4000 +#define B_FE_AG_REG_AG_MODE_HIP__A 0xC20011 +#define B_FE_AG_REG_AG_MODE_HIP_MODE_J__M 0x8 +#define B_FE_AG_REG_AG_MODE_HIP_MODE_J_STATIC 0x0 +#define B_FE_AG_REG_AG_MODE_HIP_MODE_J_DYNAMIC 0x8 +#define B_FE_AG_REG_AG_PGA_MODE__A 0xC20012 +#define B_FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN 0x0 +#define B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN 0x1 +#define B_FE_AG_REG_AG_AGC_SIO__A 0xC20013 +#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M 0x2 +#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT 0x0 +#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT 0x2 +#define B_FE_AG_REG_AG_PWD__A 0xC20015 +#define B_FE_AG_REG_AG_PWD_PWD_PD2__M 0x2 +#define B_FE_AG_REG_AG_PWD_PWD_PD2_DISABLE 0x0 +#define B_FE_AG_REG_AG_PWD_PWD_PD2_ENABLE 0x2 +#define B_FE_AG_REG_DCE_AUR_CNT__A 0xC20016 +#define B_FE_AG_REG_DCE_RUR_CNT__A 0xC20017 +#define B_FE_AG_REG_ACE_AUR_CNT__A 0xC2001A +#define B_FE_AG_REG_ACE_RUR_CNT__A 0xC2001B +#define B_FE_AG_REG_CDR_RUR_CNT__A 0xC20020 +#define B_FE_AG_REG_EGC_RUR_CNT__A 0xC20024 +#define B_FE_AG_REG_EGC_SET_LVL__A 0xC20025 +#define B_FE_AG_REG_EGC_SET_LVL__M 0x1FF +#define B_FE_AG_REG_EGC_FLA_RGN__A 0xC20026 +#define B_FE_AG_REG_EGC_SLO_RGN__A 0xC20027 +#define B_FE_AG_REG_EGC_JMP_PSN__A 0xC20028 +#define B_FE_AG_REG_EGC_FLA_INC__A 0xC20029 +#define B_FE_AG_REG_EGC_FLA_DEC__A 0xC2002A +#define B_FE_AG_REG_EGC_SLO_INC__A 0xC2002B +#define B_FE_AG_REG_EGC_SLO_DEC__A 0xC2002C +#define B_FE_AG_REG_EGC_FAS_INC__A 0xC2002D +#define B_FE_AG_REG_EGC_FAS_DEC__A 0xC2002E +#define B_FE_AG_REG_PM1_AGC_WRI__A 0xC20030 +#define B_FE_AG_REG_PM1_AGC_WRI__M 0x7FF +#define B_FE_AG_REG_GC1_AGC_RIC__A 0xC20031 +#define B_FE_AG_REG_GC1_AGC_OFF__A 0xC20032 +#define B_FE_AG_REG_GC1_AGC_MAX__A 0xC20033 +#define B_FE_AG_REG_GC1_AGC_MIN__A 0xC20034 +#define B_FE_AG_REG_GC1_AGC_DAT__A 0xC20035 +#define B_FE_AG_REG_GC1_AGC_DAT__M 0x3FF +#define B_FE_AG_REG_PM2_AGC_WRI__A 0xC20036 +#define B_FE_AG_REG_IND_WIN__A 0xC2003C +#define B_FE_AG_REG_IND_THD_LOL__A 0xC2003D +#define B_FE_AG_REG_IND_THD_HIL__A 0xC2003E +#define B_FE_AG_REG_IND_DEL__A 0xC2003F +#define B_FE_AG_REG_IND_PD1_WRI__A 0xC20040 +#define B_FE_AG_REG_PDA_AUR_CNT__A 0xC20041 +#define B_FE_AG_REG_PDA_RUR_CNT__A 0xC20042 +#define B_FE_AG_REG_PDA_AVE_DAT__A 0xC20043 +#define B_FE_AG_REG_PDC_RUR_CNT__A 0xC20044 +#define B_FE_AG_REG_PDC_SET_LVL__A 0xC20045 +#define B_FE_AG_REG_PDC_FLA_RGN__A 0xC20046 +#define B_FE_AG_REG_PDC_JMP_PSN__A 0xC20047 +#define B_FE_AG_REG_PDC_FLA_STP__A 0xC20048 +#define B_FE_AG_REG_PDC_SLO_STP__A 0xC20049 +#define B_FE_AG_REG_PDC_PD2_WRI__A 0xC2004A +#define B_FE_AG_REG_PDC_MAP_DAT__A 0xC2004B +#define B_FE_AG_REG_PDC_MAX__A 0xC2004C +#define B_FE_AG_REG_TGA_AUR_CNT__A 0xC2004D +#define B_FE_AG_REG_TGA_RUR_CNT__A 0xC2004E +#define B_FE_AG_REG_TGA_AVE_DAT__A 0xC2004F +#define B_FE_AG_REG_TGC_RUR_CNT__A 0xC20050 +#define B_FE_AG_REG_TGC_SET_LVL__A 0xC20051 +#define B_FE_AG_REG_TGC_SET_LVL__M 0x3F +#define B_FE_AG_REG_TGC_FLA_RGN__A 0xC20052 +#define B_FE_AG_REG_TGC_JMP_PSN__A 0xC20053 +#define B_FE_AG_REG_TGC_FLA_STP__A 0xC20054 +#define B_FE_AG_REG_TGC_SLO_STP__A 0xC20055 +#define B_FE_AG_REG_TGC_MAP_DAT__A 0xC20056 +#define B_FE_AG_REG_FGM_WRI__A 0xC20061 +#define B_FE_AG_REG_BGC_FGC_WRI__A 0xC20068 +#define B_FE_AG_REG_BGC_CGC_WRI__A 0xC20069 +#define B_FE_FS_REG_COMM_EXEC__A 0xC30000 +#define B_FE_FS_REG_ADD_INC_LOP__A 0xC30010 +#define B_FE_FD_REG_COMM_EXEC__A 0xC40000 +#define B_FE_FD_REG_SCL__A 0xC40010 +#define B_FE_FD_REG_MAX_LEV__A 0xC40011 +#define B_FE_FD_REG_NR__A 0xC40012 +#define B_FE_FD_REG_MEAS_VAL__A 0xC40014 +#define B_FE_IF_REG_COMM_EXEC__A 0xC50000 +#define B_FE_IF_REG_INCR0__A 0xC50010 +#define B_FE_IF_REG_INCR0__W 16 +#define B_FE_IF_REG_INCR0__M 0xFFFF +#define B_FE_IF_REG_INCR1__A 0xC50011 +#define B_FE_IF_REG_INCR1__M 0xFF +#define B_FE_CF_REG_COMM_EXEC__A 0xC60000 +#define B_FE_CF_REG_SCL__A 0xC60010 +#define B_FE_CF_REG_MAX_LEV__A 0xC60011 +#define B_FE_CF_REG_NR__A 0xC60012 +#define B_FE_CF_REG_IMP_VAL__A 0xC60013 +#define B_FE_CF_REG_MEAS_VAL__A 0xC60014 +#define B_FE_CU_REG_COMM_EXEC__A 0xC70000 +#define B_FE_CU_REG_FRM_CNT_RST__A 0xC70011 +#define B_FE_CU_REG_FRM_CNT_STR__A 0xC70012 +#define B_FE_CU_REG_CTR_NFC_ICR__A 0xC70020 +#define B_FE_CU_REG_CTR_NFC_OCR__A 0xC70021 +#define B_FE_CU_REG_DIV_NFC_CLP__A 0xC70027 +#define B_FT_COMM_EXEC__A 0x1000000 +#define B_FT_REG_COMM_EXEC__A 0x1010000 +#define B_CP_COMM_EXEC__A 0x1400000 +#define B_CP_REG_COMM_EXEC__A 0x1410000 +#define B_CP_REG_INTERVAL__A 0x1410011 +#define B_CP_REG_BR_SPL_OFFSET__A 0x1410023 +#define B_CP_REG_BR_STR_DEL__A 0x1410024 +#define B_CP_REG_RT_ANG_INC0__A 0x1410030 +#define B_CP_REG_RT_ANG_INC1__A 0x1410031 +#define B_CP_REG_RT_DETECT_TRH__A 0x1410033 +#define B_CP_REG_AC_NEXP_OFFS__A 0x1410040 +#define B_CP_REG_AC_AVER_POW__A 0x1410041 +#define B_CP_REG_AC_MAX_POW__A 0x1410042 +#define B_CP_REG_AC_WEIGHT_MAN__A 0x1410043 +#define B_CP_REG_AC_WEIGHT_EXP__A 0x1410044 +#define B_CP_REG_AC_AMP_MODE__A 0x1410047 +#define B_CP_REG_AC_AMP_FIX__A 0x1410048 +#define B_CP_REG_AC_ANG_MODE__A 0x141004A +#define B_CE_COMM_EXEC__A 0x1800000 +#define B_CE_REG_COMM_EXEC__A 0x1810000 +#define B_CE_REG_TAPSET__A 0x1810011 +#define B_CE_REG_AVG_POW__A 0x1810012 +#define B_CE_REG_MAX_POW__A 0x1810013 +#define B_CE_REG_ATT__A 0x1810014 +#define B_CE_REG_NRED__A 0x1810015 +#define B_CE_REG_NE_ERR_SELECT__A 0x1810043 +#define B_CE_REG_NE_TD_CAL__A 0x1810044 +#define B_CE_REG_NE_MIXAVG__A 0x1810046 +#define B_CE_REG_NE_NUPD_OFS__A 0x1810047 +#define B_CE_REG_PE_NEXP_OFFS__A 0x1810050 +#define B_CE_REG_PE_TIMESHIFT__A 0x1810051 +#define B_CE_REG_TP_A0_TAP_NEW__A 0x1810064 +#define B_CE_REG_TP_A0_TAP_NEW_VALID__A 0x1810065 +#define B_CE_REG_TP_A0_MU_LMS_STEP__A 0x1810066 +#define B_CE_REG_TP_A1_TAP_NEW__A 0x1810068 +#define B_CE_REG_TP_A1_TAP_NEW_VALID__A 0x1810069 +#define B_CE_REG_TP_A1_MU_LMS_STEP__A 0x181006A +#define B_CE_REG_TI_PHN_ENABLE__A 0x1810073 +#define B_CE_REG_FI_SHT_INCR__A 0x1810090 +#define B_CE_REG_FI_EXP_NORM__A 0x1810091 +#define B_CE_REG_IR_INPUTSEL__A 0x18100A0 +#define B_CE_REG_IR_STARTPOS__A 0x18100A1 +#define B_CE_REG_IR_NEXP_THRES__A 0x18100A2 +#define B_CE_REG_FR_TREAL00__A 0x1820010 +#define B_CE_REG_FR_TIMAG00__A 0x1820011 +#define B_CE_REG_FR_TREAL01__A 0x1820012 +#define B_CE_REG_FR_TIMAG01__A 0x1820013 +#define B_CE_REG_FR_TREAL02__A 0x1820014 +#define B_CE_REG_FR_TIMAG02__A 0x1820015 +#define B_CE_REG_FR_TREAL03__A 0x1820016 +#define B_CE_REG_FR_TIMAG03__A 0x1820017 +#define B_CE_REG_FR_TREAL04__A 0x1820018 +#define B_CE_REG_FR_TIMAG04__A 0x1820019 +#define B_CE_REG_FR_TREAL05__A 0x182001A +#define B_CE_REG_FR_TIMAG05__A 0x182001B +#define B_CE_REG_FR_TREAL06__A 0x182001C +#define B_CE_REG_FR_TIMAG06__A 0x182001D +#define B_CE_REG_FR_TREAL07__A 0x182001E +#define B_CE_REG_FR_TIMAG07__A 0x182001F +#define B_CE_REG_FR_TREAL08__A 0x1820020 +#define B_CE_REG_FR_TIMAG08__A 0x1820021 +#define B_CE_REG_FR_TREAL09__A 0x1820022 +#define B_CE_REG_FR_TIMAG09__A 0x1820023 +#define B_CE_REG_FR_TREAL10__A 0x1820024 +#define B_CE_REG_FR_TIMAG10__A 0x1820025 +#define B_CE_REG_FR_TREAL11__A 0x1820026 +#define B_CE_REG_FR_TIMAG11__A 0x1820027 +#define B_CE_REG_FR_MID_TAP__A 0x1820028 +#define B_CE_REG_FR_SQS_G00__A 0x1820029 +#define B_CE_REG_FR_SQS_G01__A 0x182002A +#define B_CE_REG_FR_SQS_G02__A 0x182002B +#define B_CE_REG_FR_SQS_G03__A 0x182002C +#define B_CE_REG_FR_SQS_G04__A 0x182002D +#define B_CE_REG_FR_SQS_G05__A 0x182002E +#define B_CE_REG_FR_SQS_G06__A 0x182002F +#define B_CE_REG_FR_SQS_G07__A 0x1820030 +#define B_CE_REG_FR_SQS_G08__A 0x1820031 +#define B_CE_REG_FR_SQS_G09__A 0x1820032 +#define B_CE_REG_FR_SQS_G10__A 0x1820033 +#define B_CE_REG_FR_SQS_G11__A 0x1820034 +#define B_CE_REG_FR_SQS_G12__A 0x1820035 +#define B_CE_REG_FR_RIO_G00__A 0x1820036 +#define B_CE_REG_FR_RIO_G01__A 0x1820037 +#define B_CE_REG_FR_RIO_G02__A 0x1820038 +#define B_CE_REG_FR_RIO_G03__A 0x1820039 +#define B_CE_REG_FR_RIO_G04__A 0x182003A +#define B_CE_REG_FR_RIO_G05__A 0x182003B +#define B_CE_REG_FR_RIO_G06__A 0x182003C +#define B_CE_REG_FR_RIO_G07__A 0x182003D +#define B_CE_REG_FR_RIO_G08__A 0x182003E +#define B_CE_REG_FR_RIO_G09__A 0x182003F +#define B_CE_REG_FR_RIO_G10__A 0x1820040 +#define B_CE_REG_FR_MODE__A 0x1820041 +#define B_CE_REG_FR_SQS_TRH__A 0x1820042 +#define B_CE_REG_FR_RIO_GAIN__A 0x1820043 +#define B_CE_REG_FR_BYPASS__A 0x1820044 +#define B_CE_REG_FR_PM_SET__A 0x1820045 +#define B_CE_REG_FR_ERR_SH__A 0x1820046 +#define B_CE_REG_FR_MAN_SH__A 0x1820047 +#define B_CE_REG_FR_TAP_SH__A 0x1820048 +#define B_EQ_COMM_EXEC__A 0x1C00000 +#define B_EQ_REG_COMM_EXEC__A 0x1C10000 +#define B_EQ_REG_COMM_MB__A 0x1C10002 +#define B_EQ_REG_IS_GAIN_MAN__A 0x1C10015 +#define B_EQ_REG_IS_GAIN_EXP__A 0x1C10016 +#define B_EQ_REG_IS_CLIP_EXP__A 0x1C10017 +#define B_EQ_REG_SN_CEGAIN__A 0x1C1002A +#define B_EQ_REG_SN_OFFSET__A 0x1C1002B +#define B_EQ_REG_RC_SEL_CAR__A 0x1C10032 +#define B_EQ_REG_RC_SEL_CAR_INIT 0x2 +#define B_EQ_REG_RC_SEL_CAR_DIV_ON 0x1 +#define B_EQ_REG_RC_SEL_CAR_PASS_A_CC 0x0 +#define B_EQ_REG_RC_SEL_CAR_PASS_B_CE 0x2 +#define B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC 0x0 +#define B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE 0x8 +#define B_EQ_REG_RC_SEL_CAR_MEAS_A_CC 0x0 +#define B_EQ_REG_RC_SEL_CAR_MEAS_B_CE 0x20 +#define B_EQ_REG_RC_SEL_CAR_FFTMODE__M 0x80 +#define B_EQ_REG_OT_CONST__A 0x1C10046 +#define B_EQ_REG_OT_ALPHA__A 0x1C10047 +#define B_EQ_REG_OT_QNT_THRES0__A 0x1C10048 +#define B_EQ_REG_OT_QNT_THRES1__A 0x1C10049 +#define B_EQ_REG_OT_CSI_STEP__A 0x1C1004A +#define B_EQ_REG_OT_CSI_OFFSET__A 0x1C1004B +#define B_EQ_REG_TD_REQ_SMB_CNT__A 0x1C10061 +#define B_EQ_REG_TD_TPS_PWR_OFS__A 0x1C10062 +#define B_EC_SB_REG_COMM_EXEC__A 0x2010000 +#define B_EC_SB_REG_TR_MODE__A 0x2010010 +#define B_EC_SB_REG_TR_MODE_8K 0x0 +#define B_EC_SB_REG_TR_MODE_2K 0x1 +#define B_EC_SB_REG_CONST__A 0x2010011 +#define B_EC_SB_REG_CONST_QPSK 0x0 +#define B_EC_SB_REG_CONST_16QAM 0x1 +#define B_EC_SB_REG_CONST_64QAM 0x2 +#define B_EC_SB_REG_ALPHA__A 0x2010012 +#define B_EC_SB_REG_PRIOR__A 0x2010013 +#define B_EC_SB_REG_PRIOR_HI 0x0 +#define B_EC_SB_REG_PRIOR_LO 0x1 +#define B_EC_SB_REG_CSI_HI__A 0x2010014 +#define B_EC_SB_REG_CSI_LO__A 0x2010015 +#define B_EC_SB_REG_SMB_TGL__A 0x2010016 +#define B_EC_SB_REG_SNR_HI__A 0x2010017 +#define B_EC_SB_REG_SNR_MID__A 0x2010018 +#define B_EC_SB_REG_SNR_LO__A 0x2010019 +#define B_EC_SB_REG_SCALE_MSB__A 0x201001A +#define B_EC_SB_REG_SCALE_BIT2__A 0x201001B +#define B_EC_SB_REG_SCALE_LSB__A 0x201001C +#define B_EC_SB_REG_CSI_OFS0__A 0x201001D +#define B_EC_SB_REG_CSI_OFS1__A 0x201001E +#define B_EC_SB_REG_CSI_OFS2__A 0x201001F +#define B_EC_VD_REG_COMM_EXEC__A 0x2090000 +#define B_EC_VD_REG_FORCE__A 0x2090010 +#define B_EC_VD_REG_SET_CODERATE__A 0x2090011 +#define B_EC_VD_REG_SET_CODERATE_C1_2 0x0 +#define B_EC_VD_REG_SET_CODERATE_C2_3 0x1 +#define B_EC_VD_REG_SET_CODERATE_C3_4 0x2 +#define B_EC_VD_REG_SET_CODERATE_C5_6 0x3 +#define B_EC_VD_REG_SET_CODERATE_C7_8 0x4 +#define B_EC_VD_REG_REQ_SMB_CNT__A 0x2090012 +#define B_EC_VD_REG_RLK_ENA__A 0x2090014 +#define B_EC_OD_REG_COMM_EXEC__A 0x2110000 +#define B_EC_OD_REG_SYNC__A 0x2110664 +#define B_EC_OD_DEINT_RAM__A 0x2120000 +#define B_EC_RS_REG_COMM_EXEC__A 0x2130000 +#define B_EC_RS_REG_REQ_PCK_CNT__A 0x2130010 +#define B_EC_RS_REG_VAL__A 0x2130011 +#define B_EC_RS_REG_VAL_PCK 0x1 +#define B_EC_RS_EC_RAM__A 0x2140000 +#define B_EC_OC_REG_COMM_EXEC__A 0x2150000 +#define B_EC_OC_REG_COMM_EXEC_CTL_ACTIVE 0x1 +#define B_EC_OC_REG_COMM_EXEC_CTL_HOLD 0x2 +#define B_EC_OC_REG_COMM_INT_STA__A 0x2150007 +#define B_EC_OC_REG_OC_MODE_LOP__A 0x2150010 +#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA__M 0x1 +#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE 0x0 +#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE 0x1 +#define B_EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M 0x4 +#define B_EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC 0x0 +#define B_EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M 0x80 +#define B_EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL 0x80 +#define B_EC_OC_REG_OC_MODE_HIP__A 0x2150011 +#define B_EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR 0x10 +#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M 0x200 +#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE 0x0 +#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE 0x200 +#define B_EC_OC_REG_OC_MPG_SIO__A 0x2150012 +#define B_EC_OC_REG_OC_MPG_SIO__M 0xFFF +#define B_EC_OC_REG_DTO_INC_LOP__A 0x2150014 +#define B_EC_OC_REG_DTO_INC_HIP__A 0x2150015 +#define B_EC_OC_REG_SNC_ISC_LVL__A 0x2150016 +#define B_EC_OC_REG_SNC_ISC_LVL_OSC__M 0xF0 +#define B_EC_OC_REG_TMD_TOP_MODE__A 0x215001D +#define B_EC_OC_REG_TMD_TOP_CNT__A 0x215001E +#define B_EC_OC_REG_TMD_HIL_MAR__A 0x215001F +#define B_EC_OC_REG_TMD_LOL_MAR__A 0x2150020 +#define B_EC_OC_REG_TMD_CUR_CNT__A 0x2150021 +#define B_EC_OC_REG_AVR_ASH_CNT__A 0x2150023 +#define B_EC_OC_REG_AVR_BSH_CNT__A 0x2150024 +#define B_EC_OC_REG_RCN_MODE__A 0x2150027 +#define B_EC_OC_REG_RCN_CRA_LOP__A 0x2150028 +#define B_EC_OC_REG_RCN_CRA_HIP__A 0x2150029 +#define B_EC_OC_REG_RCN_CST_LOP__A 0x215002A +#define B_EC_OC_REG_RCN_CST_HIP__A 0x215002B +#define B_EC_OC_REG_RCN_SET_LVL__A 0x215002C +#define B_EC_OC_REG_RCN_GAI_LVL__A 0x215002D +#define B_EC_OC_REG_RCN_CLP_LOP__A 0x2150032 +#define B_EC_OC_REG_RCN_CLP_HIP__A 0x2150033 +#define B_EC_OC_REG_RCN_MAP_LOP__A 0x2150034 +#define B_EC_OC_REG_RCN_MAP_HIP__A 0x2150035 +#define B_EC_OC_REG_OCR_MPG_UOS__A 0x2150036 +#define B_EC_OC_REG_OCR_MPG_UOS__M 0xFFF +#define B_EC_OC_REG_OCR_MPG_UOS_INIT 0x0 +#define B_EC_OC_REG_OCR_MPG_USR_DAT__A 0x2150038 +#define B_EC_OC_REG_IPR_INV_MPG__A 0x2150045 +#define B_EC_OC_REG_DTO_CLKMODE__A 0x2150047 +#define B_EC_OC_REG_DTO_PER__A 0x2150048 +#define B_EC_OC_REG_DTO_BUR__A 0x2150049 +#define B_EC_OC_REG_RCR_CLKMODE__A 0x215004A +#define B_CC_REG_OSC_MODE__A 0x2410010 +#define B_CC_REG_OSC_MODE_M20 0x1 +#define B_CC_REG_PLL_MODE__A 0x2410011 +#define B_CC_REG_PLL_MODE_BYPASS_PLL 0x1 +#define B_CC_REG_PLL_MODE_PUMP_CUR_12 0x14 +#define B_CC_REG_REF_DIVIDE__A 0x2410012 +#define B_CC_REG_PWD_MODE__A 0x2410015 +#define B_CC_REG_PWD_MODE_DOWN_PLL 0x2 +#define B_CC_REG_UPDATE__A 0x2410017 +#define B_CC_REG_UPDATE_KEY 0x3973 +#define B_CC_REG_JTAGID_L__A 0x2410019 +#define B_CC_REG_DIVERSITY__A 0x241001B +#define B_LC_COMM_EXEC__A 0x2800000 +#define B_LC_RA_RAM_IFINCR_NOM_L__A 0x282000C +#define B_LC_RA_RAM_FILTER_SYM_SET__A 0x282001A +#define B_LC_RA_RAM_FILTER_SYM_SET__PRE 0x3E8 +#define B_LC_RA_RAM_FILTER_CRMM_A__A 0x2820060 +#define B_LC_RA_RAM_FILTER_CRMM_A__PRE 0x4 +#define B_LC_RA_RAM_FILTER_CRMM_B__A 0x2820061 +#define B_LC_RA_RAM_FILTER_CRMM_B__PRE 0x1 +#define B_LC_RA_RAM_FILTER_SRMM_A__A 0x2820068 +#define B_LC_RA_RAM_FILTER_SRMM_A__PRE 0x4 +#define B_LC_RA_RAM_FILTER_SRMM_B__A 0x2820069 +#define B_LC_RA_RAM_FILTER_SRMM_B__PRE 0x1 + +#endif diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h new file mode 100644 index 0000000000..69fdca00f3 --- /dev/null +++ b/drivers/media/dvb-frontends/drxk.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _DRXK_H_ +#define _DRXK_H_ + +#include <linux/types.h> +#include <linux/i2c.h> + +/** + * struct drxk_config - Configure the initial parameters for DRX-K + * + * @adr: I2C address of the DRX-K + * @parallel_ts: True means that the device uses parallel TS, + * Serial otherwise. + * @dynamic_clk: True means that the clock will be dynamically + * adjusted. Static clock otherwise. + * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG. + * @single_master: Device is on the single master mode + * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner + * @antenna_gpio: GPIO bit used to control the antenna + * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1 + * means that 1=DVBC, 0 = DVBT. Zero means the opposite. + * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength. + * @chunk_size: maximum size for I2C messages + * @microcode_name: Name of the firmware file with the microcode + * @qam_demod_parameter_count: The number of parameters used for the command + * to set the demodulator parameters. All + * firmwares are using the 2-parameter command. + * An exception is the ``drxk_a3.mc`` firmware, + * which uses the 4-parameter command. + * A value of 0 (default) or lower indicates that + * the correct number of parameters will be + * automatically detected. + * + * On the ``*_gpio`` vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is + * UIO-3. + */ +struct drxk_config { + u8 adr; + bool single_master; + bool no_i2c_bridge; + bool parallel_ts; + bool dynamic_clk; + bool enable_merr_cfg; + + bool antenna_dvbt; + u16 antenna_gpio; + + u8 mpeg_out_clk_strength; + int chunk_size; + + const char *microcode_name; + int qam_demod_parameter_count; +}; + +#if IS_REACHABLE(CONFIG_DVB_DRXK) +/** + * drxk_attach - Attach a drxk demod + * + * @config: pointer to &struct drxk_config with demod configuration. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *drxk_attach(const struct drxk_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *drxk_attach(const struct drxk_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c new file mode 100644 index 0000000000..87f3d4f0eb --- /dev/null +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -0,0 +1,6821 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drxk_hard: DRX-K DVB-C/T demodulator driver + * + * Copyright (C) 2010-2011 Digital Devices GmbH + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/hardirq.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "drxk.h" +#include "drxk_hard.h" +#include <linux/int_log.h> + +static int power_down_dvbt(struct drxk_state *state, bool set_power_mode); +static int power_down_qam(struct drxk_state *state); +static int set_dvbt_standard(struct drxk_state *state, + enum operation_mode o_mode); +static int set_qam_standard(struct drxk_state *state, + enum operation_mode o_mode); +static int set_qam(struct drxk_state *state, u16 intermediate_freqk_hz, + s32 tuner_freq_offset); +static int set_dvbt_standard(struct drxk_state *state, + enum operation_mode o_mode); +static int dvbt_start(struct drxk_state *state); +static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, + s32 tuner_freq_offset); +static int get_qam_lock_status(struct drxk_state *state, u32 *p_lock_status); +static int get_dvbt_lock_status(struct drxk_state *state, u32 *p_lock_status); +static int switch_antenna_to_qam(struct drxk_state *state); +static int switch_antenna_to_dvbt(struct drxk_state *state); + +static bool is_dvbt(struct drxk_state *state) +{ + return state->m_operation_mode == OM_DVBT; +} + +static bool is_qam(struct drxk_state *state) +{ + return state->m_operation_mode == OM_QAM_ITU_A || + state->m_operation_mode == OM_QAM_ITU_B || + state->m_operation_mode == OM_QAM_ITU_C; +} + +#define NOA1ROM 0 + +#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0) +#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0) + +#define DEFAULT_MER_83 165 +#define DEFAULT_MER_93 250 + +#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH +#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02) +#endif + +#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH +#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03) +#endif + +#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700 +#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500 + +#ifndef DRXK_KI_RAGC_ATV +#define DRXK_KI_RAGC_ATV 4 +#endif +#ifndef DRXK_KI_IAGC_ATV +#define DRXK_KI_IAGC_ATV 6 +#endif +#ifndef DRXK_KI_DAGC_ATV +#define DRXK_KI_DAGC_ATV 7 +#endif + +#ifndef DRXK_KI_RAGC_QAM +#define DRXK_KI_RAGC_QAM 3 +#endif +#ifndef DRXK_KI_IAGC_QAM +#define DRXK_KI_IAGC_QAM 4 +#endif +#ifndef DRXK_KI_DAGC_QAM +#define DRXK_KI_DAGC_QAM 7 +#endif +#ifndef DRXK_KI_RAGC_DVBT +#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2) +#endif +#ifndef DRXK_KI_IAGC_DVBT +#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2) +#endif +#ifndef DRXK_KI_DAGC_DVBT +#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7) +#endif + +#ifndef DRXK_AGC_DAC_OFFSET +#define DRXK_AGC_DAC_OFFSET (0x800) +#endif + +#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ +#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L) +#endif + +#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ +#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L) +#endif + +#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ +#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L) +#endif + +#ifndef DRXK_QAM_SYMBOLRATE_MAX +#define DRXK_QAM_SYMBOLRATE_MAX (7233000) +#endif + +#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56 +#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64 +#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0 +#define DRXK_BL_ROM_OFFSET_TAPS_BG 24 +#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32 +#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40 +#define DRXK_BL_ROM_OFFSET_TAPS_FM 48 +#define DRXK_BL_ROM_OFFSET_UCODE 0 + +#define DRXK_BLC_TIMEOUT 100 + +#define DRXK_BLCC_NR_ELEMENTS_TAPS 2 +#define DRXK_BLCC_NR_ELEMENTS_UCODE 6 + +#define DRXK_BLDC_NR_ELEMENTS_TAPS 28 + +#ifndef DRXK_OFDM_NE_NOTCH_WIDTH +#define DRXK_OFDM_NE_NOTCH_WIDTH (4) +#endif + +#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960) +#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480) +#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008) +#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992) +#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520) + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \ +} while (0) + +static inline u32 Frac28a(u32 a, u32 c) +{ + int i = 0; + u32 Q1 = 0; + u32 R0 = 0; + + R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */ + Q1 = a / c; /* + * 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 / c); + R0 = (R0 % c) << 4; + } + /* rounding */ + if ((R0 >> 3) >= c) + Q1++; + + return Q1; +} + +static inline u32 log10times100(u32 value) +{ + return (100L * intlog10(value)) >> 24; +} + +/***************************************************************************/ +/* I2C **********************************************************************/ +/***************************************************************************/ + +static int drxk_i2c_lock(struct drxk_state *state) +{ + i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT); + state->drxk_i2c_exclusive_lock = true; + + return 0; +} + +static void drxk_i2c_unlock(struct drxk_state *state) +{ + if (!state->drxk_i2c_exclusive_lock) + return; + + i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT); + state->drxk_i2c_exclusive_lock = false; +} + +static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs, + unsigned len) +{ + if (state->drxk_i2c_exclusive_lock) + return __i2c_transfer(state->i2c, msgs, len); + else + return i2c_transfer(state->i2c, msgs, len); +} + +static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val) +{ + struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = 1} + }; + + return drxk_i2c_transfer(state, msgs, 1); +} + +static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len) +{ + int status; + struct i2c_msg msg = { + .addr = adr, .flags = 0, .buf = data, .len = len }; + + dprintk(3, ": %*ph\n", len, data); + + status = drxk_i2c_transfer(state, &msg, 1); + if (status >= 0 && status != 1) + status = -EIO; + + if (status < 0) + pr_err("i2c write error at addr 0x%02x\n", adr); + + return status; +} + +static int i2c_read(struct drxk_state *state, + u8 adr, u8 *msg, int len, u8 *answ, int alen) +{ + int status; + struct i2c_msg msgs[2] = { + {.addr = adr, .flags = 0, + .buf = msg, .len = len}, + {.addr = adr, .flags = I2C_M_RD, + .buf = answ, .len = alen} + }; + + status = drxk_i2c_transfer(state, msgs, 2); + if (status != 2) { + if (debug > 2) + pr_cont(": ERROR!\n"); + if (status >= 0) + status = -EIO; + + pr_err("i2c read error at addr 0x%02x\n", adr); + return status; + } + dprintk(3, ": read from %*ph, value = %*ph\n", len, msg, alen, answ); + return 0; +} + +static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags) +{ + int status; + u8 adr = state->demod_address, mm1[4], mm2[2], len; + + if (state->single_master) + flags |= 0xC0; + + if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { + mm1[0] = (((reg << 1) & 0xFF) | 0x01); + mm1[1] = ((reg >> 16) & 0xFF); + mm1[2] = ((reg >> 24) & 0xFF) | flags; + mm1[3] = ((reg >> 7) & 0xFF); + len = 4; + } else { + mm1[0] = ((reg << 1) & 0xFF); + mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); + len = 2; + } + dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); + status = i2c_read(state, adr, mm1, len, mm2, 2); + if (status < 0) + return status; + if (data) + *data = mm2[0] | (mm2[1] << 8); + + return 0; +} + +static int read16(struct drxk_state *state, u32 reg, u16 *data) +{ + return read16_flags(state, reg, data, 0); +} + +static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags) +{ + int status; + u8 adr = state->demod_address, mm1[4], mm2[4], len; + + if (state->single_master) + flags |= 0xC0; + + if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { + mm1[0] = (((reg << 1) & 0xFF) | 0x01); + mm1[1] = ((reg >> 16) & 0xFF); + mm1[2] = ((reg >> 24) & 0xFF) | flags; + mm1[3] = ((reg >> 7) & 0xFF); + len = 4; + } else { + mm1[0] = ((reg << 1) & 0xFF); + mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); + len = 2; + } + dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); + status = i2c_read(state, adr, mm1, len, mm2, 4); + if (status < 0) + return status; + if (data) + *data = mm2[0] | (mm2[1] << 8) | + (mm2[2] << 16) | (mm2[3] << 24); + + return 0; +} + +static int read32(struct drxk_state *state, u32 reg, u32 *data) +{ + return read32_flags(state, reg, data, 0); +} + +static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags) +{ + u8 adr = state->demod_address, mm[6], len; + + if (state->single_master) + flags |= 0xC0; + if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { + mm[0] = (((reg << 1) & 0xFF) | 0x01); + mm[1] = ((reg >> 16) & 0xFF); + mm[2] = ((reg >> 24) & 0xFF) | flags; + mm[3] = ((reg >> 7) & 0xFF); + len = 4; + } else { + mm[0] = ((reg << 1) & 0xFF); + mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); + len = 2; + } + mm[len] = data & 0xff; + mm[len + 1] = (data >> 8) & 0xff; + + dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags); + return i2c_write(state, adr, mm, len + 2); +} + +static int write16(struct drxk_state *state, u32 reg, u16 data) +{ + return write16_flags(state, reg, data, 0); +} + +static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags) +{ + u8 adr = state->demod_address, mm[8], len; + + if (state->single_master) + flags |= 0xC0; + if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { + mm[0] = (((reg << 1) & 0xFF) | 0x01); + mm[1] = ((reg >> 16) & 0xFF); + mm[2] = ((reg >> 24) & 0xFF) | flags; + mm[3] = ((reg >> 7) & 0xFF); + len = 4; + } else { + mm[0] = ((reg << 1) & 0xFF); + mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); + len = 2; + } + mm[len] = data & 0xff; + mm[len + 1] = (data >> 8) & 0xff; + mm[len + 2] = (data >> 16) & 0xff; + mm[len + 3] = (data >> 24) & 0xff; + dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags); + + return i2c_write(state, adr, mm, len + 4); +} + +static int write32(struct drxk_state *state, u32 reg, u32 data) +{ + return write32_flags(state, reg, data, 0); +} + +static int write_block(struct drxk_state *state, u32 address, + const int block_size, const u8 p_block[]) +{ + int status = 0, blk_size = block_size; + u8 flags = 0; + + if (state->single_master) + flags |= 0xC0; + + while (blk_size > 0) { + int chunk = blk_size > state->m_chunk_size ? + state->m_chunk_size : blk_size; + u8 *adr_buf = &state->chunk[0]; + u32 adr_length = 0; + + if (DRXDAP_FASI_LONG_FORMAT(address) || (flags != 0)) { + adr_buf[0] = (((address << 1) & 0xFF) | 0x01); + adr_buf[1] = ((address >> 16) & 0xFF); + adr_buf[2] = ((address >> 24) & 0xFF); + adr_buf[3] = ((address >> 7) & 0xFF); + adr_buf[2] |= flags; + adr_length = 4; + if (chunk == state->m_chunk_size) + chunk -= 2; + } else { + adr_buf[0] = ((address << 1) & 0xFF); + adr_buf[1] = (((address >> 16) & 0x0F) | + ((address >> 18) & 0xF0)); + adr_length = 2; + } + memcpy(&state->chunk[adr_length], p_block, chunk); + dprintk(2, "(0x%08x, 0x%02x)\n", address, flags); + if (p_block) + dprintk(2, "%*ph\n", chunk, p_block); + status = i2c_write(state, state->demod_address, + &state->chunk[0], chunk + adr_length); + if (status < 0) { + pr_err("%s: i2c write error at addr 0x%02x\n", + __func__, address); + break; + } + p_block += chunk; + address += (chunk >> 1); + blk_size -= chunk; + } + return status; +} + +#ifndef DRXK_MAX_RETRIES_POWERUP +#define DRXK_MAX_RETRIES_POWERUP 20 +#endif + +static int power_up_device(struct drxk_state *state) +{ + int status; + u8 data = 0; + u16 retry_count = 0; + + dprintk(1, "\n"); + + status = i2c_read1(state, state->demod_address, &data); + if (status < 0) { + do { + data = 0; + status = i2c_write(state, state->demod_address, + &data, 1); + usleep_range(10000, 11000); + retry_count++; + if (status < 0) + continue; + status = i2c_read1(state, state->demod_address, + &data); + } while (status < 0 && + (retry_count < DRXK_MAX_RETRIES_POWERUP)); + if (status < 0 && retry_count >= DRXK_MAX_RETRIES_POWERUP) + goto error; + } + + /* Make sure all clk domains are active */ + status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE); + if (status < 0) + goto error; + status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); + if (status < 0) + goto error; + /* Enable pll lock tests */ + status = write16(state, SIO_CC_PLL_LOCK__A, 1); + if (status < 0) + goto error; + + state->m_current_power_mode = DRX_POWER_UP; + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + + +static int init_state(struct drxk_state *state) +{ + /* + * FIXME: most (all?) of the values below should be moved into + * struct drxk_config, as they are probably board-specific + */ + u32 ul_vsb_if_agc_mode = DRXK_AGC_CTRL_AUTO; + u32 ul_vsb_if_agc_output_level = 0; + u32 ul_vsb_if_agc_min_level = 0; + u32 ul_vsb_if_agc_max_level = 0x7FFF; + u32 ul_vsb_if_agc_speed = 3; + + u32 ul_vsb_rf_agc_mode = DRXK_AGC_CTRL_AUTO; + u32 ul_vsb_rf_agc_output_level = 0; + u32 ul_vsb_rf_agc_min_level = 0; + u32 ul_vsb_rf_agc_max_level = 0x7FFF; + u32 ul_vsb_rf_agc_speed = 3; + u32 ul_vsb_rf_agc_top = 9500; + u32 ul_vsb_rf_agc_cut_off_current = 4000; + + u32 ul_atv_if_agc_mode = DRXK_AGC_CTRL_AUTO; + u32 ul_atv_if_agc_output_level = 0; + u32 ul_atv_if_agc_min_level = 0; + u32 ul_atv_if_agc_max_level = 0; + u32 ul_atv_if_agc_speed = 3; + + u32 ul_atv_rf_agc_mode = DRXK_AGC_CTRL_OFF; + u32 ul_atv_rf_agc_output_level = 0; + u32 ul_atv_rf_agc_min_level = 0; + u32 ul_atv_rf_agc_max_level = 0; + u32 ul_atv_rf_agc_top = 9500; + u32 ul_atv_rf_agc_cut_off_current = 4000; + u32 ul_atv_rf_agc_speed = 3; + + u32 ulQual83 = DEFAULT_MER_83; + u32 ulQual93 = DEFAULT_MER_93; + + u32 ul_mpeg_lock_time_out = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; + u32 ul_demod_lock_time_out = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; + + /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ + /* io_pad_cfg_mode output mode is drive always */ + /* io_pad_cfg_drive is set to power 2 (23 mA) */ + u32 ul_gpio_cfg = 0x0113; + u32 ul_invert_ts_clock = 0; + u32 ul_ts_data_strength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH; + u32 ul_dvbt_bitrate = 50000000; + u32 ul_dvbc_bitrate = DRXK_QAM_SYMBOLRATE_MAX * 8; + + u32 ul_insert_rs_byte = 0; + + u32 ul_rf_mirror = 1; + u32 ul_power_down = 0; + + dprintk(1, "\n"); + + state->m_has_lna = false; + state->m_has_dvbt = false; + state->m_has_dvbc = false; + state->m_has_atv = false; + state->m_has_oob = false; + state->m_has_audio = false; + + if (!state->m_chunk_size) + state->m_chunk_size = 124; + + state->m_osc_clock_freq = 0; + state->m_smart_ant_inverted = false; + state->m_b_p_down_open_bridge = false; + + /* real system clock frequency in kHz */ + state->m_sys_clock_freq = 151875; + /* Timing div, 250ns/Psys */ + /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */ + state->m_hi_cfg_timing_div = ((state->m_sys_clock_freq / 1000) * + HI_I2C_DELAY) / 1000; + /* Clipping */ + if (state->m_hi_cfg_timing_div > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M) + state->m_hi_cfg_timing_div = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M; + state->m_hi_cfg_wake_up_key = (state->demod_address << 1); + /* port/bridge/power down ctrl */ + state->m_hi_cfg_ctrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE; + + state->m_b_power_down = (ul_power_down != 0); + + state->m_drxk_a3_patch_code = false; + + /* Init AGC and PGA parameters */ + /* VSB IF */ + state->m_vsb_if_agc_cfg.ctrl_mode = ul_vsb_if_agc_mode; + state->m_vsb_if_agc_cfg.output_level = ul_vsb_if_agc_output_level; + state->m_vsb_if_agc_cfg.min_output_level = ul_vsb_if_agc_min_level; + state->m_vsb_if_agc_cfg.max_output_level = ul_vsb_if_agc_max_level; + state->m_vsb_if_agc_cfg.speed = ul_vsb_if_agc_speed; + state->m_vsb_pga_cfg = 140; + + /* VSB RF */ + state->m_vsb_rf_agc_cfg.ctrl_mode = ul_vsb_rf_agc_mode; + state->m_vsb_rf_agc_cfg.output_level = ul_vsb_rf_agc_output_level; + state->m_vsb_rf_agc_cfg.min_output_level = ul_vsb_rf_agc_min_level; + state->m_vsb_rf_agc_cfg.max_output_level = ul_vsb_rf_agc_max_level; + state->m_vsb_rf_agc_cfg.speed = ul_vsb_rf_agc_speed; + state->m_vsb_rf_agc_cfg.top = ul_vsb_rf_agc_top; + state->m_vsb_rf_agc_cfg.cut_off_current = ul_vsb_rf_agc_cut_off_current; + state->m_vsb_pre_saw_cfg.reference = 0x07; + state->m_vsb_pre_saw_cfg.use_pre_saw = true; + + state->m_Quality83percent = DEFAULT_MER_83; + state->m_Quality93percent = DEFAULT_MER_93; + if (ulQual93 <= 500 && ulQual83 < ulQual93) { + state->m_Quality83percent = ulQual83; + state->m_Quality93percent = ulQual93; + } + + /* ATV IF */ + state->m_atv_if_agc_cfg.ctrl_mode = ul_atv_if_agc_mode; + state->m_atv_if_agc_cfg.output_level = ul_atv_if_agc_output_level; + state->m_atv_if_agc_cfg.min_output_level = ul_atv_if_agc_min_level; + state->m_atv_if_agc_cfg.max_output_level = ul_atv_if_agc_max_level; + state->m_atv_if_agc_cfg.speed = ul_atv_if_agc_speed; + + /* ATV RF */ + state->m_atv_rf_agc_cfg.ctrl_mode = ul_atv_rf_agc_mode; + state->m_atv_rf_agc_cfg.output_level = ul_atv_rf_agc_output_level; + state->m_atv_rf_agc_cfg.min_output_level = ul_atv_rf_agc_min_level; + state->m_atv_rf_agc_cfg.max_output_level = ul_atv_rf_agc_max_level; + state->m_atv_rf_agc_cfg.speed = ul_atv_rf_agc_speed; + state->m_atv_rf_agc_cfg.top = ul_atv_rf_agc_top; + state->m_atv_rf_agc_cfg.cut_off_current = ul_atv_rf_agc_cut_off_current; + state->m_atv_pre_saw_cfg.reference = 0x04; + state->m_atv_pre_saw_cfg.use_pre_saw = true; + + + /* DVBT RF */ + state->m_dvbt_rf_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_OFF; + state->m_dvbt_rf_agc_cfg.output_level = 0; + state->m_dvbt_rf_agc_cfg.min_output_level = 0; + state->m_dvbt_rf_agc_cfg.max_output_level = 0xFFFF; + state->m_dvbt_rf_agc_cfg.top = 0x2100; + state->m_dvbt_rf_agc_cfg.cut_off_current = 4000; + state->m_dvbt_rf_agc_cfg.speed = 1; + + + /* DVBT IF */ + state->m_dvbt_if_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_AUTO; + state->m_dvbt_if_agc_cfg.output_level = 0; + state->m_dvbt_if_agc_cfg.min_output_level = 0; + state->m_dvbt_if_agc_cfg.max_output_level = 9000; + state->m_dvbt_if_agc_cfg.top = 13424; + state->m_dvbt_if_agc_cfg.cut_off_current = 0; + state->m_dvbt_if_agc_cfg.speed = 3; + state->m_dvbt_if_agc_cfg.fast_clip_ctrl_delay = 30; + state->m_dvbt_if_agc_cfg.ingain_tgt_max = 30000; + /* state->m_dvbtPgaCfg = 140; */ + + state->m_dvbt_pre_saw_cfg.reference = 4; + state->m_dvbt_pre_saw_cfg.use_pre_saw = false; + + /* QAM RF */ + state->m_qam_rf_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_OFF; + state->m_qam_rf_agc_cfg.output_level = 0; + state->m_qam_rf_agc_cfg.min_output_level = 6023; + state->m_qam_rf_agc_cfg.max_output_level = 27000; + state->m_qam_rf_agc_cfg.top = 0x2380; + state->m_qam_rf_agc_cfg.cut_off_current = 4000; + state->m_qam_rf_agc_cfg.speed = 3; + + /* QAM IF */ + state->m_qam_if_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_AUTO; + state->m_qam_if_agc_cfg.output_level = 0; + state->m_qam_if_agc_cfg.min_output_level = 0; + state->m_qam_if_agc_cfg.max_output_level = 9000; + state->m_qam_if_agc_cfg.top = 0x0511; + state->m_qam_if_agc_cfg.cut_off_current = 0; + state->m_qam_if_agc_cfg.speed = 3; + state->m_qam_if_agc_cfg.ingain_tgt_max = 5119; + state->m_qam_if_agc_cfg.fast_clip_ctrl_delay = 50; + + state->m_qam_pga_cfg = 140; + state->m_qam_pre_saw_cfg.reference = 4; + state->m_qam_pre_saw_cfg.use_pre_saw = false; + + state->m_operation_mode = OM_NONE; + state->m_drxk_state = DRXK_UNINITIALIZED; + + /* MPEG output configuration */ + state->m_enable_mpeg_output = true; /* If TRUE; enable MPEG output */ + state->m_insert_rs_byte = false; /* If TRUE; insert RS byte */ + state->m_invert_data = false; /* If TRUE; invert DATA signals */ + state->m_invert_err = false; /* If TRUE; invert ERR signal */ + state->m_invert_str = false; /* If TRUE; invert STR signals */ + state->m_invert_val = false; /* If TRUE; invert VAL signals */ + state->m_invert_clk = (ul_invert_ts_clock != 0); /* If TRUE; invert CLK signals */ + + /* If TRUE; static MPEG clockrate will be used; + otherwise clockrate will adapt to the bitrate of the TS */ + + state->m_dvbt_bitrate = ul_dvbt_bitrate; + state->m_dvbc_bitrate = ul_dvbc_bitrate; + + state->m_ts_data_strength = (ul_ts_data_strength & 0x07); + + /* Maximum bitrate in b/s in case static clockrate is selected */ + state->m_mpeg_ts_static_bitrate = 19392658; + state->m_disable_te_ihandling = false; + + if (ul_insert_rs_byte) + state->m_insert_rs_byte = true; + + state->m_mpeg_lock_time_out = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; + if (ul_mpeg_lock_time_out < 10000) + state->m_mpeg_lock_time_out = ul_mpeg_lock_time_out; + state->m_demod_lock_time_out = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; + if (ul_demod_lock_time_out < 10000) + state->m_demod_lock_time_out = ul_demod_lock_time_out; + + /* QAM defaults */ + state->m_constellation = DRX_CONSTELLATION_AUTO; + state->m_qam_interleave_mode = DRXK_QAM_I12_J17; + state->m_fec_rs_plen = 204 * 8; /* fecRsPlen annex A */ + state->m_fec_rs_prescale = 1; + + state->m_sqi_speed = DRXK_DVBT_SQI_SPEED_MEDIUM; + state->m_agcfast_clip_ctrl_delay = 0; + + state->m_gpio_cfg = ul_gpio_cfg; + + state->m_b_power_down = false; + state->m_current_power_mode = DRX_POWER_DOWN; + + state->m_rfmirror = (ul_rf_mirror == 0); + state->m_if_agc_pol = false; + return 0; +} + +static int drxx_open(struct drxk_state *state) +{ + int status = 0; + u32 jtag = 0; + u16 bid = 0; + u16 key = 0; + + dprintk(1, "\n"); + /* stop lock indicator process */ + status = write16(state, SCU_RAM_GPIO__A, + SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); + if (status < 0) + goto error; + /* Check device id */ + status = read16(state, SIO_TOP_COMM_KEY__A, &key); + if (status < 0) + goto error; + status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); + if (status < 0) + goto error; + status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag); + if (status < 0) + goto error; + status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid); + if (status < 0) + goto error; + status = write16(state, SIO_TOP_COMM_KEY__A, key); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int get_device_capabilities(struct drxk_state *state) +{ + u16 sio_pdr_ohw_cfg = 0; + u32 sio_top_jtagid_lo = 0; + int status; + const char *spin = ""; + + dprintk(1, "\n"); + + /* driver 0.9.0 */ + /* stop lock indicator process */ + status = write16(state, SCU_RAM_GPIO__A, + SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); + if (status < 0) + goto error; + status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); + if (status < 0) + goto error; + status = read16(state, SIO_PDR_OHW_CFG__A, &sio_pdr_ohw_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); + if (status < 0) + goto error; + + switch ((sio_pdr_ohw_cfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) { + case 0: + /* ignore (bypass ?) */ + break; + case 1: + /* 27 MHz */ + state->m_osc_clock_freq = 27000; + break; + case 2: + /* 20.25 MHz */ + state->m_osc_clock_freq = 20250; + break; + case 3: + /* 4 MHz */ + state->m_osc_clock_freq = 20250; + break; + default: + pr_err("Clock Frequency is unknown\n"); + return -EINVAL; + } + /* + Determine device capabilities + Based on pinning v14 + */ + status = read32(state, SIO_TOP_JTAGID_LO__A, &sio_top_jtagid_lo); + if (status < 0) + goto error; + + pr_info("status = 0x%08x\n", sio_top_jtagid_lo); + + /* driver 0.9.0 */ + switch ((sio_top_jtagid_lo >> 29) & 0xF) { + case 0: + state->m_device_spin = DRXK_SPIN_A1; + spin = "A1"; + break; + case 2: + state->m_device_spin = DRXK_SPIN_A2; + spin = "A2"; + break; + case 3: + state->m_device_spin = DRXK_SPIN_A3; + spin = "A3"; + break; + default: + state->m_device_spin = DRXK_SPIN_UNKNOWN; + status = -EINVAL; + pr_err("Spin %d unknown\n", (sio_top_jtagid_lo >> 29) & 0xF); + goto error2; + } + switch ((sio_top_jtagid_lo >> 12) & 0xFF) { + case 0x13: + /* typeId = DRX3913K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = false; + state->m_has_audio = false; + state->m_has_dvbt = true; + state->m_has_dvbc = true; + state->m_has_sawsw = true; + state->m_has_gpio2 = false; + state->m_has_gpio1 = false; + state->m_has_irqn = false; + break; + case 0x15: + /* typeId = DRX3915K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = false; + state->m_has_dvbt = true; + state->m_has_dvbc = false; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + case 0x16: + /* typeId = DRX3916K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = false; + state->m_has_dvbt = true; + state->m_has_dvbc = false; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + case 0x18: + /* typeId = DRX3918K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = true; + state->m_has_dvbt = true; + state->m_has_dvbc = false; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + case 0x21: + /* typeId = DRX3921K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = true; + state->m_has_dvbt = true; + state->m_has_dvbc = true; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + case 0x23: + /* typeId = DRX3923K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = true; + state->m_has_dvbt = true; + state->m_has_dvbc = true; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + case 0x25: + /* typeId = DRX3925K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = true; + state->m_has_dvbt = true; + state->m_has_dvbc = true; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + case 0x26: + /* typeId = DRX3926K_TYPE_ID */ + state->m_has_lna = false; + state->m_has_oob = false; + state->m_has_atv = true; + state->m_has_audio = false; + state->m_has_dvbt = true; + state->m_has_dvbc = true; + state->m_has_sawsw = true; + state->m_has_gpio2 = true; + state->m_has_gpio1 = true; + state->m_has_irqn = false; + break; + default: + pr_err("DeviceID 0x%02x not supported\n", + ((sio_top_jtagid_lo >> 12) & 0xFF)); + status = -EINVAL; + goto error2; + } + + pr_info("detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n", + ((sio_top_jtagid_lo >> 12) & 0xFF), spin, + state->m_osc_clock_freq / 1000, + state->m_osc_clock_freq % 1000); + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + +error2: + return status; +} + +static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result) +{ + int status; + bool powerdown_cmd; + + dprintk(1, "\n"); + + /* Write command */ + status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd); + if (status < 0) + goto error; + if (cmd == SIO_HI_RA_RAM_CMD_RESET) + usleep_range(1000, 2000); + + powerdown_cmd = + (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) && + ((state->m_hi_cfg_ctrl) & + 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 */ + u32 retry_count = 0; + u16 wait_cmd; + + do { + usleep_range(1000, 2000); + retry_count += 1; + status = read16(state, SIO_HI_RA_RAM_CMD__A, + &wait_cmd); + } while ((status < 0 || wait_cmd) && (retry_count < DRXK_MAX_RETRIES)); + if (status < 0) + goto error; + status = read16(state, SIO_HI_RA_RAM_RES__A, p_result); + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int hi_cfg_command(struct drxk_state *state) +{ + int status; + + dprintk(1, "\n"); + + mutex_lock(&state->mutex); + + status = write16(state, SIO_HI_RA_RAM_PAR_6__A, + state->m_hi_cfg_timeout); + if (status < 0) + goto error; + status = write16(state, SIO_HI_RA_RAM_PAR_5__A, + state->m_hi_cfg_ctrl); + if (status < 0) + goto error; + status = write16(state, SIO_HI_RA_RAM_PAR_4__A, + state->m_hi_cfg_wake_up_key); + if (status < 0) + goto error; + status = write16(state, SIO_HI_RA_RAM_PAR_3__A, + state->m_hi_cfg_bridge_delay); + if (status < 0) + goto error; + status = write16(state, SIO_HI_RA_RAM_PAR_2__A, + state->m_hi_cfg_timing_div); + if (status < 0) + goto error; + status = write16(state, SIO_HI_RA_RAM_PAR_1__A, + SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY); + if (status < 0) + goto error; + status = hi_command(state, SIO_HI_RA_RAM_CMD_CONFIG, NULL); + if (status < 0) + goto error; + + state->m_hi_cfg_ctrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; +error: + mutex_unlock(&state->mutex); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int init_hi(struct drxk_state *state) +{ + dprintk(1, "\n"); + + state->m_hi_cfg_wake_up_key = (state->demod_address << 1); + state->m_hi_cfg_timeout = 0x96FF; + /* port/bridge/power down ctrl */ + state->m_hi_cfg_ctrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE; + + return hi_cfg_command(state); +} + +static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable) +{ + int status; + u16 sio_pdr_mclk_cfg = 0; + u16 sio_pdr_mdx_cfg = 0; + u16 err_cfg = 0; + + dprintk(1, ": mpeg %s, %s mode\n", + mpeg_enable ? "enable" : "disable", + state->m_enable_parallel ? "parallel" : "serial"); + + /* stop lock indicator process */ + status = write16(state, SCU_RAM_GPIO__A, + SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); + if (status < 0) + goto error; + + /* MPEG TS pad configuration */ + status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); + if (status < 0) + goto error; + + if (!mpeg_enable) { + /* Set MPEG TS pads to inputmode */ + status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000); + if (status < 0) + goto error; + } else { + /* Enable MPEG output */ + sio_pdr_mdx_cfg = + ((state->m_ts_data_strength << + SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003); + sio_pdr_mclk_cfg = ((state->m_ts_clockk_strength << + SIO_PDR_MCLK_CFG_DRIVE__B) | + 0x0003); + + status = write16(state, SIO_PDR_MSTRT_CFG__A, sio_pdr_mdx_cfg); + if (status < 0) + goto error; + + if (state->enable_merr_cfg) + err_cfg = sio_pdr_mdx_cfg; + + status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg); + if (status < 0) + goto error; + + if (state->m_enable_parallel) { + /* parallel -> enable MD1 to MD7 */ + status = write16(state, SIO_PDR_MD1_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD2_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD3_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD4_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD5_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD6_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD7_CFG__A, + sio_pdr_mdx_cfg); + if (status < 0) + goto error; + } else { + sio_pdr_mdx_cfg = ((state->m_ts_data_strength << + SIO_PDR_MD0_CFG_DRIVE__B) + | 0x0003); + /* serial -> disable MD1 to MD7 */ + status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000); + if (status < 0) + goto error; + } + status = write16(state, SIO_PDR_MCLK_CFG__A, sio_pdr_mclk_cfg); + if (status < 0) + goto error; + status = write16(state, SIO_PDR_MD0_CFG__A, sio_pdr_mdx_cfg); + if (status < 0) + goto error; + } + /* Enable MB output over MPEG pads and ctl input */ + status = write16(state, SIO_PDR_MON_CFG__A, 0x0000); + if (status < 0) + goto error; + /* Write nomagic word to enable pdr reg write */ + status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int mpegts_disable(struct drxk_state *state) +{ + dprintk(1, "\n"); + + return mpegts_configure_pins(state, false); +} + +static int bl_chain_cmd(struct drxk_state *state, + u16 rom_offset, u16 nr_of_elements, u32 time_out) +{ + u16 bl_status = 0; + int status; + unsigned long end; + + dprintk(1, "\n"); + mutex_lock(&state->mutex); + status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN); + if (status < 0) + goto error; + status = write16(state, SIO_BL_CHAIN_ADDR__A, rom_offset); + if (status < 0) + goto error; + status = write16(state, SIO_BL_CHAIN_LEN__A, nr_of_elements); + if (status < 0) + goto error; + status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON); + if (status < 0) + goto error; + + end = jiffies + msecs_to_jiffies(time_out); + do { + usleep_range(1000, 2000); + status = read16(state, SIO_BL_STATUS__A, &bl_status); + if (status < 0) + goto error; + } while ((bl_status == 0x1) && + ((time_is_after_jiffies(end)))); + + if (bl_status == 0x1) { + pr_err("SIO not ready\n"); + status = -EINVAL; + goto error2; + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); +error2: + mutex_unlock(&state->mutex); + return status; +} + + +static int download_microcode(struct drxk_state *state, + const u8 p_mc_image[], u32 length) +{ + const u8 *p_src = p_mc_image; + u32 address; + u16 n_blocks; + u16 block_size; + u32 offset = 0; + u32 i; + int status = 0; + + dprintk(1, "\n"); + + /* down the drain (we don't care about MAGIC_WORD) */ +#if 0 + /* For future reference */ + drain = (p_src[0] << 8) | p_src[1]; +#endif + p_src += sizeof(u16); + offset += sizeof(u16); + n_blocks = (p_src[0] << 8) | p_src[1]; + p_src += sizeof(u16); + offset += sizeof(u16); + + for (i = 0; i < n_blocks; i += 1) { + address = (p_src[0] << 24) | (p_src[1] << 16) | + (p_src[2] << 8) | p_src[3]; + p_src += sizeof(u32); + offset += sizeof(u32); + + block_size = ((p_src[0] << 8) | p_src[1]) * sizeof(u16); + p_src += sizeof(u16); + offset += sizeof(u16); + +#if 0 + /* For future reference */ + flags = (p_src[0] << 8) | p_src[1]; +#endif + p_src += sizeof(u16); + offset += sizeof(u16); + +#if 0 + /* For future reference */ + block_crc = (p_src[0] << 8) | p_src[1]; +#endif + p_src += sizeof(u16); + offset += sizeof(u16); + + if (offset + block_size > length) { + pr_err("Firmware is corrupted.\n"); + return -EINVAL; + } + + status = write_block(state, address, block_size, p_src); + if (status < 0) { + pr_err("Error %d while loading firmware\n", status); + break; + } + p_src += block_size; + offset += block_size; + } + return status; +} + +static int dvbt_enable_ofdm_token_ring(struct drxk_state *state, bool enable) +{ + int status; + u16 data = 0; + u16 desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON; + u16 desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED; + unsigned long end; + + dprintk(1, "\n"); + + if (!enable) { + desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF; + desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN; + } + + status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data); + if (status >= 0 && data == desired_status) { + /* tokenring already has correct status */ + return status; + } + /* Disable/enable dvbt tokenring bridge */ + status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desired_ctrl); + + end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT); + do { + status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data); + if ((status >= 0 && data == desired_status) + || time_is_after_jiffies(end)) + break; + usleep_range(1000, 2000); + } while (1); + if (data != desired_status) { + pr_err("SIO not ready\n"); + return -EINVAL; + } + return status; +} + +static int mpegts_stop(struct drxk_state *state) +{ + int status = 0; + u16 fec_oc_snc_mode = 0; + u16 fec_oc_ipr_mode = 0; + + dprintk(1, "\n"); + + /* Graceful shutdown (byte boundaries) */ + status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode); + if (status < 0) + goto error; + fec_oc_snc_mode |= FEC_OC_SNC_MODE_SHUTDOWN__M; + status = write16(state, FEC_OC_SNC_MODE__A, fec_oc_snc_mode); + if (status < 0) + goto error; + + /* Suppress MCLK during absence of data */ + status = read16(state, FEC_OC_IPR_MODE__A, &fec_oc_ipr_mode); + if (status < 0) + goto error; + fec_oc_ipr_mode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M; + status = write16(state, FEC_OC_IPR_MODE__A, fec_oc_ipr_mode); + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int scu_command(struct drxk_state *state, + u16 cmd, u8 parameter_len, + u16 *parameter, u8 result_len, u16 *result) +{ +#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15 +#error DRXK register mapping no longer compatible with this routine! +#endif + u16 cur_cmd = 0; + int status = -EINVAL; + unsigned long end; + u8 buffer[34]; + int cnt = 0, ii; + const char *p; + char errname[30]; + + dprintk(1, "\n"); + + if ((cmd == 0) || ((parameter_len > 0) && (parameter == NULL)) || + ((result_len > 0) && (result == NULL))) { + pr_err("Error %d on %s\n", status, __func__); + return status; + } + + mutex_lock(&state->mutex); + + /* assume that the command register is ready + since it is checked afterwards */ + if (parameter) { + for (ii = parameter_len - 1; ii >= 0; ii -= 1) { + buffer[cnt++] = (parameter[ii] & 0xFF); + buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF); + } + } + buffer[cnt++] = (cmd & 0xFF); + buffer[cnt++] = ((cmd >> 8) & 0xFF); + + write_block(state, SCU_RAM_PARAM_0__A - + (parameter_len - 1), cnt, buffer); + /* Wait until SCU has processed command */ + end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME); + do { + usleep_range(1000, 2000); + status = read16(state, SCU_RAM_COMMAND__A, &cur_cmd); + if (status < 0) + goto error; + } while (!(cur_cmd == DRX_SCU_READY) && (time_is_after_jiffies(end))); + if (cur_cmd != DRX_SCU_READY) { + pr_err("SCU not ready\n"); + status = -EIO; + goto error2; + } + /* read results */ + if ((result_len > 0) && (result != NULL)) { + s16 err; + int ii; + + for (ii = result_len - 1; ii >= 0; ii -= 1) { + status = read16(state, SCU_RAM_PARAM_0__A - ii, + &result[ii]); + if (status < 0) + goto error; + } + + /* Check if an error was reported by SCU */ + err = (s16)result[0]; + if (err >= 0) + goto error; + + /* check for the known error codes */ + switch (err) { + case SCU_RESULT_UNKCMD: + p = "SCU_RESULT_UNKCMD"; + break; + case SCU_RESULT_UNKSTD: + p = "SCU_RESULT_UNKSTD"; + break; + case SCU_RESULT_SIZE: + p = "SCU_RESULT_SIZE"; + break; + case SCU_RESULT_INVPAR: + p = "SCU_RESULT_INVPAR"; + break; + default: /* Other negative values are errors */ + sprintf(errname, "ERROR: %d\n", err); + p = errname; + } + pr_err("%s while sending cmd 0x%04x with params:", p, cmd); + print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt); + status = -EINVAL; + goto error2; + } + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); +error2: + mutex_unlock(&state->mutex); + return status; +} + +static int set_iqm_af(struct drxk_state *state, bool active) +{ + u16 data = 0; + int status; + + dprintk(1, "\n"); + + /* Configure IQM */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + + if (!active) { + data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY + | IQM_AF_STDBY_STDBY_AMP_STANDBY + | IQM_AF_STDBY_STDBY_PD_STANDBY + | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY + | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY); + } else { + data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY) + & (~IQM_AF_STDBY_STDBY_AMP_STANDBY) + & (~IQM_AF_STDBY_STDBY_PD_STANDBY) + & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY) + & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY) + ); + } + status = write16(state, IQM_AF_STDBY__A, data); + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int ctrl_power_mode(struct drxk_state *state, enum drx_power_mode *mode) +{ + int status = 0; + u16 sio_cc_pwd_mode = 0; + + dprintk(1, "\n"); + + /* Check arguments */ + if (mode == NULL) + return -EINVAL; + + switch (*mode) { + case DRX_POWER_UP: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_NONE; + break; + case DRXK_POWER_DOWN_OFDM: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OFDM; + break; + case DRXK_POWER_DOWN_CORE: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_CLOCK; + break; + case DRXK_POWER_DOWN_PLL: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_PLL; + break; + case DRX_POWER_DOWN: + sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC; + break; + default: + /* Unknown sleep mode */ + return -EINVAL; + } + + /* If already in requested power mode, do nothing */ + if (state->m_current_power_mode == *mode) + return 0; + + /* For next steps make sure to start from DRX_POWER_UP mode */ + if (state->m_current_power_mode != DRX_POWER_UP) { + status = power_up_device(state); + if (status < 0) + goto error; + status = dvbt_enable_ofdm_token_ring(state, true); + if (status < 0) + goto error; + } + + if (*mode == DRX_POWER_UP) { + /* Restore analog & pin configuration */ + } else { + /* Power down to requested mode */ + /* Backup some register settings */ + /* Set pins with possible pull-ups connected + to them in input mode */ + /* Analog power down */ + /* ADC power down */ + /* Power down device */ + /* stop all comm_exec */ + /* Stop and power down previous standard */ + switch (state->m_operation_mode) { + case OM_DVBT: + status = mpegts_stop(state); + if (status < 0) + goto error; + status = power_down_dvbt(state, false); + if (status < 0) + goto error; + break; + case OM_QAM_ITU_A: + case OM_QAM_ITU_C: + status = mpegts_stop(state); + if (status < 0) + goto error; + status = power_down_qam(state); + if (status < 0) + goto error; + break; + default: + break; + } + status = dvbt_enable_ofdm_token_ring(state, false); + if (status < 0) + goto error; + status = write16(state, SIO_CC_PWD_MODE__A, sio_cc_pwd_mode); + if (status < 0) + goto error; + status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); + if (status < 0) + goto error; + + if (*mode != DRXK_POWER_DOWN_OFDM) { + state->m_hi_cfg_ctrl |= + SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; + status = hi_cfg_command(state); + if (status < 0) + goto error; + } + } + state->m_current_power_mode = *mode; + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int power_down_dvbt(struct drxk_state *state, bool set_power_mode) +{ + enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM; + u16 cmd_result = 0; + u16 data = 0; + int status; + + dprintk(1, "\n"); + + status = read16(state, SCU_COMM_EXEC__A, &data); + if (status < 0) + goto error; + if (data == SCU_COMM_EXEC_ACTIVE) { + /* Send OFDM stop command */ + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_OFDM + | SCU_RAM_COMMAND_CMD_DEMOD_STOP, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + /* Send OFDM reset command */ + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_OFDM + | SCU_RAM_COMMAND_CMD_DEMOD_RESET, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + } + + /* Reset datapath for OFDM, processors first */ + status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); + if (status < 0) + goto error; + + /* powerdown AFE */ + status = set_iqm_af(state, false); + if (status < 0) + goto error; + + /* powerdown to OFDM mode */ + if (set_power_mode) { + status = ctrl_power_mode(state, &power_mode); + if (status < 0) + goto error; + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int setoperation_mode(struct drxk_state *state, + enum operation_mode o_mode) +{ + int status = 0; + + dprintk(1, "\n"); + /* + Stop and power down previous standard + TODO investigate total power down instead of partial + power down depending on "previous" standard. + */ + + /* disable HW lock indicator */ + status = write16(state, SCU_RAM_GPIO__A, + SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); + if (status < 0) + goto error; + + /* Device is already at the required mode */ + if (state->m_operation_mode == o_mode) + return 0; + + switch (state->m_operation_mode) { + /* OM_NONE was added for start up */ + case OM_NONE: + break; + case OM_DVBT: + status = mpegts_stop(state); + if (status < 0) + goto error; + status = power_down_dvbt(state, true); + if (status < 0) + goto error; + state->m_operation_mode = OM_NONE; + break; + case OM_QAM_ITU_A: + case OM_QAM_ITU_C: + status = mpegts_stop(state); + if (status < 0) + goto error; + status = power_down_qam(state); + if (status < 0) + goto error; + state->m_operation_mode = OM_NONE; + break; + case OM_QAM_ITU_B: + default: + status = -EINVAL; + goto error; + } + + /* + Power up new standard + */ + switch (o_mode) { + case OM_DVBT: + dprintk(1, ": DVB-T\n"); + state->m_operation_mode = o_mode; + status = set_dvbt_standard(state, o_mode); + if (status < 0) + goto error; + break; + case OM_QAM_ITU_A: + case OM_QAM_ITU_C: + dprintk(1, ": DVB-C Annex %c\n", + (state->m_operation_mode == OM_QAM_ITU_A) ? 'A' : 'C'); + state->m_operation_mode = o_mode; + status = set_qam_standard(state, o_mode); + if (status < 0) + goto error; + break; + case OM_QAM_ITU_B: + default: + status = -EINVAL; + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int start(struct drxk_state *state, s32 offset_freq, + s32 intermediate_frequency) +{ + int status = -EINVAL; + + u16 i_freqk_hz; + s32 offsetk_hz = offset_freq / 1000; + + dprintk(1, "\n"); + if (state->m_drxk_state != DRXK_STOPPED && + state->m_drxk_state != DRXK_DTV_STARTED) + goto error; + + state->m_b_mirror_freq_spect = (state->props.inversion == INVERSION_ON); + + if (intermediate_frequency < 0) { + state->m_b_mirror_freq_spect = !state->m_b_mirror_freq_spect; + intermediate_frequency = -intermediate_frequency; + } + + switch (state->m_operation_mode) { + case OM_QAM_ITU_A: + case OM_QAM_ITU_C: + i_freqk_hz = (intermediate_frequency / 1000); + status = set_qam(state, i_freqk_hz, offsetk_hz); + if (status < 0) + goto error; + state->m_drxk_state = DRXK_DTV_STARTED; + break; + case OM_DVBT: + i_freqk_hz = (intermediate_frequency / 1000); + status = mpegts_stop(state); + if (status < 0) + goto error; + status = set_dvbt(state, i_freqk_hz, offsetk_hz); + if (status < 0) + goto error; + status = dvbt_start(state); + if (status < 0) + goto error; + state->m_drxk_state = DRXK_DTV_STARTED; + break; + default: + break; + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int shut_down(struct drxk_state *state) +{ + dprintk(1, "\n"); + + mpegts_stop(state); + return 0; +} + +static int get_lock_status(struct drxk_state *state, u32 *p_lock_status) +{ + int status = -EINVAL; + + dprintk(1, "\n"); + + if (p_lock_status == NULL) + goto error; + + *p_lock_status = NOT_LOCKED; + + /* define the SCU command code */ + switch (state->m_operation_mode) { + case OM_QAM_ITU_A: + case OM_QAM_ITU_B: + case OM_QAM_ITU_C: + status = get_qam_lock_status(state, p_lock_status); + break; + case OM_DVBT: + status = get_dvbt_lock_status(state, p_lock_status); + break; + default: + pr_debug("Unsupported operation mode %d in %s\n", + state->m_operation_mode, __func__); + return 0; + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int mpegts_start(struct drxk_state *state) +{ + int status; + + u16 fec_oc_snc_mode = 0; + + /* Allow OC to sync again */ + status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode); + if (status < 0) + goto error; + fec_oc_snc_mode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M; + status = write16(state, FEC_OC_SNC_MODE__A, fec_oc_snc_mode); + if (status < 0) + goto error; + status = write16(state, FEC_OC_SNC_UNLOCK__A, 1); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int mpegts_dto_init(struct drxk_state *state) +{ + int status; + + dprintk(1, "\n"); + + /* Rate integration settings */ + status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000); + if (status < 0) + goto error; + status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C); + if (status < 0) + goto error; + status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A); + if (status < 0) + goto error; + status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008); + if (status < 0) + goto error; + status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006); + if (status < 0) + goto error; + status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680); + if (status < 0) + goto error; + status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080); + if (status < 0) + goto error; + status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4); + if (status < 0) + goto error; + + /* Additional configuration */ + status = write16(state, FEC_OC_OCR_INVERT__A, 0); + if (status < 0) + goto error; + status = write16(state, FEC_OC_SNC_LWM__A, 2); + if (status < 0) + goto error; + status = write16(state, FEC_OC_SNC_HWM__A, 12); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int mpegts_dto_setup(struct drxk_state *state, + enum operation_mode o_mode) +{ + int status; + + u16 fec_oc_reg_mode = 0; /* FEC_OC_MODE register value */ + u16 fec_oc_reg_ipr_mode = 0; /* FEC_OC_IPR_MODE register value */ + u16 fec_oc_dto_mode = 0; /* FEC_OC_IPR_INVERT register value */ + u16 fec_oc_fct_mode = 0; /* FEC_OC_IPR_INVERT register value */ + u16 fec_oc_dto_period = 2; /* FEC_OC_IPR_INVERT register value */ + u16 fec_oc_dto_burst_len = 188; /* FEC_OC_IPR_INVERT register value */ + u32 fec_oc_rcn_ctl_rate = 0; /* FEC_OC_IPR_INVERT register value */ + u16 fec_oc_tmd_mode = 0; + u16 fec_oc_tmd_int_upd_rate = 0; + u32 max_bit_rate = 0; + bool static_clk = false; + + dprintk(1, "\n"); + + /* Check insertion of the Reed-Solomon parity bytes */ + status = read16(state, FEC_OC_MODE__A, &fec_oc_reg_mode); + if (status < 0) + goto error; + status = read16(state, FEC_OC_IPR_MODE__A, &fec_oc_reg_ipr_mode); + if (status < 0) + goto error; + fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M); + fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M); + if (state->m_insert_rs_byte) { + /* 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; + /* TS burst length to 204 */ + fec_oc_dto_burst_len = 204; + } + + /* Check serial or parallel output */ + fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); + if (!state->m_enable_parallel) { + /* MPEG data output is serial -> set ipr_mode[0] */ + fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M; + } + + switch (o_mode) { + case OM_DVBT: + max_bit_rate = state->m_dvbt_bitrate; + fec_oc_tmd_mode = 3; + fec_oc_rcn_ctl_rate = 0xC00000; + static_clk = state->m_dvbt_static_clk; + break; + case OM_QAM_ITU_A: + case OM_QAM_ITU_C: + fec_oc_tmd_mode = 0x0004; + fec_oc_rcn_ctl_rate = 0xD2B4EE; /* good for >63 Mb/s */ + max_bit_rate = state->m_dvbc_bitrate; + static_clk = state->m_dvbc_static_clk; + break; + default: + status = -EINVAL; + } /* switch (standard) */ + if (status < 0) + goto error; + + /* Configure DTO's */ + if (static_clk) { + u32 bit_rate = 0; + + /* Rational DTO for MCLK source (static MCLK rate), + Dynamic DTO for optimal grouping + (avoid intra-packet gaps), + DTO offset enable to sync TS burst with MSTRT */ + fec_oc_dto_mode = (FEC_OC_DTO_MODE_DYNAMIC__M | + FEC_OC_DTO_MODE_OFFSET_ENABLE__M); + fec_oc_fct_mode = (FEC_OC_FCT_MODE_RAT_ENA__M | + FEC_OC_FCT_MODE_VIRT_ENA__M); + + /* Check user defined bitrate */ + bit_rate = max_bit_rate; + if (bit_rate > 75900000UL) { /* max is 75.9 Mb/s */ + bit_rate = 75900000UL; + } + /* Rational DTO period: + dto_period = (Fsys / bitrate) - 2 + + result should be floored, + to make sure >= requested bitrate + */ + fec_oc_dto_period = (u16) (((state->m_sys_clock_freq) + * 1000) / bit_rate); + if (fec_oc_dto_period <= 2) + fec_oc_dto_period = 0; + else + fec_oc_dto_period -= 2; + fec_oc_tmd_int_upd_rate = 8; + } else { + /* (commonAttr->static_clk == false) => dynamic mode */ + fec_oc_dto_mode = FEC_OC_DTO_MODE_DYNAMIC__M; + fec_oc_fct_mode = FEC_OC_FCT_MODE__PRE; + fec_oc_tmd_int_upd_rate = 5; + } + + /* Write appropriate registers with requested configuration */ + status = write16(state, FEC_OC_DTO_BURST_LEN__A, fec_oc_dto_burst_len); + if (status < 0) + goto error; + status = write16(state, FEC_OC_DTO_PERIOD__A, fec_oc_dto_period); + if (status < 0) + goto error; + status = write16(state, FEC_OC_DTO_MODE__A, fec_oc_dto_mode); + if (status < 0) + goto error; + status = write16(state, FEC_OC_FCT_MODE__A, fec_oc_fct_mode); + if (status < 0) + goto error; + status = write16(state, FEC_OC_MODE__A, fec_oc_reg_mode); + if (status < 0) + goto error; + status = write16(state, FEC_OC_IPR_MODE__A, fec_oc_reg_ipr_mode); + if (status < 0) + goto error; + + /* Rate integration settings */ + status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fec_oc_rcn_ctl_rate); + if (status < 0) + goto error; + status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, + fec_oc_tmd_int_upd_rate); + if (status < 0) + goto error; + status = write16(state, FEC_OC_TMD_MODE__A, fec_oc_tmd_mode); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int mpegts_configure_polarity(struct drxk_state *state) +{ + u16 fec_oc_reg_ipr_invert = 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; + + dprintk(1, "\n"); + + /* Control selective inversion of output bits */ + fec_oc_reg_ipr_invert &= (~(invert_data_mask)); + if (state->m_invert_data) + fec_oc_reg_ipr_invert |= invert_data_mask; + fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M)); + if (state->m_invert_err) + fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M; + fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M)); + if (state->m_invert_str) + fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M; + fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M)); + if (state->m_invert_val) + fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M; + fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M)); + if (state->m_invert_clk) + fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M; + + return write16(state, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert); +} + +#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000 + +static int set_agc_rf(struct drxk_state *state, + struct s_cfg_agc *p_agc_cfg, bool is_dtv) +{ + int status = -EINVAL; + u16 data = 0; + struct s_cfg_agc *p_if_agc_settings; + + dprintk(1, "\n"); + + if (p_agc_cfg == NULL) + goto error; + + switch (p_agc_cfg->ctrl_mode) { + case DRXK_AGC_CTRL_AUTO: + /* Enable RF AGC DAC */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; + status = write16(state, IQM_AF_STDBY__A, data); + if (status < 0) + goto error; + status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); + if (status < 0) + goto error; + + /* Enable SCU RF AGC loop */ + data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; + + /* Polarity */ + if (state->m_rf_agc_pol) + data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M; + else + data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M; + status = write16(state, SCU_RAM_AGC_CONFIG__A, data); + if (status < 0) + goto error; + + /* Set speed (using complementary reduction value) */ + status = read16(state, SCU_RAM_AGC_KI_RED__A, &data); + if (status < 0) + goto error; + + data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M; + data |= (~(p_agc_cfg->speed << + SCU_RAM_AGC_KI_RED_RAGC_RED__B) + & SCU_RAM_AGC_KI_RED_RAGC_RED__M); + + status = write16(state, SCU_RAM_AGC_KI_RED__A, data); + if (status < 0) + goto error; + + if (is_dvbt(state)) + p_if_agc_settings = &state->m_dvbt_if_agc_cfg; + else if (is_qam(state)) + p_if_agc_settings = &state->m_qam_if_agc_cfg; + else + p_if_agc_settings = &state->m_atv_if_agc_cfg; + if (p_if_agc_settings == NULL) { + status = -EINVAL; + goto error; + } + + /* Set TOP, only if IF-AGC is in AUTO mode */ + if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO) { + status = write16(state, + SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, + p_agc_cfg->top); + if (status < 0) + goto error; + } + + /* Cut-Off current */ + status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, + p_agc_cfg->cut_off_current); + if (status < 0) + goto error; + + /* Max. output level */ + status = write16(state, SCU_RAM_AGC_RF_MAX__A, + p_agc_cfg->max_output_level); + if (status < 0) + goto error; + + break; + + case DRXK_AGC_CTRL_USER: + /* Enable RF AGC DAC */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; + status = write16(state, IQM_AF_STDBY__A, data); + if (status < 0) + goto error; + + /* Disable SCU RF AGC loop */ + status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); + if (status < 0) + goto error; + data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; + if (state->m_rf_agc_pol) + data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M; + else + data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M; + status = write16(state, SCU_RAM_AGC_CONFIG__A, data); + if (status < 0) + goto error; + + /* SCU c.o.c. to 0, enabling full control range */ + status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0); + if (status < 0) + goto error; + + /* Write value to output pin */ + status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, + p_agc_cfg->output_level); + if (status < 0) + goto error; + break; + + case DRXK_AGC_CTRL_OFF: + /* Disable RF AGC DAC */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; + status = write16(state, IQM_AF_STDBY__A, data); + if (status < 0) + goto error; + + /* Disable SCU RF AGC loop */ + status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); + if (status < 0) + goto error; + data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; + status = write16(state, SCU_RAM_AGC_CONFIG__A, data); + if (status < 0) + goto error; + break; + + default: + status = -EINVAL; + + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000 + +static int set_agc_if(struct drxk_state *state, + struct s_cfg_agc *p_agc_cfg, bool is_dtv) +{ + u16 data = 0; + int status = 0; + struct s_cfg_agc *p_rf_agc_settings; + + dprintk(1, "\n"); + + switch (p_agc_cfg->ctrl_mode) { + case DRXK_AGC_CTRL_AUTO: + + /* Enable IF AGC DAC */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; + status = write16(state, IQM_AF_STDBY__A, data); + if (status < 0) + goto error; + + status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); + if (status < 0) + goto error; + + /* Enable SCU IF AGC loop */ + data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; + + /* Polarity */ + if (state->m_if_agc_pol) + data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M; + else + data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M; + status = write16(state, SCU_RAM_AGC_CONFIG__A, data); + if (status < 0) + goto error; + + /* Set speed (using complementary reduction value) */ + status = read16(state, SCU_RAM_AGC_KI_RED__A, &data); + if (status < 0) + goto error; + data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M; + data |= (~(p_agc_cfg->speed << + SCU_RAM_AGC_KI_RED_IAGC_RED__B) + & SCU_RAM_AGC_KI_RED_IAGC_RED__M); + + status = write16(state, SCU_RAM_AGC_KI_RED__A, data); + if (status < 0) + goto error; + + if (is_qam(state)) + p_rf_agc_settings = &state->m_qam_rf_agc_cfg; + else + p_rf_agc_settings = &state->m_atv_rf_agc_cfg; + if (p_rf_agc_settings == NULL) + return -1; + /* Restore TOP */ + status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, + p_rf_agc_settings->top); + if (status < 0) + goto error; + break; + + case DRXK_AGC_CTRL_USER: + + /* Enable IF AGC DAC */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; + status = write16(state, IQM_AF_STDBY__A, data); + if (status < 0) + goto error; + + status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); + if (status < 0) + goto error; + + /* Disable SCU IF AGC loop */ + data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; + + /* Polarity */ + if (state->m_if_agc_pol) + data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M; + else + data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M; + status = write16(state, SCU_RAM_AGC_CONFIG__A, data); + if (status < 0) + goto error; + + /* Write value to output pin */ + status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, + p_agc_cfg->output_level); + if (status < 0) + goto error; + break; + + case DRXK_AGC_CTRL_OFF: + + /* Disable If AGC DAC */ + status = read16(state, IQM_AF_STDBY__A, &data); + if (status < 0) + goto error; + data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; + status = write16(state, IQM_AF_STDBY__A, data); + if (status < 0) + goto error; + + /* Disable SCU IF AGC loop */ + status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); + if (status < 0) + goto error; + data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; + status = write16(state, SCU_RAM_AGC_CONFIG__A, data); + if (status < 0) + goto error; + break; + } /* switch (agcSettingsIf->ctrl_mode) */ + + /* always set the top to support + configurations without if-loop */ + status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, p_agc_cfg->top); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int get_qam_signal_to_noise(struct drxk_state *state, + s32 *p_signal_to_noise) +{ + int status = 0; + u16 qam_sl_err_power = 0; /* accum. error between + raw and sliced symbols */ + u32 qam_sl_sig_power = 0; /* used for MER, depends of + QAM modulation */ + u32 qam_sl_mer = 0; /* QAM MER */ + + dprintk(1, "\n"); + + /* MER calculation */ + + /* get the register value needed for MER */ + status = read16(state, QAM_SL_ERR_POWER__A, &qam_sl_err_power); + if (status < 0) { + pr_err("Error %d on %s\n", status, __func__); + return -EINVAL; + } + + switch (state->props.modulation) { + case QAM_16: + qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM16 << 2; + break; + case QAM_32: + qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM32 << 2; + break; + case QAM_64: + qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM64 << 2; + break; + case QAM_128: + qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM128 << 2; + break; + default: + case QAM_256: + qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM256 << 2; + break; + } + + if (qam_sl_err_power > 0) { + qam_sl_mer = log10times100(qam_sl_sig_power) - + log10times100((u32) qam_sl_err_power); + } + *p_signal_to_noise = qam_sl_mer; + + return status; +} + +static int get_dvbt_signal_to_noise(struct drxk_state *state, + s32 *p_signal_to_noise) +{ + int status; + u16 reg_data = 0; + u32 eq_reg_td_sqr_err_i = 0; + u32 eq_reg_td_sqr_err_q = 0; + u16 eq_reg_td_sqr_err_exp = 0; + u16 eq_reg_td_tps_pwr_ofs = 0; + u16 eq_reg_td_req_smb_cnt = 0; + u32 tps_cnt = 0; + u32 sqr_err_iq = 0; + u32 a = 0; + u32 b = 0; + u32 c = 0; + u32 i_mer = 0; + u16 transmission_params = 0; + + dprintk(1, "\n"); + + status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, + &eq_reg_td_tps_pwr_ofs); + if (status < 0) + goto error; + status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, + &eq_reg_td_req_smb_cnt); + if (status < 0) + goto error; + status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, + &eq_reg_td_sqr_err_exp); + if (status < 0) + goto error; + status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, + ®_data); + if (status < 0) + goto error; + /* Extend SQR_ERR_I operational range */ + eq_reg_td_sqr_err_i = (u32) reg_data; + if ((eq_reg_td_sqr_err_exp > 11) && + (eq_reg_td_sqr_err_i < 0x00000FFFUL)) { + eq_reg_td_sqr_err_i += 0x00010000UL; + } + status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, ®_data); + if (status < 0) + goto error; + /* Extend SQR_ERR_Q operational range */ + eq_reg_td_sqr_err_q = (u32) reg_data; + if ((eq_reg_td_sqr_err_exp > 11) && + (eq_reg_td_sqr_err_q < 0x00000FFFUL)) + eq_reg_td_sqr_err_q += 0x00010000UL; + + status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, + &transmission_params); + if (status < 0) + goto error; + + /* Check input data for MER */ + + /* MER calculation (in 0.1 dB) without math.h */ + if ((eq_reg_td_tps_pwr_ofs == 0) || (eq_reg_td_req_smb_cnt == 0)) + i_mer = 0; + else if ((eq_reg_td_sqr_err_i + eq_reg_td_sqr_err_q) == 0) { + /* No error at all, this must be the HW reset value + * Apparently no first measurement yet + * Set MER to 0.0 */ + i_mer = 0; + } else { + sqr_err_iq = (eq_reg_td_sqr_err_i + eq_reg_td_sqr_err_q) << + eq_reg_td_sqr_err_exp; + if ((transmission_params & + OFDM_SC_RA_RAM_OP_PARAM_MODE__M) + == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K) + tps_cnt = 17; + else + tps_cnt = 68; + + /* IMER = 100 * log10 (x) + where x = (eq_reg_td_tps_pwr_ofs^2 * + eq_reg_td_req_smb_cnt * tps_cnt)/sqr_err_iq + + => IMER = a + b -c + where a = 100 * log10 (eq_reg_td_tps_pwr_ofs^2) + b = 100 * log10 (eq_reg_td_req_smb_cnt * tps_cnt) + c = 100 * log10 (sqr_err_iq) + */ + + /* log(x) x = 9bits * 9bits->18 bits */ + a = log10times100(eq_reg_td_tps_pwr_ofs * + eq_reg_td_tps_pwr_ofs); + /* log(x) x = 16bits * 7bits->23 bits */ + b = log10times100(eq_reg_td_req_smb_cnt * tps_cnt); + /* log(x) x = (16bits + 16bits) << 15 ->32 bits */ + c = log10times100(sqr_err_iq); + + i_mer = a + b - c; + } + *p_signal_to_noise = i_mer; + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int get_signal_to_noise(struct drxk_state *state, s32 *p_signal_to_noise) +{ + dprintk(1, "\n"); + + *p_signal_to_noise = 0; + switch (state->m_operation_mode) { + case OM_DVBT: + return get_dvbt_signal_to_noise(state, p_signal_to_noise); + case OM_QAM_ITU_A: + case OM_QAM_ITU_C: + return get_qam_signal_to_noise(state, p_signal_to_noise); + default: + break; + } + return 0; +} + +#if 0 +static int get_dvbt_quality(struct drxk_state *state, s32 *p_quality) +{ + /* SNR Values for quasi errorfree reception rom Nordig 2.2 */ + int status = 0; + + dprintk(1, "\n"); + + static s32 QE_SN[] = { + 51, /* QPSK 1/2 */ + 69, /* QPSK 2/3 */ + 79, /* QPSK 3/4 */ + 89, /* QPSK 5/6 */ + 97, /* QPSK 7/8 */ + 108, /* 16-QAM 1/2 */ + 131, /* 16-QAM 2/3 */ + 146, /* 16-QAM 3/4 */ + 156, /* 16-QAM 5/6 */ + 160, /* 16-QAM 7/8 */ + 165, /* 64-QAM 1/2 */ + 187, /* 64-QAM 2/3 */ + 202, /* 64-QAM 3/4 */ + 216, /* 64-QAM 5/6 */ + 225, /* 64-QAM 7/8 */ + }; + + *p_quality = 0; + + do { + s32 signal_to_noise = 0; + u16 constellation = 0; + u16 code_rate = 0; + u32 signal_to_noise_rel; + u32 ber_quality; + + status = get_dvbt_signal_to_noise(state, &signal_to_noise); + if (status < 0) + break; + status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, + &constellation); + if (status < 0) + break; + constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M; + + status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, + &code_rate); + if (status < 0) + break; + code_rate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M; + + if (constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM || + code_rate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8) + break; + signal_to_noise_rel = signal_to_noise - + QE_SN[constellation * 5 + code_rate]; + ber_quality = 100; + + if (signal_to_noise_rel < -70) + *p_quality = 0; + else if (signal_to_noise_rel < 30) + *p_quality = ((signal_to_noise_rel + 70) * + ber_quality) / 100; + else + *p_quality = ber_quality; + } while (0); + return 0; +}; + +static int get_dvbc_quality(struct drxk_state *state, s32 *p_quality) +{ + int status = 0; + *p_quality = 0; + + dprintk(1, "\n"); + + do { + u32 signal_to_noise = 0; + u32 ber_quality = 100; + u32 signal_to_noise_rel = 0; + + status = get_qam_signal_to_noise(state, &signal_to_noise); + if (status < 0) + break; + + switch (state->props.modulation) { + case QAM_16: + signal_to_noise_rel = signal_to_noise - 200; + break; + case QAM_32: + signal_to_noise_rel = signal_to_noise - 230; + break; /* Not in NorDig */ + case QAM_64: + signal_to_noise_rel = signal_to_noise - 260; + break; + case QAM_128: + signal_to_noise_rel = signal_to_noise - 290; + break; + default: + case QAM_256: + signal_to_noise_rel = signal_to_noise - 320; + break; + } + + if (signal_to_noise_rel < -70) + *p_quality = 0; + else if (signal_to_noise_rel < 30) + *p_quality = ((signal_to_noise_rel + 70) * + ber_quality) / 100; + else + *p_quality = ber_quality; + } while (0); + + return status; +} + +static int get_quality(struct drxk_state *state, s32 *p_quality) +{ + dprintk(1, "\n"); + + switch (state->m_operation_mode) { + case OM_DVBT: + return get_dvbt_quality(state, p_quality); + case OM_QAM_ITU_A: + return get_dvbc_quality(state, p_quality); + default: + break; + } + + return 0; +} +#endif + +/* 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 DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A) +#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7) +#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ +#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE + +#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F) +#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F) +#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF) + +static int ConfigureI2CBridge(struct drxk_state *state, bool b_enable_bridge) +{ + int status = -EINVAL; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return 0; + if (state->m_drxk_state == DRXK_POWERED_DOWN) + goto error; + + if (state->no_i2c_bridge) + return 0; + + status = write16(state, SIO_HI_RA_RAM_PAR_1__A, + SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY); + if (status < 0) + goto error; + if (b_enable_bridge) { + status = write16(state, SIO_HI_RA_RAM_PAR_2__A, + SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED); + if (status < 0) + goto error; + } else { + status = write16(state, SIO_HI_RA_RAM_PAR_2__A, + SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN); + if (status < 0) + goto error; + } + + status = hi_command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, NULL); + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int set_pre_saw(struct drxk_state *state, + struct s_cfg_pre_saw *p_pre_saw_cfg) +{ + int status = -EINVAL; + + dprintk(1, "\n"); + + if ((p_pre_saw_cfg == NULL) + || (p_pre_saw_cfg->reference > IQM_AF_PDREF__M)) + goto error; + + status = write16(state, IQM_AF_PDREF__A, p_pre_saw_cfg->reference); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int bl_direct_cmd(struct drxk_state *state, u32 target_addr, + u16 rom_offset, u16 nr_of_elements, u32 time_out) +{ + u16 bl_status = 0; + u16 offset = (u16) ((target_addr >> 0) & 0x00FFFF); + u16 blockbank = (u16) ((target_addr >> 16) & 0x000FFF); + int status; + unsigned long end; + + dprintk(1, "\n"); + + mutex_lock(&state->mutex); + status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT); + if (status < 0) + goto error; + status = write16(state, SIO_BL_TGT_HDR__A, blockbank); + if (status < 0) + goto error; + status = write16(state, SIO_BL_TGT_ADDR__A, offset); + if (status < 0) + goto error; + status = write16(state, SIO_BL_SRC_ADDR__A, rom_offset); + if (status < 0) + goto error; + status = write16(state, SIO_BL_SRC_LEN__A, nr_of_elements); + if (status < 0) + goto error; + status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON); + if (status < 0) + goto error; + + end = jiffies + msecs_to_jiffies(time_out); + do { + status = read16(state, SIO_BL_STATUS__A, &bl_status); + if (status < 0) + goto error; + } while ((bl_status == 0x1) && time_is_after_jiffies(end)); + if (bl_status == 0x1) { + pr_err("SIO not ready\n"); + status = -EINVAL; + goto error2; + } +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); +error2: + mutex_unlock(&state->mutex); + return status; + +} + +static int adc_sync_measurement(struct drxk_state *state, u16 *count) +{ + u16 data = 0; + int status; + + dprintk(1, "\n"); + + /* start measurement */ + status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + status = write16(state, IQM_AF_START_LOCK__A, 1); + if (status < 0) + goto error; + + *count = 0; + status = read16(state, IQM_AF_PHASE0__A, &data); + if (status < 0) + goto error; + if (data == 127) + *count = *count + 1; + status = read16(state, IQM_AF_PHASE1__A, &data); + if (status < 0) + goto error; + if (data == 127) + *count = *count + 1; + status = read16(state, IQM_AF_PHASE2__A, &data); + if (status < 0) + goto error; + if (data == 127) + *count = *count + 1; + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int adc_synchronization(struct drxk_state *state) +{ + u16 count = 0; + int status; + + dprintk(1, "\n"); + + status = adc_sync_measurement(state, &count); + if (status < 0) + goto error; + + if (count == 1) { + /* Try sampling on a different edge */ + u16 clk_neg = 0; + + status = read16(state, IQM_AF_CLKNEG__A, &clk_neg); + if (status < 0) + goto error; + if ((clk_neg & IQM_AF_CLKNEG_CLKNEGDATA__M) == + IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) { + clk_neg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); + clk_neg |= + IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG; + } else { + clk_neg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); + clk_neg |= + IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS; + } + status = write16(state, IQM_AF_CLKNEG__A, clk_neg); + if (status < 0) + goto error; + status = adc_sync_measurement(state, &count); + if (status < 0) + goto error; + } + + if (count < 2) + status = -EINVAL; +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int set_frequency_shifter(struct drxk_state *state, + u16 intermediate_freqk_hz, + s32 tuner_freq_offset, bool is_dtv) +{ + bool select_pos_image = false; + u32 rf_freq_residual = tuner_freq_offset; + u32 fm_frequency_shift = 0; + bool tuner_mirror = !state->m_b_mirror_freq_spect; + u32 adc_freq; + bool adc_flip; + int status; + u32 if_freq_actual; + u32 sampling_frequency = (u32) (state->m_sys_clock_freq / 3); + u32 frequency_shift; + bool image_to_select; + + dprintk(1, "\n"); + + /* + Program frequency shifter + No need to account for mirroring on RF + */ + if (is_dtv) { + if ((state->m_operation_mode == OM_QAM_ITU_A) || + (state->m_operation_mode == OM_QAM_ITU_C) || + (state->m_operation_mode == OM_DVBT)) + select_pos_image = true; + else + select_pos_image = false; + } + if (tuner_mirror) + /* tuner doesn't mirror */ + if_freq_actual = intermediate_freqk_hz + + rf_freq_residual + fm_frequency_shift; + else + /* tuner mirrors */ + if_freq_actual = intermediate_freqk_hz - + rf_freq_residual - fm_frequency_shift; + if (if_freq_actual > sampling_frequency / 2) { + /* adc mirrors */ + adc_freq = sampling_frequency - if_freq_actual; + adc_flip = true; + } else { + /* adc doesn't mirror */ + adc_freq = if_freq_actual; + adc_flip = false; + } + + frequency_shift = adc_freq; + image_to_select = state->m_rfmirror ^ tuner_mirror ^ + adc_flip ^ select_pos_image; + state->m_iqm_fs_rate_ofs = + Frac28a((frequency_shift), sampling_frequency); + + if (image_to_select) + state->m_iqm_fs_rate_ofs = ~state->m_iqm_fs_rate_ofs + 1; + + /* Program frequency shifter with tuner offset compensation */ + /* frequency_shift += tuner_freq_offset; TODO */ + status = write32(state, IQM_FS_RATE_OFS_LO__A, + state->m_iqm_fs_rate_ofs); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int init_agc(struct drxk_state *state, bool is_dtv) +{ + u16 ingain_tgt = 0; + u16 ingain_tgt_min = 0; + u16 ingain_tgt_max = 0; + u16 clp_cyclen = 0; + u16 clp_sum_min = 0; + u16 clp_dir_to = 0; + u16 sns_sum_min = 0; + u16 sns_sum_max = 0; + u16 clp_sum_max = 0; + u16 sns_dir_to = 0; + u16 ki_innergain_min = 0; + u16 if_iaccu_hi_tgt = 0; + u16 if_iaccu_hi_tgt_min = 0; + u16 if_iaccu_hi_tgt_max = 0; + u16 data = 0; + u16 fast_clp_ctrl_delay = 0; + u16 clp_ctrl_mode = 0; + int status = 0; + + dprintk(1, "\n"); + + /* Common settings */ + sns_sum_max = 1023; + if_iaccu_hi_tgt_min = 2047; + clp_cyclen = 500; + clp_sum_max = 1023; + + /* AGCInit() not available for DVBT; init done in microcode */ + if (!is_qam(state)) { + pr_err("%s: mode %d is not DVB-C\n", + __func__, state->m_operation_mode); + return -EINVAL; + } + + /* FIXME: Analog TV AGC require different settings */ + + /* Standard specific settings */ + clp_sum_min = 8; + clp_dir_to = (u16) -9; + clp_ctrl_mode = 0; + sns_sum_min = 8; + sns_dir_to = (u16) -9; + ki_innergain_min = (u16) -1030; + if_iaccu_hi_tgt_max = 0x2380; + if_iaccu_hi_tgt = 0x2380; + ingain_tgt_min = 0x0511; + ingain_tgt = 0x0511; + ingain_tgt_max = 5119; + fast_clp_ctrl_delay = state->m_qam_if_agc_cfg.fast_clip_ctrl_delay; + + status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, + fast_clp_ctrl_delay); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clp_ctrl_mode); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingain_tgt); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingain_tgt_min); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingain_tgt_max); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, + if_iaccu_hi_tgt_min); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, + if_iaccu_hi_tgt_max); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clp_sum_max); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, sns_sum_max); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, + ki_innergain_min); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, + if_iaccu_hi_tgt); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clp_cyclen); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clp_sum_min); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, sns_sum_min); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clp_dir_to); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, sns_dir_to); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500); + if (status < 0) + goto error; + + /* Initialize inner-loop KI gain factors */ + status = read16(state, SCU_RAM_AGC_KI__A, &data); + if (status < 0) + goto error; + + data = 0x0657; + data &= ~SCU_RAM_AGC_KI_RF__M; + data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B); + data &= ~SCU_RAM_AGC_KI_IF__M; + data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B); + + status = write16(state, SCU_RAM_AGC_KI__A, data); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int dvbtqam_get_acc_pkt_err(struct drxk_state *state, u16 *packet_err) +{ + int status; + + dprintk(1, "\n"); + if (packet_err == NULL) + status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0); + else + status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, + packet_err); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int dvbt_sc_command(struct drxk_state *state, + u16 cmd, u16 subcmd, + u16 param0, u16 param1, u16 param2, + u16 param3, u16 param4) +{ + u16 cur_cmd = 0; + u16 err_code = 0; + u16 retry_cnt = 0; + u16 sc_exec = 0; + int status; + + dprintk(1, "\n"); + status = read16(state, OFDM_SC_COMM_EXEC__A, &sc_exec); + if (sc_exec != 1) { + /* SC is not running */ + status = -EINVAL; + } + if (status < 0) + goto error; + + /* Wait until sc is ready to receive command */ + retry_cnt = 0; + do { + usleep_range(1000, 2000); + status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd); + retry_cnt++; + } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES)); + if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0)) + goto error; + + /* Write sub-command */ + switch (cmd) { + /* All commands using sub-cmd */ + case OFDM_SC_RA_RAM_CMD_PROC_START: + case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: + case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: + status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd); + if (status < 0) + goto error; + break; + default: + /* Do nothing */ + break; + } + + /* Write needed parameters and the command */ + status = 0; + switch (cmd) { + /* All commands using 5 parameters */ + /* All commands using 4 parameters */ + /* All commands using 3 parameters */ + /* All commands using 2 parameters */ + case OFDM_SC_RA_RAM_CMD_PROC_START: + case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: + case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: + status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); + fallthrough; /* All commands using 1 parameters */ + case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: + case OFDM_SC_RA_RAM_CMD_USER_IO: + status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); + fallthrough; /* All commands using 0 parameters */ + case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: + case OFDM_SC_RA_RAM_CMD_NULL: + /* Write command */ + status |= write16(state, OFDM_SC_RA_RAM_CMD__A, cmd); + break; + default: + /* Unknown command */ + status = -EINVAL; + } + if (status < 0) + goto error; + + /* Wait until sc is ready processing command */ + retry_cnt = 0; + do { + usleep_range(1000, 2000); + status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd); + retry_cnt++; + } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES)); + if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0)) + goto error; + + /* Check for illegal cmd */ + status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &err_code); + if (err_code == 0xFFFF) { + /* illegal command */ + status = -EINVAL; + } + if (status < 0) + goto error; + + /* Retrieve results parameters from SC */ + switch (cmd) { + /* All commands yielding 5 results */ + /* All commands yielding 4 results */ + /* All commands yielding 3 results */ + /* All commands yielding 2 results */ + /* All commands yielding 1 result */ + case OFDM_SC_RA_RAM_CMD_USER_IO: + case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: + status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0)); + break; + /* All commands yielding 0 results */ + case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: + case OFDM_SC_RA_RAM_CMD_SET_TIMER: + case OFDM_SC_RA_RAM_CMD_PROC_START: + case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: + case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: + case OFDM_SC_RA_RAM_CMD_NULL: + break; + default: + /* Unknown command */ + status = -EINVAL; + break; + } /* switch (cmd->cmd) */ +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int power_up_dvbt(struct drxk_state *state) +{ + enum drx_power_mode power_mode = DRX_POWER_UP; + int status; + + dprintk(1, "\n"); + status = ctrl_power_mode(state, &power_mode); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int dvbt_ctrl_set_inc_enable(struct drxk_state *state, bool *enabled) +{ + int status; + + dprintk(1, "\n"); + if (*enabled) + status = write16(state, IQM_CF_BYPASSDET__A, 0); + else + status = write16(state, IQM_CF_BYPASSDET__A, 1); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +#define DEFAULT_FR_THRES_8K 4000 +static int dvbt_ctrl_set_fr_enable(struct drxk_state *state, bool *enabled) +{ + + int status; + + dprintk(1, "\n"); + if (*enabled) { + /* write mask to 1 */ + status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, + DEFAULT_FR_THRES_8K); + } else { + /* write mask to 0 */ + status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0); + } + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int dvbt_ctrl_set_echo_threshold(struct drxk_state *state, + struct drxk_cfg_dvbt_echo_thres_t *echo_thres) +{ + u16 data = 0; + int status; + + dprintk(1, "\n"); + status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data); + if (status < 0) + goto error; + + switch (echo_thres->fft_mode) { + case DRX_FFTMODE_2K: + data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M; + data |= ((echo_thres->threshold << + OFDM_SC_RA_RAM_ECHO_THRES_2K__B) + & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M)); + break; + case DRX_FFTMODE_8K: + data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M; + data |= ((echo_thres->threshold << + OFDM_SC_RA_RAM_ECHO_THRES_8K__B) + & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M)); + break; + default: + return -EINVAL; + } + + status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int dvbt_ctrl_set_sqi_speed(struct drxk_state *state, + enum drxk_cfg_dvbt_sqi_speed *speed) +{ + int status = -EINVAL; + + dprintk(1, "\n"); + + switch (*speed) { + case DRXK_DVBT_SQI_SPEED_FAST: + case DRXK_DVBT_SQI_SPEED_MEDIUM: + case DRXK_DVBT_SQI_SPEED_SLOW: + break; + default: + goto error; + } + status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A, + (u16) *speed); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ + +/* +* \brief Activate DVBT specific presets +* \param demod instance of demodulator. +* \return DRXStatus_t. +* +* Called in DVBTSetStandard +* +*/ +static int dvbt_activate_presets(struct drxk_state *state) +{ + int status; + bool setincenable = false; + bool setfrenable = true; + + struct drxk_cfg_dvbt_echo_thres_t echo_thres2k = { 0, DRX_FFTMODE_2K }; + struct drxk_cfg_dvbt_echo_thres_t echo_thres8k = { 0, DRX_FFTMODE_8K }; + + dprintk(1, "\n"); + status = dvbt_ctrl_set_inc_enable(state, &setincenable); + if (status < 0) + goto error; + status = dvbt_ctrl_set_fr_enable(state, &setfrenable); + if (status < 0) + goto error; + status = dvbt_ctrl_set_echo_threshold(state, &echo_thres2k); + if (status < 0) + goto error; + status = dvbt_ctrl_set_echo_threshold(state, &echo_thres8k); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, + state->m_dvbt_if_agc_cfg.ingain_tgt_max); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ + +/* +* \brief Initialize channelswitch-independent settings for DVBT. +* \param demod instance of demodulator. +* \return DRXStatus_t. +* +* For ROM code channel filter taps are loaded from the bootloader. For microcode +* the DVB-T taps from the drxk_filters.h are used. +*/ +static int set_dvbt_standard(struct drxk_state *state, + enum operation_mode o_mode) +{ + u16 cmd_result = 0; + u16 data = 0; + int status; + + dprintk(1, "\n"); + + power_up_dvbt(state); + /* added antenna switch */ + switch_antenna_to_dvbt(state); + /* send OFDM reset command */ + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_OFDM + | SCU_RAM_COMMAND_CMD_DEMOD_RESET, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + + /* send OFDM setenv command */ + status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM + | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + + /* reset datapath for OFDM, processors first */ + status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); + if (status < 0) + goto error; + + /* IQM setup */ + /* synchronize on ofdstate->m_festart */ + status = write16(state, IQM_AF_UPD_SEL__A, 1); + if (status < 0) + goto error; + /* window size for clipping ADC detection */ + status = write16(state, IQM_AF_CLP_LEN__A, 0); + if (status < 0) + goto error; + /* window size for sense pre-SAW detection */ + status = write16(state, IQM_AF_SNS_LEN__A, 0); + if (status < 0) + goto error; + /* sense threshold for sense pre-SAW detection */ + status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC); + if (status < 0) + goto error; + status = set_iqm_af(state, true); + if (status < 0) + goto error; + + status = write16(state, IQM_AF_AGC_RF__A, 0); + if (status < 0) + goto error; + + /* Impulse noise cruncher setup */ + status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */ + if (status < 0) + goto error; + status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */ + if (status < 0) + goto error; + status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */ + if (status < 0) + goto error; + + status = write16(state, IQM_RC_STRETCH__A, 16); + if (status < 0) + goto error; + status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */ + if (status < 0) + goto error; + status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */ + if (status < 0) + goto error; + status = write16(state, IQM_CF_SCALE__A, 1600); + if (status < 0) + goto error; + status = write16(state, IQM_CF_SCALE_SH__A, 0); + if (status < 0) + goto error; + + /* virtual clipping threshold for clipping ADC detection */ + status = write16(state, IQM_AF_CLP_TH__A, 448); + if (status < 0) + goto error; + status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */ + if (status < 0) + goto error; + + status = bl_chain_cmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, + DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); + if (status < 0) + goto error; + + status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */ + if (status < 0) + goto error; + status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2); + if (status < 0) + goto error; + /* enable power measurement interrupt */ + status = write16(state, IQM_CF_COMM_INT_MSK__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE); + if (status < 0) + goto error; + + /* IQM will not be reset from here, sync ADC and update/init AGC */ + status = adc_synchronization(state); + if (status < 0) + goto error; + status = set_pre_saw(state, &state->m_dvbt_pre_saw_cfg); + if (status < 0) + goto error; + + /* Halt SCU to enable safe non-atomic accesses */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); + if (status < 0) + goto error; + + status = set_agc_rf(state, &state->m_dvbt_rf_agc_cfg, true); + if (status < 0) + goto error; + status = set_agc_if(state, &state->m_dvbt_if_agc_cfg, true); + if (status < 0) + goto error; + + /* Set Noise Estimation notch width and enable DC fix */ + status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data); + if (status < 0) + goto error; + data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M; + status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data); + if (status < 0) + goto error; + + /* Activate SCU to enable SCU commands */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + + if (!state->m_drxk_a3_rom_code) { + /* AGCInit() is not done for DVBT, so set agcfast_clip_ctrl_delay */ + status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, + state->m_dvbt_if_agc_cfg.fast_clip_ctrl_delay); + if (status < 0) + goto error; + } + + /* OFDM_SC setup */ +#ifdef COMPILE_FOR_NONRT + status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2); + if (status < 0) + goto error; +#endif + + /* FEC setup */ + status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */ + if (status < 0) + goto error; + + +#ifdef COMPILE_FOR_NONRT + status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400); + if (status < 0) + goto error; +#else + status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000); + if (status < 0) + goto error; +#endif + status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001); + if (status < 0) + goto error; + + /* Setup MPEG bus */ + status = mpegts_dto_setup(state, OM_DVBT); + if (status < 0) + goto error; + /* Set DVBT Presets */ + status = dvbt_activate_presets(state); + if (status < 0) + goto error; + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ +/* +* \brief start dvbt demodulating for channel. +* \param demod instance of demodulator. +* \return DRXStatus_t. +*/ +static int dvbt_start(struct drxk_state *state) +{ + u16 param1; + int status; + /* drxk_ofdm_sc_cmd_t scCmd; */ + + dprintk(1, "\n"); + /* start correct processes to get in lock */ + /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */ + param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN; + status = dvbt_sc_command(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, + OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, + 0, 0, 0); + if (status < 0) + goto error; + /* start FEC OC */ + status = mpegts_start(state); + if (status < 0) + goto error; + status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + + +/*============================================================================*/ + +/* +* \brief Set up dvbt demodulator for channel. +* \param demod instance of demodulator. +* \return DRXStatus_t. +* // original DVBTSetChannel() +*/ +static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, + s32 tuner_freq_offset) +{ + u16 cmd_result = 0; + u16 transmission_params = 0; + u32 iqm_rc_rate_ofs = 0; + u32 bandwidth = 0; + u16 param1; + int status; + + dprintk(1, "IF =%d, TFO = %d\n", + intermediate_freqk_hz, tuner_freq_offset); + + status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM + | SCU_RAM_COMMAND_CMD_DEMOD_STOP, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + + /* Halt SCU to enable safe non-atomic accesses */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); + if (status < 0) + goto error; + + /* Stop processors */ + status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); + if (status < 0) + goto error; + + /* Mandatory fix, always stop CP, required to set spl offset back to + hardware default (is set to 0 by ucode during pilot detection */ + status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP); + if (status < 0) + goto error; + + /*== Write channel settings to device ================================*/ + + /* mode */ + switch (state->props.transmission_mode) { + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_8K: + default: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K; + break; + case TRANSMISSION_MODE_2K: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K; + break; + } + + /* guard */ + switch (state->props.guard_interval) { + default: + case GUARD_INTERVAL_AUTO: /* try first guess DRX_GUARD_1DIV4 */ + case GUARD_INTERVAL_1_4: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4; + break; + case GUARD_INTERVAL_1_32: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32; + break; + case GUARD_INTERVAL_1_16: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16; + break; + case GUARD_INTERVAL_1_8: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8; + break; + } + + /* hierarchy */ + switch (state->props.hierarchy) { + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: /* try first guess SC_RA_RAM_OP_PARAM_HIER_NO */ + case HIERARCHY_1: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1; + break; + case HIERARCHY_2: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2; + break; + case HIERARCHY_4: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4; + break; + } + + + /* modulation */ + switch (state->props.modulation) { + case QAM_AUTO: + default: /* try first guess DRX_CONSTELLATION_QAM64 */ + case QAM_64: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64; + break; + case QPSK: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK; + break; + case QAM_16: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16; + break; + } +#if 0 + /* No hierarchical channels support in BDA */ + /* Priority (only for hierarchical channels) */ + switch (channel->priority) { + case DRX_PRIORITY_LOW: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO; + WR16(dev_addr, OFDM_EC_SB_PRIOR__A, + OFDM_EC_SB_PRIOR_LO); + break; + case DRX_PRIORITY_HIGH: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI; + WR16(dev_addr, OFDM_EC_SB_PRIOR__A, + OFDM_EC_SB_PRIOR_HI)); + break; + case DRX_PRIORITY_UNKNOWN: + default: + status = -EINVAL; + goto error; + } +#else + /* Set Priority high */ + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI; + status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI); + if (status < 0) + goto error; +#endif + + /* coderate */ + switch (state->props.code_rate_HP) { + case FEC_AUTO: + default: /* try first guess DRX_CODERATE_2DIV3 */ + case FEC_2_3: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3; + break; + case FEC_1_2: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2; + break; + case FEC_3_4: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4; + break; + case FEC_5_6: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6; + break; + case FEC_7_8: + transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8; + break; + } + + /* + * SAW filter selection: normally not necessary, but if wanted + * the application can select a SAW filter via the driver by + * using UIOs + */ + + /* First determine real bandwidth (Hz) */ + /* Also set delay for impulse noise cruncher */ + /* + * Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is + * changed by SC for fix for some 8K,1/8 guard but is restored by + * InitEC and ResetEC functions + */ + switch (state->props.bandwidth_hz) { + case 0: + state->props.bandwidth_hz = 8000000; + fallthrough; + case 8000000: + bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ; + status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, + 3052); + if (status < 0) + goto error; + /* cochannel protection for PAL 8 MHz */ + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, + 7); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, + 7); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, + 7); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, + 1); + if (status < 0) + goto error; + break; + case 7000000: + bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ; + status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, + 3491); + if (status < 0) + goto error; + /* cochannel protection for PAL 7 MHz */ + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, + 8); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, + 8); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, + 4); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, + 1); + if (status < 0) + goto error; + break; + case 6000000: + bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ; + status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, + 4073); + if (status < 0) + goto error; + /* cochannel protection for NTSC 6 MHz */ + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, + 19); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, + 19); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, + 14); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, + 1); + if (status < 0) + goto error; + break; + default: + status = -EINVAL; + goto error; + } + + if (iqm_rc_rate_ofs == 0) { + /* Now compute IQM_RC_RATE_OFS + (((SysFreq/BandWidth)/2)/2) -1) * 2^23) + => + ((SysFreq / BandWidth) * (2^21)) - (2^23) + */ + /* (SysFreq / BandWidth) * (2^28) */ + /* + * assert (MAX(sysClk)/MIN(bandwidth) < 16) + * => assert(MAX(sysClk) < 16*MIN(bandwidth)) + * => assert(109714272 > 48000000) = true + * so Frac 28 can be used + */ + iqm_rc_rate_ofs = Frac28a((u32) + ((state->m_sys_clock_freq * + 1000) / 3), bandwidth); + /* (SysFreq / BandWidth) * (2^21), rounding before truncating */ + if ((iqm_rc_rate_ofs & 0x7fL) >= 0x40) + iqm_rc_rate_ofs += 0x80L; + iqm_rc_rate_ofs = iqm_rc_rate_ofs >> 7; + /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */ + iqm_rc_rate_ofs = iqm_rc_rate_ofs - (1 << 23); + } + + iqm_rc_rate_ofs &= + ((((u32) IQM_RC_RATE_OFS_HI__M) << + IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M); + status = write32(state, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate_ofs); + if (status < 0) + goto error; + + /* Bandwidth setting done */ + +#if 0 + status = dvbt_set_frequency_shift(demod, channel, tuner_offset); + if (status < 0) + goto error; +#endif + status = set_frequency_shifter(state, intermediate_freqk_hz, + tuner_freq_offset, true); + if (status < 0) + goto error; + + /*== start SC, write channel settings to SC ==========================*/ + + /* Activate SCU to enable SCU commands */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + + /* Enable SC after setting all other parameters */ + status = write16(state, OFDM_SC_COMM_STATE__A, 0); + if (status < 0) + goto error; + status = write16(state, OFDM_SC_COMM_EXEC__A, 1); + if (status < 0) + goto error; + + + status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM + | SCU_RAM_COMMAND_CMD_DEMOD_START, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + + /* Write SC parameter registers, set all AUTO flags in operation mode */ + param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M | + OFDM_SC_RA_RAM_OP_AUTO_GUARD__M | + OFDM_SC_RA_RAM_OP_AUTO_CONST__M | + OFDM_SC_RA_RAM_OP_AUTO_HIER__M | + OFDM_SC_RA_RAM_OP_AUTO_RATE__M); + status = dvbt_sc_command(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM, + 0, transmission_params, param1, 0, 0, 0); + if (status < 0) + goto error; + + if (!state->m_drxk_a3_rom_code) + status = dvbt_ctrl_set_sqi_speed(state, &state->m_sqi_speed); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + + +/*============================================================================*/ + +/* +* \brief Retrieve lock status . +* \param demod Pointer to demodulator instance. +* \param lockStat Pointer to lock status structure. +* \return DRXStatus_t. +* +*/ +static int get_dvbt_lock_status(struct drxk_state *state, u32 *p_lock_status) +{ + int status; + const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M | + OFDM_SC_RA_RAM_LOCK_FEC__M); + const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M); + const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M; + + u16 sc_ra_ram_lock = 0; + u16 sc_comm_exec = 0; + + dprintk(1, "\n"); + + *p_lock_status = NOT_LOCKED; + /* driver 0.9.0 */ + /* Check if SC is running */ + status = read16(state, OFDM_SC_COMM_EXEC__A, &sc_comm_exec); + if (status < 0) + goto end; + if (sc_comm_exec == OFDM_SC_COMM_EXEC_STOP) + goto end; + + status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &sc_ra_ram_lock); + if (status < 0) + goto end; + + if ((sc_ra_ram_lock & mpeg_lock_mask) == mpeg_lock_mask) + *p_lock_status = MPEG_LOCK; + else if ((sc_ra_ram_lock & fec_lock_mask) == fec_lock_mask) + *p_lock_status = FEC_LOCK; + else if ((sc_ra_ram_lock & demod_lock_mask) == demod_lock_mask) + *p_lock_status = DEMOD_LOCK; + else if (sc_ra_ram_lock & OFDM_SC_RA_RAM_LOCK_NODVBT__M) + *p_lock_status = NEVER_LOCK; +end: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int power_up_qam(struct drxk_state *state) +{ + enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM; + int status; + + dprintk(1, "\n"); + status = ctrl_power_mode(state, &power_mode); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + + +/* Power Down QAM */ +static int power_down_qam(struct drxk_state *state) +{ + u16 data = 0; + u16 cmd_result; + int status = 0; + + dprintk(1, "\n"); + status = read16(state, SCU_COMM_EXEC__A, &data); + if (status < 0) + goto error; + if (data == SCU_COMM_EXEC_ACTIVE) { + /* + STOP demodulator + QAM and HW blocks + */ + /* stop all comstate->m_exec */ + status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM + | SCU_RAM_COMMAND_CMD_DEMOD_STOP, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + } + /* powerdown AFE */ + status = set_iqm_af(state, false); + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +/*============================================================================*/ + +/* +* \brief Setup of the QAM Measurement intervals for signal quality +* \param demod instance of demod. +* \param modulation current modulation. +* \return DRXStatus_t. +* +* NOTE: +* Take into account that for certain settings the errorcounters can overflow. +* The implementation does not check this. +* +*/ +static int set_qam_measurement(struct drxk_state *state, + enum e_drxk_constellation modulation, + u32 symbol_rate) +{ + u32 fec_bits_desired = 0; /* BER accounting period */ + u32 fec_rs_period_total = 0; /* Total period */ + u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */ + u16 fec_rs_period = 0; /* Value for corresponding I2C register */ + int status = 0; + + dprintk(1, "\n"); + + fec_rs_prescale = 1; + /* fec_bits_desired = symbol_rate [kHz] * + FrameLenght [ms] * + (modulation + 1) * + SyncLoss (== 1) * + ViterbiLoss (==1) + */ + switch (modulation) { + case DRX_CONSTELLATION_QAM16: + fec_bits_desired = 4 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM32: + fec_bits_desired = 5 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM64: + fec_bits_desired = 6 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM128: + fec_bits_desired = 7 * symbol_rate; + break; + case DRX_CONSTELLATION_QAM256: + fec_bits_desired = 8 * symbol_rate; + break; + default: + status = -EINVAL; + } + if (status < 0) + goto error; + + fec_bits_desired /= 1000; /* symbol_rate [Hz] -> symbol_rate [kHz] */ + fec_bits_desired *= 500; /* meas. period [ms] */ + + /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */ + /* fec_rs_period_total = fec_bits_desired / 1632 */ + fec_rs_period_total = (fec_bits_desired / 1632UL) + 1; /* roughly ceil */ + + /* fec_rs_period_total = fec_rs_prescale * fec_rs_period */ + fec_rs_prescale = 1 + (u16) (fec_rs_period_total >> 16); + if (fec_rs_prescale == 0) { + /* Divide by zero (though impossible) */ + status = -EINVAL; + if (status < 0) + goto error; + } + fec_rs_period = + ((u16) fec_rs_period_total + + (fec_rs_prescale >> 1)) / fec_rs_prescale; + + /* write corresponding registers */ + status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fec_rs_period); + if (status < 0) + goto error; + status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, + fec_rs_prescale); + if (status < 0) + goto error; + status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fec_rs_period); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int set_qam16(struct drxk_state *state) +{ + int status = 0; + + dprintk(1, "\n"); + /* QAM Equalizer Setup */ + /* Equalizer */ + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517); + if (status < 0) + goto error; + /* Decision Feedback Equalizer */ + status = write16(state, QAM_DQ_QUAL_FUN0__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN1__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN2__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN3__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN4__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); + if (status < 0) + goto error; + + status = write16(state, QAM_SY_SYNC_HWM__A, 5); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_AWM__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_LWM__A, 3); + if (status < 0) + goto error; + + /* QAM Slicer Settings */ + status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, + DRXK_QAM_SL_SIG_POWER_QAM16); + if (status < 0) + goto error; + + /* QAM Loop Controller Coeficients */ + status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); + if (status < 0) + goto error; + + + /* QAM State Machine (FSM) Thresholds */ + + status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24); + if (status < 0) + goto error; + + + /* QAM FSM Tracking Parameters */ + + status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127); + if (status < 0) + goto error; + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ + +/* +* \brief QAM32 specific setup +* \param demod instance of demod. +* \return DRXStatus_t. +*/ +static int set_qam32(struct drxk_state *state) +{ + int status = 0; + + dprintk(1, "\n"); + + /* QAM Equalizer Setup */ + /* Equalizer */ + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707); + if (status < 0) + goto error; + + /* Decision Feedback Equalizer */ + status = write16(state, QAM_DQ_QUAL_FUN0__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN1__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN2__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN3__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN4__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); + if (status < 0) + goto error; + + status = write16(state, QAM_SY_SYNC_HWM__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_AWM__A, 5); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_LWM__A, 3); + if (status < 0) + goto error; + + /* QAM Slicer Settings */ + + status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, + DRXK_QAM_SL_SIG_POWER_QAM32); + if (status < 0) + goto error; + + + /* QAM Loop Controller Coeficients */ + + status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0); + if (status < 0) + goto error; + + + /* QAM State Machine (FSM) Thresholds */ + + status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10); + if (status < 0) + goto error; + + + /* QAM FSM Tracking Parameters */ + + status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ + +/* +* \brief QAM64 specific setup +* \param demod instance of demod. +* \return DRXStatus_t. +*/ +static int set_qam64(struct drxk_state *state) +{ + int status = 0; + + dprintk(1, "\n"); + /* QAM Equalizer Setup */ + /* Equalizer */ + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609); + if (status < 0) + goto error; + + /* Decision Feedback Equalizer */ + status = write16(state, QAM_DQ_QUAL_FUN0__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN1__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN2__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN3__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN4__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); + if (status < 0) + goto error; + + status = write16(state, QAM_SY_SYNC_HWM__A, 5); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_AWM__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_LWM__A, 3); + if (status < 0) + goto error; + + /* QAM Slicer Settings */ + status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, + DRXK_QAM_SL_SIG_POWER_QAM64); + if (status < 0) + goto error; + + + /* QAM Loop Controller Coeficients */ + + status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); + if (status < 0) + goto error; + + + /* QAM State Machine (FSM) Thresholds */ + + status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15); + if (status < 0) + goto error; + + + /* QAM FSM Tracking Parameters */ + + status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +/*============================================================================*/ + +/* +* \brief QAM128 specific setup +* \param demod: instance of demod. +* \return DRXStatus_t. +*/ +static int set_qam128(struct drxk_state *state) +{ + int status = 0; + + dprintk(1, "\n"); + /* QAM Equalizer Setup */ + /* Equalizer */ + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238); + if (status < 0) + goto error; + + /* Decision Feedback Equalizer */ + status = write16(state, QAM_DQ_QUAL_FUN0__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN1__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN2__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN3__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN4__A, 5); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); + if (status < 0) + goto error; + + status = write16(state, QAM_SY_SYNC_HWM__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_AWM__A, 5); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_LWM__A, 3); + if (status < 0) + goto error; + + + /* QAM Slicer Settings */ + + status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, + DRXK_QAM_SL_SIG_POWER_QAM128); + if (status < 0) + goto error; + + + /* QAM Loop Controller Coeficients */ + + status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0); + if (status < 0) + goto error; + + + /* QAM State Machine (FSM) Thresholds */ + + status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12); + if (status < 0) + goto error; + + /* QAM FSM Tracking Parameters */ + + status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +/*============================================================================*/ + +/* +* \brief QAM256 specific setup +* \param demod: instance of demod. +* \return DRXStatus_t. +*/ +static int set_qam256(struct drxk_state *state) +{ + int status = 0; + + dprintk(1, "\n"); + /* QAM Equalizer Setup */ + /* Equalizer */ + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385); + if (status < 0) + goto error; + + /* Decision Feedback Equalizer */ + status = write16(state, QAM_DQ_QUAL_FUN0__A, 8); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN1__A, 8); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN2__A, 8); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN3__A, 8); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN4__A, 6); + if (status < 0) + goto error; + status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); + if (status < 0) + goto error; + + status = write16(state, QAM_SY_SYNC_HWM__A, 5); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_AWM__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_SY_SYNC_LWM__A, 3); + if (status < 0) + goto error; + + /* QAM Slicer Settings */ + + status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, + DRXK_QAM_SL_SIG_POWER_QAM256); + if (status < 0) + goto error; + + + /* QAM Loop Controller Coeficients */ + + status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); + if (status < 0) + goto error; + + + /* QAM State Machine (FSM) Thresholds */ + + status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110); + if (status < 0) + goto error; + + status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12); + if (status < 0) + goto error; + + + /* QAM FSM Tracking Parameters */ + + status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0); + if (status < 0) + goto error; + status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + + +/*============================================================================*/ +/* +* \brief Reset QAM block. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \return DRXStatus_t. +*/ +static int qam_reset_qam(struct drxk_state *state) +{ + int status; + u16 cmd_result; + + dprintk(1, "\n"); + /* Stop QAM comstate->m_exec */ + status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP); + if (status < 0) + goto error; + + status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM + | SCU_RAM_COMMAND_CMD_DEMOD_RESET, + 0, NULL, 1, &cmd_result); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ + +/* +* \brief Set QAM symbolrate. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \return DRXStatus_t. +*/ +static int qam_set_symbolrate(struct drxk_state *state) +{ + u32 adc_frequency = 0; + u32 symb_freq = 0; + u32 iqm_rc_rate = 0; + u16 ratesel = 0; + u32 lc_symb_rate = 0; + int status; + + dprintk(1, "\n"); + /* Select & calculate correct IQM rate */ + adc_frequency = (state->m_sys_clock_freq * 1000) / 3; + ratesel = 0; + if (state->props.symbol_rate <= 1188750) + ratesel = 3; + else if (state->props.symbol_rate <= 2377500) + ratesel = 2; + else if (state->props.symbol_rate <= 4755000) + ratesel = 1; + status = write16(state, IQM_FD_RATESEL__A, ratesel); + if (status < 0) + goto error; + + /* + IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23) + */ + symb_freq = state->props.symbol_rate * (1 << ratesel); + if (symb_freq == 0) { + /* Divide by zero */ + status = -EINVAL; + goto error; + } + iqm_rc_rate = (adc_frequency / symb_freq) * (1 << 21) + + (Frac28a((adc_frequency % symb_freq), symb_freq) >> 7) - + (1 << 23); + status = write32(state, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate); + if (status < 0) + goto error; + state->m_iqm_rc_rate = iqm_rc_rate; + /* + LcSymbFreq = round (.125 * symbolrate / adc_freq * (1<<15)) + */ + symb_freq = state->props.symbol_rate; + if (adc_frequency == 0) { + /* Divide by zero */ + status = -EINVAL; + goto error; + } + lc_symb_rate = (symb_freq / adc_frequency) * (1 << 12) + + (Frac28a((symb_freq % adc_frequency), adc_frequency) >> + 16); + if (lc_symb_rate > 511) + lc_symb_rate = 511; + status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lc_symb_rate); + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +/*============================================================================*/ + +/* +* \brief Get QAM lock status. +* \param demod: instance of demod. +* \param channel: pointer to channel data. +* \return DRXStatus_t. +*/ + +static int get_qam_lock_status(struct drxk_state *state, u32 *p_lock_status) +{ + int status; + u16 result[2] = { 0, 0 }; + + dprintk(1, "\n"); + *p_lock_status = NOT_LOCKED; + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | + SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2, + result); + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) { + /* 0x0000 NOT LOCKED */ + } else if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) { + /* 0x4000 DEMOD LOCKED */ + *p_lock_status = DEMOD_LOCK; + } else if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) { + /* 0x8000 DEMOD + FEC LOCKED (system lock) */ + *p_lock_status = MPEG_LOCK; + } else { + /* 0xC000 NEVER LOCKED */ + /* (system will never be able to lock to the signal) */ + /* + * TODO: check this, intermediate & standard specific lock + * states are not taken into account here + */ + *p_lock_status = NEVER_LOCK; + } + return status; +} + +#define QAM_MIRROR__M 0x03 +#define QAM_MIRROR_NORMAL 0x00 +#define QAM_MIRRORED 0x01 +#define QAM_MIRROR_AUTO_ON 0x02 +#define QAM_LOCKRANGE__M 0x10 +#define QAM_LOCKRANGE_NORMAL 0x10 + +static int qam_demodulator_command(struct drxk_state *state, + int number_of_parameters) +{ + int status; + u16 cmd_result; + u16 set_param_parameters[4] = { 0, 0, 0, 0 }; + + set_param_parameters[0] = state->m_constellation; /* modulation */ + set_param_parameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ + + if (number_of_parameters == 2) { + u16 set_env_parameters[1] = { 0 }; + + if (state->m_operation_mode == OM_QAM_ITU_C) + set_env_parameters[0] = QAM_TOP_ANNEX_C; + else + set_env_parameters[0] = QAM_TOP_ANNEX_A; + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM + | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, + 1, set_env_parameters, 1, &cmd_result); + if (status < 0) + goto error; + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM + | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, + number_of_parameters, set_param_parameters, + 1, &cmd_result); + } else if (number_of_parameters == 4) { + if (state->m_operation_mode == OM_QAM_ITU_C) + set_param_parameters[2] = QAM_TOP_ANNEX_C; + else + set_param_parameters[2] = QAM_TOP_ANNEX_A; + + set_param_parameters[3] |= (QAM_MIRROR_AUTO_ON); + /* Env parameters */ + /* check for LOCKRANGE Extended */ + /* set_param_parameters[3] |= QAM_LOCKRANGE_NORMAL; */ + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM + | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, + number_of_parameters, set_param_parameters, + 1, &cmd_result); + } else { + pr_warn("Unknown QAM demodulator parameter count %d\n", + number_of_parameters); + status = -EINVAL; + } + +error: + if (status < 0) + pr_warn("Warning %d on %s\n", status, __func__); + return status; +} + +static int set_qam(struct drxk_state *state, u16 intermediate_freqk_hz, + s32 tuner_freq_offset) +{ + int status; + u16 cmd_result; + int qam_demod_param_count = state->qam_demod_parameter_count; + + dprintk(1, "\n"); + /* + * STEP 1: reset demodulator + * resets FEC DI and FEC RS + * resets QAM block + * resets SCU variables + */ + status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = qam_reset_qam(state); + if (status < 0) + goto error; + + /* + * STEP 2: configure demodulator + * -set params; resets IQM,QAM,FEC HW; initializes some + * SCU variables + */ + status = qam_set_symbolrate(state); + if (status < 0) + goto error; + + /* Set params */ + switch (state->props.modulation) { + case QAM_256: + state->m_constellation = DRX_CONSTELLATION_QAM256; + break; + case QAM_AUTO: + case QAM_64: + state->m_constellation = DRX_CONSTELLATION_QAM64; + break; + case QAM_16: + state->m_constellation = DRX_CONSTELLATION_QAM16; + break; + case QAM_32: + state->m_constellation = DRX_CONSTELLATION_QAM32; + break; + case QAM_128: + state->m_constellation = DRX_CONSTELLATION_QAM128; + break; + default: + status = -EINVAL; + break; + } + if (status < 0) + goto error; + + /* Use the 4-parameter if it's requested or we're probing for + * the correct command. */ + if (state->qam_demod_parameter_count == 4 + || !state->qam_demod_parameter_count) { + qam_demod_param_count = 4; + status = qam_demodulator_command(state, qam_demod_param_count); + } + + /* Use the 2-parameter command if it was requested or if we're + * probing for the correct command and the 4-parameter command + * failed. */ + if (state->qam_demod_parameter_count == 2 + || (!state->qam_demod_parameter_count && status < 0)) { + qam_demod_param_count = 2; + status = qam_demodulator_command(state, qam_demod_param_count); + } + + if (status < 0) { + dprintk(1, "Could not set demodulator parameters.\n"); + dprintk(1, + "Make sure qam_demod_parameter_count (%d) is correct for your firmware (%s).\n", + state->qam_demod_parameter_count, + state->microcode_name); + goto error; + } else if (!state->qam_demod_parameter_count) { + dprintk(1, + "Auto-probing the QAM command parameters was successful - using %d parameters.\n", + qam_demod_param_count); + + /* + * One of our commands was successful. We don't need to + * auto-probe anymore, now that we got the correct command. + */ + state->qam_demod_parameter_count = qam_demod_param_count; + } + + /* + * STEP 3: enable the system in a mode where the ADC provides valid + * signal setup modulation independent registers + */ +#if 0 + status = set_frequency(channel, tuner_freq_offset)); + if (status < 0) + goto error; +#endif + status = set_frequency_shifter(state, intermediate_freqk_hz, + tuner_freq_offset, true); + if (status < 0) + goto error; + + /* Setup BER measurement */ + status = set_qam_measurement(state, state->m_constellation, + state->props.symbol_rate); + if (status < 0) + goto error; + + /* Reset default values */ + status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE); + if (status < 0) + goto error; + status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE); + if (status < 0) + goto error; + + /* Reset default LC values */ + status = write16(state, QAM_LC_RATE_LIMIT__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_LC_LPF_FACTORP__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_LC_LPF_FACTORI__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_LC_MODE__A, 7); + if (status < 0) + goto error; + + status = write16(state, QAM_LC_QUAL_TAB0__A, 1); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB1__A, 1); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB2__A, 1); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB3__A, 1); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB4__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB5__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB6__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB8__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB9__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB10__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB12__A, 2); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB15__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB16__A, 3); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB20__A, 4); + if (status < 0) + goto error; + status = write16(state, QAM_LC_QUAL_TAB25__A, 4); + if (status < 0) + goto error; + + /* Mirroring, QAM-block starting point not inverted */ + status = write16(state, QAM_SY_SP_INV__A, + QAM_SY_SP_INV_SPECTRUM_INV_DIS); + if (status < 0) + goto error; + + /* Halt SCU to enable safe non-atomic accesses */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); + if (status < 0) + goto error; + + /* STEP 4: modulation specific setup */ + switch (state->props.modulation) { + case QAM_16: + status = set_qam16(state); + break; + case QAM_32: + status = set_qam32(state); + break; + case QAM_AUTO: + case QAM_64: + status = set_qam64(state); + break; + case QAM_128: + status = set_qam128(state); + break; + case QAM_256: + status = set_qam256(state); + break; + default: + status = -EINVAL; + break; + } + if (status < 0) + goto error; + + /* Activate SCU to enable SCU commands */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + + /* Re-configure MPEG output, requires knowledge of channel bitrate */ + /* extAttr->currentChannel.modulation = channel->modulation; */ + /* extAttr->currentChannel.symbolrate = channel->symbolrate; */ + status = mpegts_dto_setup(state, state->m_operation_mode); + if (status < 0) + goto error; + + /* start processes */ + status = mpegts_start(state); + if (status < 0) + goto error; + status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE); + if (status < 0) + goto error; + + /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */ + status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM + | SCU_RAM_COMMAND_CMD_DEMOD_START, + 0, NULL, 1, &cmd_result); + if (status < 0) + goto error; + + /* update global DRXK data container */ +/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */ + +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int set_qam_standard(struct drxk_state *state, + enum operation_mode o_mode) +{ + int status; +#ifdef DRXK_QAM_TAPS +#define DRXK_QAMA_TAPS_SELECT +#include "drxk_filters.h" +#undef DRXK_QAMA_TAPS_SELECT +#endif + + dprintk(1, "\n"); + + /* added antenna switch */ + switch_antenna_to_qam(state); + + /* Ensure correct power-up mode */ + status = power_up_qam(state); + if (status < 0) + goto error; + /* Reset QAM block */ + status = qam_reset_qam(state); + if (status < 0) + goto error; + + /* Setup IQM */ + + status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); + if (status < 0) + goto error; + status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC); + if (status < 0) + goto error; + + /* Upload IQM Channel Filter settings by + boot loader from ROM table */ + switch (o_mode) { + case OM_QAM_ITU_A: + status = bl_chain_cmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, + DRXK_BLCC_NR_ELEMENTS_TAPS, + DRXK_BLC_TIMEOUT); + break; + case OM_QAM_ITU_C: + status = bl_direct_cmd(state, IQM_CF_TAP_RE0__A, + DRXK_BL_ROM_OFFSET_TAPS_ITU_C, + DRXK_BLDC_NR_ELEMENTS_TAPS, + DRXK_BLC_TIMEOUT); + if (status < 0) + goto error; + status = bl_direct_cmd(state, + IQM_CF_TAP_IM0__A, + DRXK_BL_ROM_OFFSET_TAPS_ITU_C, + DRXK_BLDC_NR_ELEMENTS_TAPS, + DRXK_BLC_TIMEOUT); + break; + default: + status = -EINVAL; + } + if (status < 0) + goto error; + + status = write16(state, IQM_CF_OUT_ENA__A, 1 << IQM_CF_OUT_ENA_QAM__B); + if (status < 0) + goto error; + status = write16(state, IQM_CF_SYMMETRIC__A, 0); + if (status < 0) + goto error; + status = write16(state, IQM_CF_MIDTAP__A, + ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B))); + if (status < 0) + goto error; + + status = write16(state, IQM_RC_STRETCH__A, 21); + if (status < 0) + goto error; + status = write16(state, IQM_AF_CLP_LEN__A, 0); + if (status < 0) + goto error; + status = write16(state, IQM_AF_CLP_TH__A, 448); + if (status < 0) + goto error; + status = write16(state, IQM_AF_SNS_LEN__A, 0); + if (status < 0) + goto error; + status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0); + if (status < 0) + goto error; + + status = write16(state, IQM_FS_ADJ_SEL__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_RC_ADJ_SEL__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_CF_ADJ_SEL__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_AF_UPD_SEL__A, 0); + if (status < 0) + goto error; + + /* IQM Impulse Noise Processing Unit */ + status = write16(state, IQM_CF_CLP_VAL__A, 500); + if (status < 0) + goto error; + status = write16(state, IQM_CF_DATATH__A, 1000); + if (status < 0) + goto error; + status = write16(state, IQM_CF_BYPASSDET__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_CF_DET_LCT__A, 0); + if (status < 0) + goto error; + status = write16(state, IQM_CF_WND_LEN__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_CF_PKDTH__A, 1); + if (status < 0) + goto error; + status = write16(state, IQM_AF_INC_BYPASS__A, 1); + if (status < 0) + goto error; + + /* turn on IQMAF. Must be done before setAgc**() */ + status = set_iqm_af(state, true); + if (status < 0) + goto error; + status = write16(state, IQM_AF_START_LOCK__A, 0x01); + if (status < 0) + goto error; + + /* IQM will not be reset from here, sync ADC and update/init AGC */ + status = adc_synchronization(state); + if (status < 0) + goto error; + + /* Set the FSM step period */ + status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000); + if (status < 0) + goto error; + + /* Halt SCU to enable safe non-atomic accesses */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); + if (status < 0) + goto error; + + /* No more resets of the IQM, current standard correctly set => + now AGCs can be configured. */ + + status = init_agc(state, true); + if (status < 0) + goto error; + status = set_pre_saw(state, &(state->m_qam_pre_saw_cfg)); + if (status < 0) + goto error; + + /* Configure AGC's */ + status = set_agc_rf(state, &(state->m_qam_rf_agc_cfg), true); + if (status < 0) + goto error; + status = set_agc_if(state, &(state->m_qam_if_agc_cfg), true); + if (status < 0) + goto error; + + /* Activate SCU to enable SCU commands */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int write_gpio(struct drxk_state *state) +{ + int status; + u16 value = 0; + + dprintk(1, "\n"); + /* stop lock indicator process */ + status = write16(state, SCU_RAM_GPIO__A, + SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); + if (status < 0) + goto error; + + /* Write magic word to enable pdr reg write */ + status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); + if (status < 0) + goto error; + + if (state->m_has_sawsw) { + if (state->uio_mask & 0x0001) { /* UIO-1 */ + /* write to io pad configuration register - output mode */ + status = write16(state, SIO_PDR_SMA_TX_CFG__A, + state->m_gpio_cfg); + if (status < 0) + goto error; + + /* use corresponding bit in io data output registar */ + status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); + if (status < 0) + goto error; + if ((state->m_gpio & 0x0001) == 0) + value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */ + else + value |= 0x8000; /* write one to 15th bit - 1st UIO */ + /* write back to io data output register */ + status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); + if (status < 0) + goto error; + } + if (state->uio_mask & 0x0002) { /* UIO-2 */ + /* write to io pad configuration register - output mode */ + status = write16(state, SIO_PDR_SMA_RX_CFG__A, + state->m_gpio_cfg); + if (status < 0) + goto error; + + /* use corresponding bit in io data output registar */ + status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); + if (status < 0) + goto error; + if ((state->m_gpio & 0x0002) == 0) + value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */ + else + value |= 0x4000; /* write one to 14th bit - 2st UIO */ + /* write back to io data output register */ + status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); + if (status < 0) + goto error; + } + if (state->uio_mask & 0x0004) { /* UIO-3 */ + /* write to io pad configuration register - output mode */ + status = write16(state, SIO_PDR_GPIO_CFG__A, + state->m_gpio_cfg); + if (status < 0) + goto error; + + /* use corresponding bit in io data output registar */ + status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); + if (status < 0) + goto error; + if ((state->m_gpio & 0x0004) == 0) + value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */ + else + value |= 0x0004; /* write one to 2nd bit - 3rd UIO */ + /* write back to io data output register */ + status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); + if (status < 0) + goto error; + } + } + /* Write magic word to disable pdr reg write */ + status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int switch_antenna_to_qam(struct drxk_state *state) +{ + int status = 0; + bool gpio_state; + + dprintk(1, "\n"); + + if (!state->antenna_gpio) + return 0; + + gpio_state = state->m_gpio & state->antenna_gpio; + + if (state->antenna_dvbt ^ gpio_state) { + /* Antenna is on DVB-T mode. Switch */ + if (state->antenna_dvbt) + state->m_gpio &= ~state->antenna_gpio; + else + state->m_gpio |= state->antenna_gpio; + status = write_gpio(state); + } + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + +static int switch_antenna_to_dvbt(struct drxk_state *state) +{ + int status = 0; + bool gpio_state; + + dprintk(1, "\n"); + + if (!state->antenna_gpio) + return 0; + + gpio_state = state->m_gpio & state->antenna_gpio; + + if (!(state->antenna_dvbt ^ gpio_state)) { + /* Antenna is on DVB-C mode. Switch */ + if (state->antenna_dvbt) + state->m_gpio |= state->antenna_gpio; + else + state->m_gpio &= ~state->antenna_gpio; + status = write_gpio(state); + } + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + return status; +} + + +static int power_down_device(struct drxk_state *state) +{ + /* Power down to requested mode */ + /* Backup some register settings */ + /* Set pins with possible pull-ups connected to them in input mode */ + /* Analog power down */ + /* ADC power down */ + /* Power down device */ + int status; + + dprintk(1, "\n"); + if (state->m_b_p_down_open_bridge) { + /* Open I2C bridge before power down of DRXK */ + status = ConfigureI2CBridge(state, true); + if (status < 0) + goto error; + } + /* driver 0.9.0 */ + status = dvbt_enable_ofdm_token_ring(state, false); + if (status < 0) + goto error; + + status = write16(state, SIO_CC_PWD_MODE__A, + SIO_CC_PWD_MODE_LEVEL_CLOCK); + if (status < 0) + goto error; + status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); + if (status < 0) + goto error; + state->m_hi_cfg_ctrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; + status = hi_cfg_command(state); +error: + if (status < 0) + pr_err("Error %d on %s\n", status, __func__); + + return status; +} + +static int init_drxk(struct drxk_state *state) +{ + int status = 0, n = 0; + enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM; + u16 driver_version; + + dprintk(1, "\n"); + if (state->m_drxk_state == DRXK_UNINITIALIZED) { + drxk_i2c_lock(state); + status = power_up_device(state); + if (status < 0) + goto error; + status = drxx_open(state); + if (status < 0) + goto error; + /* Soft reset of OFDM-, sys- and osc-clockdomain */ + status = write16(state, SIO_CC_SOFT_RST__A, + SIO_CC_SOFT_RST_OFDM__M + | SIO_CC_SOFT_RST_SYS__M + | SIO_CC_SOFT_RST_OSC__M); + if (status < 0) + goto error; + status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); + if (status < 0) + goto error; + /* + * TODO is this needed? If yes, how much delay in + * worst case scenario + */ + usleep_range(1000, 2000); + state->m_drxk_a3_patch_code = true; + status = get_device_capabilities(state); + if (status < 0) + goto error; + + /* Bridge delay, uses oscilator clock */ + /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */ + /* SDA brdige delay */ + state->m_hi_cfg_bridge_delay = + (u16) ((state->m_osc_clock_freq / 1000) * + HI_I2C_BRIDGE_DELAY) / 1000; + /* Clipping */ + if (state->m_hi_cfg_bridge_delay > + SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) { + state->m_hi_cfg_bridge_delay = + SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M; + } + /* SCL bridge delay, same as SDA for now */ + state->m_hi_cfg_bridge_delay += + state->m_hi_cfg_bridge_delay << + SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B; + + status = init_hi(state); + if (status < 0) + goto error; + /* disable various processes */ +#if NOA1ROM + if (!(state->m_DRXK_A1_ROM_CODE) + && !(state->m_DRXK_A2_ROM_CODE)) +#endif + { + status = write16(state, SCU_RAM_GPIO__A, + SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); + if (status < 0) + goto error; + } + + /* disable MPEG port */ + status = mpegts_disable(state); + if (status < 0) + goto error; + + /* Stop AUD and SCU */ + status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP); + if (status < 0) + goto error; + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP); + if (status < 0) + goto error; + + /* enable token-ring bus through OFDM block for possible ucode upload */ + status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, + SIO_OFDM_SH_OFDM_RING_ENABLE_ON); + if (status < 0) + goto error; + + /* include boot loader section */ + status = write16(state, SIO_BL_COMM_EXEC__A, + SIO_BL_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + status = bl_chain_cmd(state, 0, 6, 100); + if (status < 0) + goto error; + + if (state->fw) { + status = download_microcode(state, state->fw->data, + state->fw->size); + if (status < 0) + goto error; + } + + /* disable token-ring bus through OFDM block for possible ucode upload */ + status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, + SIO_OFDM_SH_OFDM_RING_ENABLE_OFF); + if (status < 0) + goto error; + + /* Run SCU for a little while to initialize microcode version numbers */ + status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); + if (status < 0) + goto error; + status = drxx_open(state); + if (status < 0) + goto error; + /* added for test */ + msleep(30); + + power_mode = DRXK_POWER_DOWN_OFDM; + status = ctrl_power_mode(state, &power_mode); + if (status < 0) + goto error; + + /* Stamp driver version number in SCU data RAM in BCD code + Done to enable field application engineers to retrieve drxdriver version + via I2C from SCU RAM. + Not using SCU command interface for SCU register access since no + microcode may be present. + */ + driver_version = + (((DRXK_VERSION_MAJOR / 100) % 10) << 12) + + (((DRXK_VERSION_MAJOR / 10) % 10) << 8) + + ((DRXK_VERSION_MAJOR % 10) << 4) + + (DRXK_VERSION_MINOR % 10); + status = write16(state, SCU_RAM_DRIVER_VER_HI__A, + driver_version); + if (status < 0) + goto error; + driver_version = + (((DRXK_VERSION_PATCH / 1000) % 10) << 12) + + (((DRXK_VERSION_PATCH / 100) % 10) << 8) + + (((DRXK_VERSION_PATCH / 10) % 10) << 4) + + (DRXK_VERSION_PATCH % 10); + status = write16(state, SCU_RAM_DRIVER_VER_LO__A, + driver_version); + if (status < 0) + goto error; + + pr_info("DRXK driver version %d.%d.%d\n", + DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR, + DRXK_VERSION_PATCH); + + /* + * Dirty fix of default values for ROM/PATCH microcode + * Dirty because this fix makes it impossible to setup + * suitable values before calling DRX_Open. This solution + * requires changes to RF AGC speed to be done via the CTRL + * function after calling DRX_Open + */ + + /* m_dvbt_rf_agc_cfg.speed = 3; */ + + /* Reset driver debug flags to 0 */ + status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0); + if (status < 0) + goto error; + /* driver 0.9.0 */ + /* Setup FEC OC: + NOTE: No more full FEC resets allowed afterwards!! */ + status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP); + if (status < 0) + goto error; + /* MPEGTS functions are still the same */ + status = mpegts_dto_init(state); + if (status < 0) + goto error; + status = mpegts_stop(state); + if (status < 0) + goto error; + status = mpegts_configure_polarity(state); + if (status < 0) + goto error; + status = mpegts_configure_pins(state, state->m_enable_mpeg_output); + if (status < 0) + goto error; + /* added: configure GPIO */ + status = write_gpio(state); + if (status < 0) + goto error; + + state->m_drxk_state = DRXK_STOPPED; + + if (state->m_b_power_down) { + status = power_down_device(state); + if (status < 0) + goto error; + state->m_drxk_state = DRXK_POWERED_DOWN; + } else + state->m_drxk_state = DRXK_STOPPED; + + /* Initialize the supported delivery systems */ + n = 0; + if (state->m_has_dvbc) { + state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; + state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; + strlcat(state->frontend.ops.info.name, " DVB-C", + sizeof(state->frontend.ops.info.name)); + } + if (state->m_has_dvbt) { + state->frontend.ops.delsys[n++] = SYS_DVBT; + strlcat(state->frontend.ops.info.name, " DVB-T", + sizeof(state->frontend.ops.info.name)); + } + drxk_i2c_unlock(state); + } +error: + if (status < 0) { + state->m_drxk_state = DRXK_NO_DEV; + drxk_i2c_unlock(state); + pr_err("Error %d on %s\n", status, __func__); + } + + return status; +} + +static void load_firmware_cb(const struct firmware *fw, + void *context) +{ + struct drxk_state *state = context; + + dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded"); + if (!fw) { + pr_err("Could not load firmware file %s.\n", + state->microcode_name); + pr_info("Copy %s to your hotplug directory!\n", + state->microcode_name); + state->microcode_name = NULL; + + /* + * As firmware is now load asynchronous, it is not possible + * anymore to fail at frontend attach. We might silently + * return here, and hope that the driver won't crash. + * We might also change all DVB callbacks to return -ENODEV + * if the device is not initialized. + * As the DRX-K devices have their own internal firmware, + * let's just hope that it will match a firmware revision + * compatible with this driver and proceed. + */ + } + state->fw = fw; + + init_drxk(state); +} + +static void drxk_release(struct dvb_frontend *fe) +{ + struct drxk_state *state = fe->demodulator_priv; + + dprintk(1, "\n"); + release_firmware(state->fw); + + kfree(state); +} + +static int drxk_sleep(struct dvb_frontend *fe) +{ + struct drxk_state *state = fe->demodulator_priv; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return 0; + + shut_down(state); + return 0; +} + +static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct drxk_state *state = fe->demodulator_priv; + + dprintk(1, ": %s\n", enable ? "enable" : "disable"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + + return ConfigureI2CBridge(state, enable ? true : false); +} + +static int drxk_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 delsys = p->delivery_system, old_delsys; + struct drxk_state *state = fe->demodulator_priv; + u32 IF; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return -EAGAIN; + + if (!fe->ops.tuner_ops.get_if_frequency) { + pr_err("Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); + return -EINVAL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + old_delsys = state->props.delivery_system; + state->props = *p; + + if (old_delsys != delsys) { + shut_down(state); + switch (delsys) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_C: + if (!state->m_has_dvbc) + return -EINVAL; + state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ? + true : false; + if (state->m_itut_annex_c) + setoperation_mode(state, OM_QAM_ITU_C); + else + setoperation_mode(state, OM_QAM_ITU_A); + break; + case SYS_DVBT: + if (!state->m_has_dvbt) + return -EINVAL; + setoperation_mode(state, OM_DVBT); + break; + default: + return -EINVAL; + } + } + + fe->ops.tuner_ops.get_if_frequency(fe, &IF); + start(state, 0, IF); + + /* After set_frontend, stats aren't available */ + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */ + + return 0; +} + +static int get_strength(struct drxk_state *state, u64 *strength) +{ + int status; + struct s_cfg_agc rf_agc, if_agc; + u32 total_gain = 0; + u32 atten = 0; + u32 agc_range = 0; + u16 scu_lvl = 0; + u16 scu_coc = 0; + /* FIXME: those are part of the tuner presets */ + u16 tuner_rf_gain = 50; /* Default value on az6007 driver */ + u16 tuner_if_gain = 40; /* Default value on az6007 driver */ + + *strength = 0; + + if (is_dvbt(state)) { + rf_agc = state->m_dvbt_rf_agc_cfg; + if_agc = state->m_dvbt_if_agc_cfg; + } else if (is_qam(state)) { + rf_agc = state->m_qam_rf_agc_cfg; + if_agc = state->m_qam_if_agc_cfg; + } else { + rf_agc = state->m_atv_rf_agc_cfg; + if_agc = state->m_atv_if_agc_cfg; + } + + if (rf_agc.ctrl_mode == DRXK_AGC_CTRL_AUTO) { + /* SCU output_level */ + status = read16(state, SCU_RAM_AGC_RF_IACCU_HI__A, &scu_lvl); + if (status < 0) + return status; + + /* SCU c.o.c. */ + status = read16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, &scu_coc); + if (status < 0) + return status; + + if (((u32) scu_lvl + (u32) scu_coc) < 0xffff) + rf_agc.output_level = scu_lvl + scu_coc; + else + rf_agc.output_level = 0xffff; + + /* Take RF gain into account */ + total_gain += tuner_rf_gain; + + /* clip output value */ + if (rf_agc.output_level < rf_agc.min_output_level) + rf_agc.output_level = rf_agc.min_output_level; + if (rf_agc.output_level > rf_agc.max_output_level) + rf_agc.output_level = rf_agc.max_output_level; + + agc_range = (u32) (rf_agc.max_output_level - rf_agc.min_output_level); + if (agc_range > 0) { + atten += 100UL * + ((u32)(tuner_rf_gain)) * + ((u32)(rf_agc.output_level - rf_agc.min_output_level)) + / agc_range; + } + } + + if (if_agc.ctrl_mode == DRXK_AGC_CTRL_AUTO) { + status = read16(state, SCU_RAM_AGC_IF_IACCU_HI__A, + &if_agc.output_level); + if (status < 0) + return status; + + status = read16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, + &if_agc.top); + if (status < 0) + return status; + + /* Take IF gain into account */ + total_gain += (u32) tuner_if_gain; + + /* clip output value */ + if (if_agc.output_level < if_agc.min_output_level) + if_agc.output_level = if_agc.min_output_level; + if (if_agc.output_level > if_agc.max_output_level) + if_agc.output_level = if_agc.max_output_level; + + agc_range = (u32)(if_agc.max_output_level - if_agc.min_output_level); + if (agc_range > 0) { + atten += 100UL * + ((u32)(tuner_if_gain)) * + ((u32)(if_agc.output_level - if_agc.min_output_level)) + / agc_range; + } + } + + /* + * Convert to 0..65535 scale. + * If it can't be measured (AGC is disabled), just show 100%. + */ + if (total_gain > 0) + *strength = (65535UL * atten / total_gain / 100); + else + *strength = 65535; + + return 0; +} + +static int drxk_get_stats(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct drxk_state *state = fe->demodulator_priv; + int status; + u32 stat; + u16 reg16; + u32 post_bit_count; + u32 post_bit_err_count; + u32 post_bit_error_scale; + u32 pre_bit_err_count; + u32 pre_bit_count; + u32 pkt_count; + u32 pkt_error_count; + s32 cnr; + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return -EAGAIN; + + /* get status */ + state->fe_status = 0; + get_lock_status(state, &stat); + if (stat == MPEG_LOCK) + state->fe_status |= 0x1f; + if (stat == FEC_LOCK) + state->fe_status |= 0x0f; + if (stat == DEMOD_LOCK) + state->fe_status |= 0x07; + + /* + * Estimate signal strength from AGC + */ + get_strength(state, &c->strength.stat[0].uvalue); + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + + + if (stat >= DEMOD_LOCK) { + get_signal_to_noise(state, &cnr); + c->cnr.stat[0].svalue = cnr * 100; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + if (stat < FEC_LOCK) { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + /* Get post BER */ + + /* BER measurement is valid if at least FEC lock is achieved */ + + /* + * OFDM_EC_VD_REQ_SMB_CNT__A and/or OFDM_EC_VD_REQ_BIT_CNT can be + * written to set nr of symbols or bits over which to measure + * EC_VD_REG_ERR_BIT_CNT__A . See CtrlSetCfg(). + */ + + /* Read registers for post/preViterbi BER calculation */ + status = read16(state, OFDM_EC_VD_ERR_BIT_CNT__A, ®16); + if (status < 0) + goto error; + pre_bit_err_count = reg16; + + status = read16(state, OFDM_EC_VD_IN_BIT_CNT__A , ®16); + if (status < 0) + goto error; + pre_bit_count = reg16; + + /* Number of bit-errors */ + status = read16(state, FEC_RS_NR_BIT_ERRORS__A, ®16); + if (status < 0) + goto error; + post_bit_err_count = reg16; + + status = read16(state, FEC_RS_MEASUREMENT_PRESCALE__A, ®16); + if (status < 0) + goto error; + post_bit_error_scale = reg16; + + status = read16(state, FEC_RS_MEASUREMENT_PERIOD__A, ®16); + if (status < 0) + goto error; + pkt_count = reg16; + + status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, ®16); + if (status < 0) + goto error; + pkt_error_count = reg16; + write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0); + + post_bit_err_count *= post_bit_error_scale; + + post_bit_count = pkt_count * 204 * 8; + + /* Store the results */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += pkt_error_count; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += pkt_count; + + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue += pre_bit_err_count; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue += pre_bit_count; + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += post_bit_err_count; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += post_bit_count; + +error: + return status; +} + + +static int drxk_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct drxk_state *state = fe->demodulator_priv; + int rc; + + dprintk(1, "\n"); + + rc = drxk_get_stats(fe); + if (rc < 0) + return rc; + + *status = state->fe_status; + + return 0; +} + +static int drxk_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct drxk_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return -EAGAIN; + + *strength = c->strength.stat[0].uvalue; + return 0; +} + +static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct drxk_state *state = fe->demodulator_priv; + s32 snr2; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return -EAGAIN; + + get_signal_to_noise(state, &snr2); + + /* No negative SNR, clip to zero */ + if (snr2 < 0) + snr2 = 0; + *snr = snr2 & 0xffff; + return 0; +} + +static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct drxk_state *state = fe->demodulator_priv; + u16 err = 0; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return -EAGAIN; + + dvbtqam_get_acc_pkt_err(state, &err); + *ucblocks = (u32) err; + return 0; +} + +static int drxk_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *sets) +{ + struct drxk_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dprintk(1, "\n"); + + if (state->m_drxk_state == DRXK_NO_DEV) + return -ENODEV; + if (state->m_drxk_state == DRXK_UNINITIALIZED) + return -EAGAIN; + + switch (p->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_C: + case SYS_DVBT: + sets->min_delay_ms = 3000; + sets->max_drift = 0; + sets->step_size = 0; + return 0; + default: + return -EINVAL; + } +} + +static const struct dvb_frontend_ops drxk_ops = { + /* .delsys will be filled dynamically */ + .info = { + .name = "DRXK", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 865 * MHz, + /* For DVB-C */ + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + /* For DVB-T */ + .frequency_stepsize_hz = 166667, + + .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_MUTE_TS | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO + }, + + .release = drxk_release, + .sleep = drxk_sleep, + .i2c_gate_ctrl = drxk_gate_ctrl, + + .set_frontend = drxk_set_parameters, + .get_tune_settings = drxk_get_tune_settings, + + .read_status = drxk_read_status, + .read_signal_strength = drxk_read_signal_strength, + .read_snr = drxk_read_snr, + .read_ucblocks = drxk_read_ucblocks, +}; + +struct dvb_frontend *drxk_attach(const struct drxk_config *config, + struct i2c_adapter *i2c) +{ + struct dtv_frontend_properties *p; + struct drxk_state *state = NULL; + u8 adr = config->adr; + int status; + + dprintk(1, "\n"); + state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL); + if (!state) + return NULL; + + state->i2c = i2c; + state->demod_address = adr; + state->single_master = config->single_master; + state->microcode_name = config->microcode_name; + state->qam_demod_parameter_count = config->qam_demod_parameter_count; + state->no_i2c_bridge = config->no_i2c_bridge; + state->antenna_gpio = config->antenna_gpio; + state->antenna_dvbt = config->antenna_dvbt; + state->m_chunk_size = config->chunk_size; + state->enable_merr_cfg = config->enable_merr_cfg; + + if (config->dynamic_clk) { + state->m_dvbt_static_clk = false; + state->m_dvbc_static_clk = false; + } else { + state->m_dvbt_static_clk = true; + state->m_dvbc_static_clk = true; + } + + + if (config->mpeg_out_clk_strength) + state->m_ts_clockk_strength = config->mpeg_out_clk_strength & 0x07; + else + state->m_ts_clockk_strength = 0x06; + + if (config->parallel_ts) + state->m_enable_parallel = true; + else + state->m_enable_parallel = false; + + /* NOTE: as more UIO bits will be used, add them to the mask */ + state->uio_mask = config->antenna_gpio; + + /* Default gpio to DVB-C */ + if (!state->antenna_dvbt && state->antenna_gpio) + state->m_gpio |= state->antenna_gpio; + else + state->m_gpio &= ~state->antenna_gpio; + + mutex_init(&state->mutex); + + memcpy(&state->frontend.ops, &drxk_ops, sizeof(drxk_ops)); + state->frontend.demodulator_priv = state; + + init_state(state); + + /* Load firmware and initialize DRX-K */ + if (state->microcode_name) { + const struct firmware *fw = NULL; + + status = request_firmware(&fw, state->microcode_name, + state->i2c->dev.parent); + if (status < 0) + fw = NULL; + load_firmware_cb(fw, state); + } else if (init_drxk(state) < 0) + goto error; + + + /* Initialize stats */ + p = &state->frontend.dtv_property_cache; + p->strength.len = 1; + p->cnr.len = 1; + p->block_error.len = 1; + p->block_count.len = 1; + p->pre_bit_error.len = 1; + p->pre_bit_count.len = 1; + p->post_bit_error.len = 1; + p->post_bit_count.len = 1; + + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + pr_info("frontend initialized.\n"); + return &state->frontend; + +error: + pr_err("not found\n"); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(drxk_attach); + +MODULE_DESCRIPTION("DRX-K driver"); +MODULE_AUTHOR("Ralph Metzler"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h new file mode 100644 index 0000000000..a850a876de --- /dev/null +++ b/drivers/media/dvb-frontends/drxk_hard.h @@ -0,0 +1,368 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "drxk_map.h" + +#define DRXK_VERSION_MAJOR 0 +#define DRXK_VERSION_MINOR 9 +#define DRXK_VERSION_PATCH 4300 + +#define HI_I2C_DELAY 42 +#define HI_I2C_BRIDGE_DELAY 350 +#define DRXK_MAX_RETRIES 100 + +#define DRIVER_4400 1 + +#define DRXX_JTAGID 0x039210D9 +#define DRXX_J_JTAGID 0x239310D9 +#define DRXX_K_JTAGID 0x039210D9 + +#define DRX_UNKNOWN 254 +#define DRX_AUTO 255 + +#define DRX_SCU_READY 0 +#define DRXK_MAX_WAITTIME (200) +#define SCU_RESULT_OK 0 +#define SCU_RESULT_SIZE -4 +#define SCU_RESULT_INVPAR -3 +#define SCU_RESULT_UNKSTD -2 +#define SCU_RESULT_UNKCMD -1 + +#ifndef DRXK_OFDM_TR_SHUTDOWN_TIMEOUT +#define DRXK_OFDM_TR_SHUTDOWN_TIMEOUT (200) +#endif + +#define DRXK_8VSB_MPEG_BIT_RATE 19392658UL /*bps*/ +#define DRXK_DVBT_MPEG_BIT_RATE 32000000UL /*bps*/ +#define DRXK_QAM16_MPEG_BIT_RATE 27000000UL /*bps*/ +#define DRXK_QAM32_MPEG_BIT_RATE 33000000UL /*bps*/ +#define DRXK_QAM64_MPEG_BIT_RATE 40000000UL /*bps*/ +#define DRXK_QAM128_MPEG_BIT_RATE 46000000UL /*bps*/ +#define DRXK_QAM256_MPEG_BIT_RATE 52000000UL /*bps*/ +#define DRXK_MAX_MPEG_BIT_RATE 52000000UL /*bps*/ + +#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 + +enum operation_mode { + OM_NONE, + OM_QAM_ITU_A, + OM_QAM_ITU_B, + OM_QAM_ITU_C, + OM_DVBT +}; + +enum drx_power_mode { + DRX_POWER_UP = 0, + DRX_POWER_MODE_1, + DRX_POWER_MODE_2, + DRX_POWER_MODE_3, + DRX_POWER_MODE_4, + DRX_POWER_MODE_5, + DRX_POWER_MODE_6, + DRX_POWER_MODE_7, + DRX_POWER_MODE_8, + + DRX_POWER_MODE_9, + DRX_POWER_MODE_10, + DRX_POWER_MODE_11, + DRX_POWER_MODE_12, + DRX_POWER_MODE_13, + DRX_POWER_MODE_14, + DRX_POWER_MODE_15, + DRX_POWER_MODE_16, + DRX_POWER_DOWN = 255 +}; + + +/* Intermediate power mode for DRXK, power down OFDM clock domain */ +#ifndef DRXK_POWER_DOWN_OFDM +#define DRXK_POWER_DOWN_OFDM DRX_POWER_MODE_1 +#endif + +/* Intermediate power mode for DRXK, power down core (sysclk) */ +#ifndef DRXK_POWER_DOWN_CORE +#define DRXK_POWER_DOWN_CORE DRX_POWER_MODE_9 +#endif + +/* Intermediate power mode for DRXK, power down pll (only osc runs) */ +#ifndef DRXK_POWER_DOWN_PLL +#define DRXK_POWER_DOWN_PLL DRX_POWER_MODE_10 +#endif + + +enum agc_ctrl_mode { + DRXK_AGC_CTRL_AUTO = 0, + DRXK_AGC_CTRL_USER, + DRXK_AGC_CTRL_OFF +}; + +enum e_drxk_state { + DRXK_UNINITIALIZED = 0, + DRXK_STOPPED, + DRXK_DTV_STARTED, + DRXK_ATV_STARTED, + DRXK_POWERED_DOWN, + DRXK_NO_DEV /* If drxk init failed */ +}; + +enum e_drxk_coef_array_index { + DRXK_COEF_IDX_MN = 0, + DRXK_COEF_IDX_FM , + DRXK_COEF_IDX_L , + DRXK_COEF_IDX_LP , + DRXK_COEF_IDX_BG , + DRXK_COEF_IDX_DK , + DRXK_COEF_IDX_I , + DRXK_COEF_IDX_MAX +}; +enum e_drxk_sif_attenuation { + DRXK_SIF_ATTENUATION_0DB, + DRXK_SIF_ATTENUATION_3DB, + DRXK_SIF_ATTENUATION_6DB, + DRXK_SIF_ATTENUATION_9DB +}; +enum e_drxk_constellation { + DRX_CONSTELLATION_BPSK = 0, + DRX_CONSTELLATION_QPSK, + DRX_CONSTELLATION_PSK8, + DRX_CONSTELLATION_QAM16, + DRX_CONSTELLATION_QAM32, + DRX_CONSTELLATION_QAM64, + DRX_CONSTELLATION_QAM128, + DRX_CONSTELLATION_QAM256, + DRX_CONSTELLATION_QAM512, + DRX_CONSTELLATION_QAM1024, + DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN, + DRX_CONSTELLATION_AUTO = DRX_AUTO +}; +enum e_drxk_interleave_mode { + DRXK_QAM_I12_J17 = 16, + DRXK_QAM_I_UNKNOWN = DRX_UNKNOWN +}; +enum { + DRXK_SPIN_A1 = 0, + DRXK_SPIN_A2, + DRXK_SPIN_A3, + DRXK_SPIN_UNKNOWN +}; + +enum drxk_cfg_dvbt_sqi_speed { + DRXK_DVBT_SQI_SPEED_FAST = 0, + DRXK_DVBT_SQI_SPEED_MEDIUM, + DRXK_DVBT_SQI_SPEED_SLOW, + DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN +} ; + +enum drx_fftmode_t { + DRX_FFTMODE_2K = 0, + DRX_FFTMODE_4K, + DRX_FFTMODE_8K, + DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN, + DRX_FFTMODE_AUTO = DRX_AUTO +}; + +enum drxmpeg_str_width_t { + DRX_MPEG_STR_WIDTH_1, + DRX_MPEG_STR_WIDTH_8 +}; + +enum drx_qam_lock_range_t { + DRX_QAM_LOCKRANGE_NORMAL, + DRX_QAM_LOCKRANGE_EXTENDED +}; + +struct drxk_cfg_dvbt_echo_thres_t { + u16 threshold; + enum drx_fftmode_t fft_mode; +} ; + +struct s_cfg_agc { + enum agc_ctrl_mode ctrl_mode; /* off, user, auto */ + u16 output_level; /* range dependent on AGC */ + u16 min_output_level; /* range dependent on AGC */ + u16 max_output_level; /* range dependent on AGC */ + u16 speed; /* range dependent on AGC */ + u16 top; /* rf-agc take over point */ + u16 cut_off_current; /* rf-agc is accelerated if output current + is below cut-off current */ + u16 ingain_tgt_max; + u16 fast_clip_ctrl_delay; +}; + +struct s_cfg_pre_saw { + u16 reference; /* pre SAW reference value, range 0 .. 31 */ + bool use_pre_saw; /* TRUE algorithms must use pre SAW sense */ +}; + +struct drxk_ofdm_sc_cmd_t { + u16 cmd; /* Command number */ + u16 subcmd; /* Sub-command parameter*/ + u16 param0; /* General purpous param */ + u16 param1; /* General purpous param */ + u16 param2; /* General purpous param */ + u16 param3; /* General purpous param */ + u16 param4; /* General purpous param */ +}; + +struct drxk_state { + struct dvb_frontend frontend; + struct dtv_frontend_properties props; + struct device *dev; + + struct i2c_adapter *i2c; + u8 demod_address; + void *priv; + + struct mutex mutex; + + u32 m_instance; /* Channel 1,2,3 or 4 */ + + int m_chunk_size; + u8 chunk[256]; + + bool m_has_lna; + bool m_has_dvbt; + bool m_has_dvbc; + bool m_has_audio; + bool m_has_atv; + bool m_has_oob; + bool m_has_sawsw; /* TRUE if mat_tx is available */ + bool m_has_gpio1; /* TRUE if mat_rx is available */ + bool m_has_gpio2; /* TRUE if GPIO is available */ + bool m_has_irqn; /* TRUE if IRQN is available */ + u16 m_osc_clock_freq; + u16 m_hi_cfg_timing_div; + u16 m_hi_cfg_bridge_delay; + u16 m_hi_cfg_wake_up_key; + u16 m_hi_cfg_timeout; + u16 m_hi_cfg_ctrl; + s32 m_sys_clock_freq; /* system clock frequency in kHz */ + + enum e_drxk_state m_drxk_state; /* State of Drxk (init,stopped,started) */ + enum operation_mode m_operation_mode; /* digital standards */ + struct s_cfg_agc m_vsb_rf_agc_cfg; /* settings for VSB RF-AGC */ + struct s_cfg_agc m_vsb_if_agc_cfg; /* settings for VSB IF-AGC */ + u16 m_vsb_pga_cfg; /* settings for VSB PGA */ + struct s_cfg_pre_saw m_vsb_pre_saw_cfg; /* settings for pre SAW sense */ + s32 m_Quality83percent; /* MER level (*0.1 dB) for 83% quality indication */ + s32 m_Quality93percent; /* MER level (*0.1 dB) for 93% quality indication */ + bool m_smart_ant_inverted; + bool m_b_debug_enable_bridge; + bool m_b_p_down_open_bridge; /* only open DRXK bridge before power-down once it has been accessed */ + bool m_b_power_down; /* Power down when not used */ + + u32 m_iqm_fs_rate_ofs; /* frequency shift as written to DRXK register (28bit fixpoint) */ + + bool m_enable_mpeg_output; /* If TRUE, enable MPEG output */ + bool m_insert_rs_byte; /* If TRUE, insert RS byte */ + bool m_enable_parallel; /* If TRUE, parallel out otherwise serial */ + bool m_invert_data; /* If TRUE, invert DATA signals */ + bool m_invert_err; /* If TRUE, invert ERR signal */ + bool m_invert_str; /* If TRUE, invert STR signals */ + bool m_invert_val; /* If TRUE, invert VAL signals */ + bool m_invert_clk; /* If TRUE, invert CLK signals */ + bool m_dvbc_static_clk; + bool m_dvbt_static_clk; /* If TRUE, static MPEG clockrate will + be used, otherwise clockrate will + adapt to the bitrate of the TS */ + u32 m_dvbt_bitrate; + u32 m_dvbc_bitrate; + + u8 m_ts_data_strength; + u8 m_ts_clockk_strength; + + bool m_itut_annex_c; /* If true, uses ITU-T DVB-C Annex C, instead of Annex A */ + + enum drxmpeg_str_width_t m_width_str; /* MPEG start width */ + u32 m_mpeg_ts_static_bitrate; /* Maximum bitrate in b/s in case + static clockrate is selected */ + + /* LARGE_INTEGER m_startTime; */ /* Contains the time of the last demod start */ + s32 m_mpeg_lock_time_out; /* WaitForLockStatus Timeout (counts from start time) */ + s32 m_demod_lock_time_out; /* WaitForLockStatus Timeout (counts from start time) */ + + bool m_disable_te_ihandling; + + bool m_rf_agc_pol; + bool m_if_agc_pol; + + struct s_cfg_agc m_atv_rf_agc_cfg; /* settings for ATV RF-AGC */ + struct s_cfg_agc m_atv_if_agc_cfg; /* settings for ATV IF-AGC */ + struct s_cfg_pre_saw m_atv_pre_saw_cfg; /* settings for ATV pre SAW sense */ + bool m_phase_correction_bypass; + s16 m_atv_top_vid_peak; + u16 m_atv_top_noise_th; + enum e_drxk_sif_attenuation m_sif_attenuation; + bool m_enable_cvbs_output; + bool m_enable_sif_output; + bool m_b_mirror_freq_spect; + enum e_drxk_constellation m_constellation; /* constellation type of the channel */ + u32 m_curr_symbol_rate; /* Current QAM symbol rate */ + struct s_cfg_agc m_qam_rf_agc_cfg; /* settings for QAM RF-AGC */ + struct s_cfg_agc m_qam_if_agc_cfg; /* settings for QAM IF-AGC */ + u16 m_qam_pga_cfg; /* settings for QAM PGA */ + struct s_cfg_pre_saw m_qam_pre_saw_cfg; /* settings for QAM pre SAW sense */ + enum e_drxk_interleave_mode m_qam_interleave_mode; /* QAM Interleave mode */ + u16 m_fec_rs_plen; + u16 m_fec_rs_prescale; + + enum drxk_cfg_dvbt_sqi_speed m_sqi_speed; + + u16 m_gpio; + u16 m_gpio_cfg; + + struct s_cfg_agc m_dvbt_rf_agc_cfg; /* settings for QAM RF-AGC */ + struct s_cfg_agc m_dvbt_if_agc_cfg; /* settings for QAM IF-AGC */ + struct s_cfg_pre_saw m_dvbt_pre_saw_cfg; /* settings for QAM pre SAW sense */ + + u16 m_agcfast_clip_ctrl_delay; + bool m_adc_comp_passed; + u16 m_adcCompCoef[64]; + u16 m_adc_state; + + u8 *m_microcode; + int m_microcode_length; + bool m_drxk_a3_rom_code; + bool m_drxk_a3_patch_code; + + bool m_rfmirror; + u8 m_device_spin; + u32 m_iqm_rc_rate; + + enum drx_power_mode m_current_power_mode; + + /* when true, avoids other devices to use the I2C bus */ + bool drxk_i2c_exclusive_lock; + + /* + * Configurable parameters at the driver. They stores the values found + * at struct drxk_config. + */ + + u16 uio_mask; /* Bits used by UIO */ + + bool enable_merr_cfg; + bool single_master; + bool no_i2c_bridge; + bool antenna_dvbt; + u16 antenna_gpio; + + enum fe_status fe_status; + + /* Firmware */ + const char *microcode_name; + struct completion fw_wait_load; + const struct firmware *fw; + int qam_demod_parameter_count; +}; + +#define NEVER_LOCK 0 +#define NOT_LOCKED 1 +#define DEMOD_LOCK 2 +#define FEC_LOCK 3 +#define MPEG_LOCK 4 + diff --git a/drivers/media/dvb-frontends/drxk_map.h b/drivers/media/dvb-frontends/drxk_map.h new file mode 100644 index 0000000000..9234ef4fb6 --- /dev/null +++ b/drivers/media/dvb-frontends/drxk_map.h @@ -0,0 +1,455 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define AUD_COMM_EXEC__A 0x1000000 +#define AUD_COMM_EXEC_STOP 0x0 +#define FEC_COMM_EXEC__A 0x1C00000 +#define FEC_COMM_EXEC_STOP 0x0 +#define FEC_COMM_EXEC_ACTIVE 0x1 +#define FEC_DI_COMM_EXEC__A 0x1C20000 +#define FEC_DI_COMM_EXEC_STOP 0x0 +#define FEC_DI_INPUT_CTL__A 0x1C20016 +#define FEC_RS_COMM_EXEC__A 0x1C30000 +#define FEC_RS_COMM_EXEC_STOP 0x0 +#define FEC_RS_MEASUREMENT_PERIOD__A 0x1C30012 +#define FEC_RS_MEASUREMENT_PRESCALE__A 0x1C30013 +#define FEC_RS_NR_BIT_ERRORS__A 0x1C30014 +#define FEC_OC_MODE__A 0x1C40011 +#define FEC_OC_MODE_PARITY__M 0x1 +#define FEC_OC_DTO_MODE__A 0x1C40014 +#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1 +#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4 +#define FEC_OC_DTO_PERIOD__A 0x1C40015 +#define FEC_OC_DTO_BURST_LEN__A 0x1C40018 +#define FEC_OC_FCT_MODE__A 0x1C4001A +#define FEC_OC_FCT_MODE__PRE 0x0 +#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1 +#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2 +#define FEC_OC_TMD_MODE__A 0x1C4001E +#define FEC_OC_TMD_COUNT__A 0x1C4001F +#define FEC_OC_TMD_HI_MARGIN__A 0x1C40020 +#define FEC_OC_TMD_LO_MARGIN__A 0x1C40021 +#define FEC_OC_TMD_INT_UPD_RATE__A 0x1C40023 +#define FEC_OC_AVR_PARM_A__A 0x1C40026 +#define FEC_OC_AVR_PARM_B__A 0x1C40027 +#define FEC_OC_RCN_GAIN__A 0x1C4002E +#define FEC_OC_RCN_CTL_RATE_LO__A 0x1C40030 +#define FEC_OC_RCN_CTL_STEP_LO__A 0x1C40032 +#define FEC_OC_RCN_CTL_STEP_HI__A 0x1C40033 +#define FEC_OC_SNC_MODE__A 0x1C40040 +#define FEC_OC_SNC_MODE_SHUTDOWN__M 0x10 +#define FEC_OC_SNC_LWM__A 0x1C40041 +#define FEC_OC_SNC_HWM__A 0x1C40042 +#define FEC_OC_SNC_UNLOCK__A 0x1C40043 +#define FEC_OC_SNC_FAIL_PERIOD__A 0x1C40046 +#define FEC_OC_IPR_MODE__A 0x1C40048 +#define FEC_OC_IPR_MODE_SERIAL__M 0x1 +#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4 +#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10 +#define FEC_OC_IPR_INVERT__A 0x1C40049 +#define FEC_OC_IPR_INVERT_MD0__M 0x1 +#define FEC_OC_IPR_INVERT_MD1__M 0x2 +#define FEC_OC_IPR_INVERT_MD2__M 0x4 +#define FEC_OC_IPR_INVERT_MD3__M 0x8 +#define FEC_OC_IPR_INVERT_MD4__M 0x10 +#define FEC_OC_IPR_INVERT_MD5__M 0x20 +#define FEC_OC_IPR_INVERT_MD6__M 0x40 +#define FEC_OC_IPR_INVERT_MD7__M 0x80 +#define FEC_OC_IPR_INVERT_MERR__M 0x100 +#define FEC_OC_IPR_INVERT_MSTRT__M 0x200 +#define FEC_OC_IPR_INVERT_MVAL__M 0x400 +#define FEC_OC_IPR_INVERT_MCLK__M 0x800 +#define FEC_OC_OCR_INVERT__A 0x1C40052 +#define IQM_COMM_EXEC__A 0x1800000 +#define IQM_COMM_EXEC_B_STOP 0x0 +#define IQM_COMM_EXEC_B_ACTIVE 0x1 +#define IQM_FS_RATE_OFS_LO__A 0x1820010 +#define IQM_FS_ADJ_SEL__A 0x1820014 +#define IQM_FS_ADJ_SEL_B_OFF 0x0 +#define IQM_FS_ADJ_SEL_B_QAM 0x1 +#define IQM_FS_ADJ_SEL_B_VSB 0x2 +#define IQM_FD_RATESEL__A 0x1830010 +#define IQM_RC_RATE_OFS_LO__A 0x1840010 +#define IQM_RC_RATE_OFS_LO__W 16 +#define IQM_RC_RATE_OFS_LO__M 0xFFFF +#define IQM_RC_RATE_OFS_HI__M 0xFF +#define IQM_RC_ADJ_SEL__A 0x1840014 +#define IQM_RC_ADJ_SEL_B_OFF 0x0 +#define IQM_RC_ADJ_SEL_B_QAM 0x1 +#define IQM_RC_ADJ_SEL_B_VSB 0x2 +#define IQM_RC_STRETCH__A 0x1840016 +#define IQM_CF_COMM_INT_MSK__A 0x1860006 +#define IQM_CF_SYMMETRIC__A 0x1860010 +#define IQM_CF_MIDTAP__A 0x1860011 +#define IQM_CF_MIDTAP_RE__B 0 +#define IQM_CF_MIDTAP_IM__B 1 +#define IQM_CF_OUT_ENA__A 0x1860012 +#define IQM_CF_OUT_ENA_QAM__B 1 +#define IQM_CF_OUT_ENA_OFDM__M 0x4 +#define IQM_CF_ADJ_SEL__A 0x1860013 +#define IQM_CF_SCALE__A 0x1860014 +#define IQM_CF_SCALE_SH__A 0x1860015 +#define IQM_CF_SCALE_SH__PRE 0x0 +#define IQM_CF_POW_MEAS_LEN__A 0x1860017 +#define IQM_CF_DS_ENA__A 0x1860019 +#define IQM_CF_TAP_RE0__A 0x1860020 +#define IQM_CF_TAP_IM0__A 0x1860040 +#define IQM_CF_CLP_VAL__A 0x1860060 +#define IQM_CF_DATATH__A 0x1860061 +#define IQM_CF_PKDTH__A 0x1860062 +#define IQM_CF_WND_LEN__A 0x1860063 +#define IQM_CF_DET_LCT__A 0x1860064 +#define IQM_CF_BYPASSDET__A 0x1860067 +#define IQM_AF_COMM_EXEC__A 0x1870000 +#define IQM_AF_COMM_EXEC_ACTIVE 0x1 +#define IQM_AF_CLKNEG__A 0x1870012 +#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2 +#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0 +#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2 +#define IQM_AF_START_LOCK__A 0x187001B +#define IQM_AF_PHASE0__A 0x187001C +#define IQM_AF_PHASE1__A 0x187001D +#define IQM_AF_PHASE2__A 0x187001E +#define IQM_AF_CLP_LEN__A 0x1870023 +#define IQM_AF_CLP_TH__A 0x1870024 +#define IQM_AF_SNS_LEN__A 0x1870026 +#define IQM_AF_AGC_IF__A 0x1870028 +#define IQM_AF_AGC_RF__A 0x1870029 +#define IQM_AF_PDREF__A 0x187002B +#define IQM_AF_PDREF__M 0x1F +#define IQM_AF_STDBY__A 0x187002C +#define IQM_AF_STDBY_STDBY_ADC_STANDBY 0x2 +#define IQM_AF_STDBY_STDBY_AMP_STANDBY 0x4 +#define IQM_AF_STDBY_STDBY_PD_STANDBY 0x8 +#define IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY 0x10 +#define IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY 0x20 +#define IQM_AF_AMUX__A 0x187002D +#define IQM_AF_AMUX_SIGNAL2ADC 0x1 +#define IQM_AF_UPD_SEL__A 0x187002F +#define IQM_AF_INC_LCT__A 0x1870034 +#define IQM_AF_INC_BYPASS__A 0x1870036 +#define OFDM_CP_COMM_EXEC__A 0x2800000 +#define OFDM_CP_COMM_EXEC_STOP 0x0 +#define OFDM_EC_SB_PRIOR__A 0x3410013 +#define OFDM_EC_SB_PRIOR_HI 0x0 +#define OFDM_EC_SB_PRIOR_LO 0x1 +#define OFDM_EC_VD_ERR_BIT_CNT__A 0x3420017 +#define OFDM_EC_VD_IN_BIT_CNT__A 0x3420018 +#define OFDM_EQ_TOP_TD_TPS_CONST__A 0x3010054 +#define OFDM_EQ_TOP_TD_TPS_CONST__M 0x3 +#define OFDM_EQ_TOP_TD_TPS_CONST_64QAM 0x2 +#define OFDM_EQ_TOP_TD_TPS_CODE_HP__A 0x3010056 +#define OFDM_EQ_TOP_TD_TPS_CODE_HP__M 0x7 +#define OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8 0x4 +#define OFDM_EQ_TOP_TD_SQR_ERR_I__A 0x301005E +#define OFDM_EQ_TOP_TD_SQR_ERR_Q__A 0x301005F +#define OFDM_EQ_TOP_TD_SQR_ERR_EXP__A 0x3010060 +#define OFDM_EQ_TOP_TD_REQ_SMB_CNT__A 0x3010061 +#define OFDM_EQ_TOP_TD_TPS_PWR_OFS__A 0x3010062 +#define OFDM_LC_COMM_EXEC__A 0x3800000 +#define OFDM_LC_COMM_EXEC_STOP 0x0 +#define OFDM_SC_COMM_EXEC__A 0x3C00000 +#define OFDM_SC_COMM_EXEC_STOP 0x0 +#define OFDM_SC_COMM_STATE__A 0x3C00001 +#define OFDM_SC_RA_RAM_PARAM0__A 0x3C20040 +#define OFDM_SC_RA_RAM_PARAM1__A 0x3C20041 +#define OFDM_SC_RA_RAM_CMD_ADDR__A 0x3C20042 +#define OFDM_SC_RA_RAM_CMD__A 0x3C20043 +#define OFDM_SC_RA_RAM_CMD_NULL 0x0 +#define OFDM_SC_RA_RAM_CMD_PROC_START 0x1 +#define OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 +#define OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM 0x4 +#define OFDM_SC_RA_RAM_CMD_GET_OP_PARAM 0x5 +#define OFDM_SC_RA_RAM_CMD_USER_IO 0x6 +#define OFDM_SC_RA_RAM_CMD_SET_TIMER 0x7 +#define OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING 0x8 +#define OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 +#define OFDM_SC_RA_RAM_LOCKTRACK_MIN 0x1 +#define OFDM_SC_RA_RAM_OP_PARAM__A 0x3C20048 +#define OFDM_SC_RA_RAM_OP_PARAM_MODE__M 0x3 +#define OFDM_SC_RA_RAM_OP_PARAM_MODE_2K 0x0 +#define OFDM_SC_RA_RAM_OP_PARAM_MODE_8K 0x1 +#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_32 0x0 +#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_16 0x4 +#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_8 0x8 +#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_4 0xC +#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 +#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 +#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 +#define OFDM_SC_RA_RAM_OP_PARAM_HIER_NO 0x0 +#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A1 0x40 +#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A2 0x80 +#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 +#define OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 +#define OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 +#define OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 +#define OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 +#define OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 +#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 +#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 +#define OFDM_SC_RA_RAM_OP_AUTO_MODE__M 0x1 +#define OFDM_SC_RA_RAM_OP_AUTO_GUARD__M 0x2 +#define OFDM_SC_RA_RAM_OP_AUTO_CONST__M 0x4 +#define OFDM_SC_RA_RAM_OP_AUTO_HIER__M 0x8 +#define OFDM_SC_RA_RAM_OP_AUTO_RATE__M 0x10 +#define OFDM_SC_RA_RAM_LOCK__A 0x3C2004B +#define OFDM_SC_RA_RAM_LOCK_DEMOD__M 0x1 +#define OFDM_SC_RA_RAM_LOCK_FEC__M 0x2 +#define OFDM_SC_RA_RAM_LOCK_MPEG__M 0x4 +#define OFDM_SC_RA_RAM_LOCK_NODVBT__M 0x8 +#define OFDM_SC_RA_RAM_BE_OPT_DELAY__A 0x3C2004D +#define OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A 0x3C2004E +#define OFDM_SC_RA_RAM_ECHO_THRES__A 0x3C2004F +#define OFDM_SC_RA_RAM_ECHO_THRES_8K__B 0 +#define OFDM_SC_RA_RAM_ECHO_THRES_8K__M 0xFF +#define OFDM_SC_RA_RAM_ECHO_THRES_2K__B 8 +#define OFDM_SC_RA_RAM_ECHO_THRES_2K__M 0xFF00 +#define OFDM_SC_RA_RAM_CONFIG__A 0x3C20050 +#define OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M 0x800 +#define OFDM_SC_RA_RAM_FR_THRES_8K__A 0x3C2007D +#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A 0x3C200E0 +#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A 0x3C200E1 +#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A 0x3C200E3 +#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A 0x3C200E4 +#define OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A 0x3C200F8 +#define QAM_COMM_EXEC__A 0x1400000 +#define QAM_COMM_EXEC_STOP 0x0 +#define QAM_COMM_EXEC_ACTIVE 0x1 +#define QAM_TOP_ANNEX_A 0x0 +#define QAM_TOP_ANNEX_C 0x2 +#define QAM_SL_ERR_POWER__A 0x1430017 +#define QAM_DQ_QUAL_FUN0__A 0x1440018 +#define QAM_DQ_QUAL_FUN1__A 0x1440019 +#define QAM_DQ_QUAL_FUN2__A 0x144001A +#define QAM_DQ_QUAL_FUN3__A 0x144001B +#define QAM_DQ_QUAL_FUN4__A 0x144001C +#define QAM_DQ_QUAL_FUN5__A 0x144001D +#define QAM_LC_MODE__A 0x1450010 +#define QAM_LC_QUAL_TAB0__A 0x1450018 +#define QAM_LC_QUAL_TAB1__A 0x1450019 +#define QAM_LC_QUAL_TAB2__A 0x145001A +#define QAM_LC_QUAL_TAB3__A 0x145001B +#define QAM_LC_QUAL_TAB4__A 0x145001C +#define QAM_LC_QUAL_TAB5__A 0x145001D +#define QAM_LC_QUAL_TAB6__A 0x145001E +#define QAM_LC_QUAL_TAB8__A 0x145001F +#define QAM_LC_QUAL_TAB9__A 0x1450020 +#define QAM_LC_QUAL_TAB10__A 0x1450021 +#define QAM_LC_QUAL_TAB12__A 0x1450022 +#define QAM_LC_QUAL_TAB15__A 0x1450023 +#define QAM_LC_QUAL_TAB16__A 0x1450024 +#define QAM_LC_QUAL_TAB20__A 0x1450025 +#define QAM_LC_QUAL_TAB25__A 0x1450026 +#define QAM_LC_LPF_FACTORP__A 0x1450028 +#define QAM_LC_LPF_FACTORI__A 0x1450029 +#define QAM_LC_RATE_LIMIT__A 0x145002A +#define QAM_LC_SYMBOL_FREQ__A 0x145002B +#define QAM_SY_TIMEOUT__A 0x1470011 +#define QAM_SY_TIMEOUT__PRE 0x3A98 +#define QAM_SY_SYNC_LWM__A 0x1470012 +#define QAM_SY_SYNC_AWM__A 0x1470013 +#define QAM_SY_SYNC_HWM__A 0x1470014 +#define QAM_SY_SP_INV__A 0x1470017 +#define QAM_SY_SP_INV_SPECTRUM_INV_DIS 0x0 +#define SCU_COMM_EXEC__A 0x800000 +#define SCU_COMM_EXEC_STOP 0x0 +#define SCU_COMM_EXEC_ACTIVE 0x1 +#define SCU_COMM_EXEC_HOLD 0x2 +#define SCU_RAM_DRIVER_DEBUG__A 0x831EBF +#define SCU_RAM_QAM_FSM_STEP_PERIOD__A 0x831EC4 +#define SCU_RAM_GPIO__A 0x831EC7 +#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0 +#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8 +#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB +#define SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A 0x831F05 +#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831F15 +#define SCU_RAM_AGC_KI_CYCLEN__A 0x831F17 +#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831F18 +#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831F19 +#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831F1A +#define SCU_RAM_AGC_RF_MAX__A 0x831F1B +#define SCU_RAM_AGC_CONFIG__A 0x831F24 +#define SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M 0x1 +#define SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M 0x2 +#define SCU_RAM_AGC_CONFIG_INV_IF_POL__M 0x100 +#define SCU_RAM_AGC_CONFIG_INV_RF_POL__M 0x200 +#define SCU_RAM_AGC_KI__A 0x831F25 +#define SCU_RAM_AGC_KI_RF__B 4 +#define SCU_RAM_AGC_KI_RF__M 0xF0 +#define SCU_RAM_AGC_KI_IF__B 8 +#define SCU_RAM_AGC_KI_IF__M 0xF00 +#define SCU_RAM_AGC_KI_RED__A 0x831F26 +#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2 +#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC +#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4 +#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30 +#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831F27 +#define SCU_RAM_AGC_KI_MINGAIN__A 0x831F28 +#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831F29 +#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831F2A +#define SCU_RAM_AGC_KI_MIN__A 0x831F2B +#define SCU_RAM_AGC_KI_MAX__A 0x831F2C +#define SCU_RAM_AGC_CLP_SUM__A 0x831F2D +#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831F2E +#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831F2F +#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831F30 +#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831F31 +#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831F32 +#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831F33 +#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831F34 +#define SCU_RAM_AGC_SNS_SUM__A 0x831F35 +#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831F36 +#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831F37 +#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831F38 +#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831F39 +#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831F3A +#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831F3B +#define SCU_RAM_AGC_INGAIN_TGT__A 0x831F3D +#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831F3E +#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831F3F +#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831F40 +#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831F41 +#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831F42 +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831F43 +#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831F44 +#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831F45 +#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831F46 +#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831F47 +#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84 +#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89 +#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A +#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E +#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F +#define SCU_RAM_QAM_FSM_PTH__A 0x831F90 +#define SCU_RAM_QAM_FSM_MTH__A 0x831F91 +#define SCU_RAM_QAM_FSM_CTH__A 0x831F92 +#define SCU_RAM_QAM_FSM_QTH__A 0x831F93 +#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94 +#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95 +#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96 +#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97 +#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99 +#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A +#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B +#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C +#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D +#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E +#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F +#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0 +#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1 +#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2 +#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3 +#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4 +#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5 +#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6 +#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7 +#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8 +#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9 +#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA +#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB +#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC +#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD +#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE +#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF +#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0 +#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1 +#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2 +#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000 +#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000 +#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000 +#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831FEA +#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB +#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC +#define SCU_RAM_PARAM_15__A 0x831FED +#define SCU_RAM_PARAM_0__A 0x831FFC +#define SCU_RAM_COMMAND__A 0x831FFD +#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1 +#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2 +#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3 +#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4 +#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5 +#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9 +#define SCU_RAM_COMMAND_STANDARD_QAM 0x200 +#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400 +#define SIO_TOP_COMM_KEY__A 0x41000F +#define SIO_TOP_COMM_KEY_KEY 0xFABA +#define SIO_TOP_JTAGID_LO__A 0x410012 +#define SIO_HI_RA_RAM_RES__A 0x420031 +#define SIO_HI_RA_RAM_CMD__A 0x420032 +#define SIO_HI_RA_RAM_CMD_RESET 0x2 +#define SIO_HI_RA_RAM_CMD_CONFIG 0x3 +#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7 +#define SIO_HI_RA_RAM_PAR_1__A 0x420033 +#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945 +#define SIO_HI_RA_RAM_PAR_2__A 0x420034 +#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0 +#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4 +#define SIO_HI_RA_RAM_PAR_3__A 0x420035 +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F +#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0 +#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8 +#define SIO_HI_RA_RAM_PAR_4__A 0x420036 +#define SIO_HI_RA_RAM_PAR_5__A 0x420037 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8 +#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8 +#define SIO_HI_RA_RAM_PAR_6__A 0x420038 +#define SIO_CC_PLL_LOCK__A 0x450012 +#define SIO_CC_PWD_MODE__A 0x450015 +#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0 +#define SIO_CC_PWD_MODE_LEVEL_OFDM 0x1 +#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x2 +#define SIO_CC_PWD_MODE_LEVEL_PLL 0x3 +#define SIO_CC_PWD_MODE_LEVEL_OSC 0x4 +#define SIO_CC_SOFT_RST__A 0x450016 +#define SIO_CC_SOFT_RST_OFDM__M 0x1 +#define SIO_CC_SOFT_RST_SYS__M 0x2 +#define SIO_CC_SOFT_RST_OSC__M 0x4 +#define SIO_CC_UPDATE__A 0x450017 +#define SIO_CC_UPDATE_KEY 0xFABA +#define SIO_OFDM_SH_OFDM_RING_ENABLE__A 0x470010 +#define SIO_OFDM_SH_OFDM_RING_ENABLE_OFF 0x0 +#define SIO_OFDM_SH_OFDM_RING_ENABLE_ON 0x1 +#define SIO_OFDM_SH_OFDM_RING_STATUS__A 0x470012 +#define SIO_OFDM_SH_OFDM_RING_STATUS_DOWN 0x0 +#define SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED 0x1 +#define SIO_BL_COMM_EXEC__A 0x480000 +#define SIO_BL_COMM_EXEC_ACTIVE 0x1 +#define SIO_BL_STATUS__A 0x480010 +#define SIO_BL_MODE__A 0x480011 +#define SIO_BL_MODE_DIRECT 0x0 +#define SIO_BL_MODE_CHAIN 0x1 +#define SIO_BL_ENABLE__A 0x480012 +#define SIO_BL_ENABLE_ON 0x1 +#define SIO_BL_TGT_HDR__A 0x480014 +#define SIO_BL_TGT_ADDR__A 0x480015 +#define SIO_BL_SRC_ADDR__A 0x480016 +#define SIO_BL_SRC_LEN__A 0x480017 +#define SIO_BL_CHAIN_ADDR__A 0x480018 +#define SIO_BL_CHAIN_LEN__A 0x480019 +#define SIO_PDR_MON_CFG__A 0x7F0010 +#define SIO_PDR_UIO_IN_HI__A 0x7F0015 +#define SIO_PDR_UIO_OUT_LO__A 0x7F0016 +#define SIO_PDR_OHW_CFG__A 0x7F001F +#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3 +#define SIO_PDR_GPIO_CFG__A 0x7F0021 +#define SIO_PDR_MSTRT_CFG__A 0x7F0025 +#define SIO_PDR_MERR_CFG__A 0x7F0026 +#define SIO_PDR_MCLK_CFG__A 0x7F0028 +#define SIO_PDR_MCLK_CFG_DRIVE__B 3 +#define SIO_PDR_MVAL_CFG__A 0x7F0029 +#define SIO_PDR_MD0_CFG__A 0x7F002A +#define SIO_PDR_MD0_CFG_DRIVE__B 3 +#define SIO_PDR_MD1_CFG__A 0x7F002B +#define SIO_PDR_MD2_CFG__A 0x7F002C +#define SIO_PDR_MD3_CFG__A 0x7F002D +#define SIO_PDR_MD4_CFG__A 0x7F002F +#define SIO_PDR_MD5_CFG__A 0x7F0030 +#define SIO_PDR_MD6_CFG__A 0x7F0031 +#define SIO_PDR_MD7_CFG__A 0x7F0032 +#define SIO_PDR_SMA_RX_CFG__A 0x7F0037 +#define SIO_PDR_SMA_TX_CFG__A 0x7F0038 diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c new file mode 100644 index 0000000000..515aa7c7ba --- /dev/null +++ b/drivers/media/dvb-frontends/ds3000.c @@ -0,0 +1,1130 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009-2012 TurboSight.com + + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/firmware.h> + +#include <media/dvb_frontend.h> +#include "ts2020.h" +#include "ds3000.h" + +static int debug; + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(args); \ + } while (0) + +/* as of March 2009 current DS3000 firmware version is 1.78 */ +/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */ +#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" + +#define DS3000_SAMPLE_RATE 96000 /* in kHz */ + +/* Register values to initialise the demod in DVB-S mode */ +static u8 ds3000_dvbs_init_tab[] = { + 0x23, 0x05, + 0x08, 0x03, + 0x0c, 0x00, + 0x21, 0x54, + 0x25, 0x82, + 0x27, 0x31, + 0x30, 0x08, + 0x31, 0x40, + 0x32, 0x32, + 0x33, 0x35, + 0x35, 0xff, + 0x3a, 0x00, + 0x37, 0x10, + 0x38, 0x10, + 0x39, 0x02, + 0x42, 0x60, + 0x4a, 0x40, + 0x4b, 0x04, + 0x4d, 0x91, + 0x5d, 0xc8, + 0x50, 0x77, + 0x51, 0x77, + 0x52, 0x36, + 0x53, 0x36, + 0x56, 0x01, + 0x63, 0x43, + 0x64, 0x30, + 0x65, 0x40, + 0x68, 0x26, + 0x69, 0x4c, + 0x70, 0x20, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x40, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x60, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x80, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0xa0, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x1f, + 0x76, 0x00, + 0x77, 0xd1, + 0x78, 0x0c, + 0x79, 0x80, + 0x7f, 0x04, + 0x7c, 0x00, + 0x80, 0x86, + 0x81, 0xa6, + 0x85, 0x04, + 0xcd, 0xf4, + 0x90, 0x33, + 0xa0, 0x44, + 0xc0, 0x18, + 0xc3, 0x10, + 0xc4, 0x08, + 0xc5, 0x80, + 0xc6, 0x80, + 0xc7, 0x0a, + 0xc8, 0x1a, + 0xc9, 0x80, + 0xfe, 0x92, + 0xe0, 0xf8, + 0xe6, 0x8b, + 0xd0, 0x40, + 0xf8, 0x20, + 0xfa, 0x0f, + 0xfd, 0x20, + 0xad, 0x20, + 0xae, 0x07, + 0xb8, 0x00, +}; + +/* Register values to initialise the demod in DVB-S2 mode */ +static u8 ds3000_dvbs2_init_tab[] = { + 0x23, 0x0f, + 0x08, 0x07, + 0x0c, 0x00, + 0x21, 0x54, + 0x25, 0x82, + 0x27, 0x31, + 0x30, 0x08, + 0x31, 0x32, + 0x32, 0x32, + 0x33, 0x35, + 0x35, 0xff, + 0x3a, 0x00, + 0x37, 0x10, + 0x38, 0x10, + 0x39, 0x02, + 0x42, 0x60, + 0x4a, 0x80, + 0x4b, 0x04, + 0x4d, 0x81, + 0x5d, 0x88, + 0x50, 0x36, + 0x51, 0x36, + 0x52, 0x36, + 0x53, 0x36, + 0x63, 0x60, + 0x64, 0x10, + 0x65, 0x10, + 0x68, 0x04, + 0x69, 0x29, + 0x70, 0x20, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x40, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x60, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x80, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0xa0, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x1f, + 0xa0, 0x44, + 0xc0, 0x08, + 0xc1, 0x10, + 0xc2, 0x08, + 0xc3, 0x10, + 0xc4, 0x08, + 0xc5, 0xf0, + 0xc6, 0xf0, + 0xc7, 0x0a, + 0xc8, 0x1a, + 0xc9, 0x80, + 0xca, 0x23, + 0xcb, 0x24, + 0xce, 0x74, + 0x90, 0x03, + 0x76, 0x80, + 0x77, 0x42, + 0x78, 0x0a, + 0x79, 0x80, + 0xad, 0x40, + 0xae, 0x07, + 0x7f, 0xd4, + 0x7c, 0x00, + 0x80, 0xa8, + 0x81, 0xda, + 0x7c, 0x01, + 0x80, 0xda, + 0x81, 0xec, + 0x7c, 0x02, + 0x80, 0xca, + 0x81, 0xeb, + 0x7c, 0x03, + 0x80, 0xba, + 0x81, 0xdb, + 0x85, 0x08, + 0x86, 0x00, + 0x87, 0x02, + 0x89, 0x80, + 0x8b, 0x44, + 0x8c, 0xaa, + 0x8a, 0x10, + 0xba, 0x00, + 0xf5, 0x04, + 0xfe, 0x44, + 0xd2, 0x32, + 0xb8, 0x00, +}; + +struct ds3000_state { + struct i2c_adapter *i2c; + const struct ds3000_config *config; + struct dvb_frontend frontend; + /* previous uncorrected block counter for DVB-S2 */ + u16 prevUCBS2; +}; + +static int ds3000_writereg(struct ds3000_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", + __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct ds3000_state *state = fe->demodulator_priv; + + if (enable) + ds3000_writereg(state, 0x03, 0x12); + else + ds3000_writereg(state, 0x03, 0x02); + + return 0; +} + +/* I2C write for 8k firmware load */ +static int ds3000_writeFW(struct ds3000_state *state, int reg, + const u8 *data, u16 len) +{ + int i, ret = 0; + struct i2c_msg msg; + u8 *buf; + + buf = kmalloc(33, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + *(buf) = reg; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = 33; + + for (i = 0; i < len; i += 32) { + memcpy(buf + 1, data + i, 32); + + dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s: write error(err == %i, reg == 0x%02x\n", + __func__, ret, reg); + ret = -EREMOTEIO; + goto error; + } + } + ret = 0; + +error: + kfree(buf); + + return ret; +} + +static int ds3000_readreg(struct ds3000_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); + + return b1[0]; +} + +static int ds3000_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw); + +static int ds3000_firmware_ondemand(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret = 0; + + dprintk("%s()\n", __func__); + + ret = ds3000_readreg(state, 0xb2); + if (ret < 0) + return ret; + + /* Load firmware */ + /* request the firmware, this will block until someone uploads it */ + printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, + DS3000_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n", + __func__); + return ret; + } + + ret = ds3000_load_firmware(fe, fw); + if (ret) + printk("%s: Writing firmware to device failed\n", __func__); + + release_firmware(fw); + + dprintk("%s: Firmware upload %s\n", __func__, + ret == 0 ? "complete" : "failed"); + + return ret; +} + +static int ds3000_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw) +{ + struct ds3000_state *state = fe->demodulator_priv; + int ret = 0; + + dprintk("%s\n", __func__); + dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", + fw->size, + fw->data[0], + fw->data[1], + fw->data[fw->size - 2], + fw->data[fw->size - 1]); + + /* Begin the firmware load process */ + ds3000_writereg(state, 0xb2, 0x01); + /* write the entire firmware */ + ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size); + ds3000_writereg(state, 0xb2, 0x00); + + return ret; +} + +static int ds3000_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct ds3000_state *state = fe->demodulator_priv; + u8 data; + + dprintk("%s(%d)\n", __func__, voltage); + + data = ds3000_readreg(state, 0xa2); + data |= 0x03; /* bit0 V/H, bit1 off/on */ + + switch (voltage) { + case SEC_VOLTAGE_18: + data &= ~0x03; + break; + case SEC_VOLTAGE_13: + data &= ~0x03; + data |= 0x01; + break; + case SEC_VOLTAGE_OFF: + break; + } + + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +static int ds3000_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int lock; + + *status = 0; + + switch (c->delivery_system) { + case SYS_DVBS: + lock = ds3000_readreg(state, 0xd1); + if ((lock & 0x07) == 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + break; + case SYS_DVBS2: + lock = ds3000_readreg(state, 0x0d); + if ((lock & 0x8f) == 0x8f) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + break; + default: + return -EINVAL; + } + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, *status == 0 ? 0 : 1); + + dprintk("%s: status = 0x%02x\n", __func__, lock); + + return 0; +} + +/* read DS3000 BER value */ +static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 data; + u32 ber_reading, lpdc_frames; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + /* set the number of bytes checked during + BER estimation */ + ds3000_writereg(state, 0xf9, 0x04); + /* read BER estimation status */ + data = ds3000_readreg(state, 0xf8); + /* check if BER estimation is ready */ + if ((data & 0x10) == 0) { + /* this is the number of error bits, + to calculate the bit error rate + divide to 8388608 */ + *ber = (ds3000_readreg(state, 0xf7) << 8) | + ds3000_readreg(state, 0xf6); + /* start counting error bits */ + /* need to be set twice + otherwise it fails sometimes */ + data |= 0x10; + ds3000_writereg(state, 0xf8, data); + ds3000_writereg(state, 0xf8, data); + } else + /* used to indicate that BER estimation + is not ready, i.e. BER is unknown */ + *ber = 0xffffffff; + break; + case SYS_DVBS2: + /* read the number of LPDC decoded frames */ + lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) | + (ds3000_readreg(state, 0xd6) << 8) | + ds3000_readreg(state, 0xd5); + /* read the number of packets with bad CRC */ + ber_reading = (ds3000_readreg(state, 0xf8) << 8) | + ds3000_readreg(state, 0xf7); + if (lpdc_frames > 750) { + /* clear LPDC frame counters */ + ds3000_writereg(state, 0xd1, 0x01); + /* clear bad packets counter */ + ds3000_writereg(state, 0xf9, 0x01); + /* enable bad packets counter */ + ds3000_writereg(state, 0xf9, 0x00); + /* enable LPDC frame counters */ + ds3000_writereg(state, 0xd1, 0x00); + *ber = ber_reading; + } else + /* used to indicate that BER estimation is not ready, + i.e. BER is unknown */ + *ber = 0xffffffff; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ds3000_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, signal_strength); + + return 0; +} + +/* calculate DS3000 snr value in dB */ +static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 snr_reading, snr_value; + u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp; + static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */ + 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03, + 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717, + 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505 + }; + static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */ + 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103, + 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5, + 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6, + 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888, + 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51, + 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68, + 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206, + 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a, + 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649, + 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813, + 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1, + 0x49e9, 0x4a20, 0x4a57 + }; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + snr_reading = ds3000_readreg(state, 0xff); + snr_reading /= 8; + if (snr_reading == 0) + *snr = 0x0000; + else { + if (snr_reading > 20) + snr_reading = 20; + snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = snr_value * 8 * 655; + } + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + break; + case SYS_DVBS2: + dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) + + (ds3000_readreg(state, 0x8d) << 4); + dvbs2_signal_reading = ds3000_readreg(state, 0x8e); + tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1; + if (tmp == 0) { + *snr = 0x0000; + return 0; + } + if (dvbs2_noise_reading == 0) { + snr_value = 0x0013; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = 0xffff; + return 0; + } + if (tmp > dvbs2_noise_reading) { + snr_reading = tmp / dvbs2_noise_reading; + if (snr_reading > 80) + snr_reading = 80; + snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = snr_value * 5 * 655; + } else { + snr_reading = dvbs2_noise_reading / tmp; + if (snr_reading > 80) + snr_reading = 80; + *snr = -(dvbs2_snr_tab[snr_reading - 1] / 1000); + } + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* read DS3000 uncorrected blocks */ +static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 data; + u16 _ucblocks; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + *ucblocks = (ds3000_readreg(state, 0xf5) << 8) | + ds3000_readreg(state, 0xf4); + data = ds3000_readreg(state, 0xf8); + /* clear packet counters */ + data &= ~0x20; + ds3000_writereg(state, 0xf8, data); + /* enable packet counters */ + data |= 0x20; + ds3000_writereg(state, 0xf8, data); + break; + case SYS_DVBS2: + _ucblocks = (ds3000_readreg(state, 0xe2) << 8) | + ds3000_readreg(state, 0xe1); + if (_ucblocks > state->prevUCBS2) + *ucblocks = _ucblocks - state->prevUCBS2; + else + *ucblocks = state->prevUCBS2 - _ucblocks; + state->prevUCBS2 = _ucblocks; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ds3000_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct ds3000_state *state = fe->demodulator_priv; + u8 data; + + dprintk("%s(%d)\n", __func__, tone); + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); + return -EINVAL; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + switch (tone) { + case SEC_TONE_ON: + dprintk("%s: setting tone on\n", __func__); + data = ds3000_readreg(state, 0xa1); + data &= ~0x43; + data |= 0x04; + ds3000_writereg(state, 0xa1, data); + break; + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n", __func__); + data = ds3000_readreg(state, 0xa2); + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + break; + } + + return 0; +} + +static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct ds3000_state *state = fe->demodulator_priv; + int i; + u8 data; + + /* Dump DiSEqC message */ + dprintk("%s(", __func__); + for (i = 0 ; i < d->msg_len;) { + dprintk("0x%02x", d->msg[i]); + if (++i < d->msg_len) + dprintk(", "); + } + + /* enable DiSEqC message send pin */ + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + ds3000_writereg(state, 0xa3 + i, d->msg[i]); + + data = ds3000_readreg(state, 0xa1); + /* clear DiSEqC message length and status, + enable DiSEqC message send */ + data &= ~0xf8; + /* set DiSEqC mode, modulation active during 33 pulses, + set DiSEqC message length */ + data |= ((d->msg_len - 1) << 3) | 0x07; + ds3000_writereg(state, 0xa1, data); + + /* wait up to 150ms for DiSEqC transmission to complete */ + for (i = 0; i < 15; i++) { + data = ds3000_readreg(state, 0xa1); + if ((data & 0x40) == 0) + break; + msleep(10); + } + + /* DiSEqC timeout after 150ms */ + if (i == 15) { + data = ds3000_readreg(state, 0xa1); + data &= ~0x80; + data |= 0x40; + ds3000_writereg(state, 0xa1, data); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return -ETIMEDOUT; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +/* Send DiSEqC burst */ +static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct ds3000_state *state = fe->demodulator_priv; + int i; + u8 data; + + dprintk("%s()\n", __func__); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + /* DiSEqC burst */ + if (burst == SEC_MINI_A) + /* Unmodulated tone burst */ + ds3000_writereg(state, 0xa1, 0x02); + else if (burst == SEC_MINI_B) + /* Modulated tone burst */ + ds3000_writereg(state, 0xa1, 0x01); + else + return -EINVAL; + + msleep(13); + for (i = 0; i < 5; i++) { + data = ds3000_readreg(state, 0xa1); + if ((data & 0x40) == 0) + break; + msleep(1); + } + + if (i == 5) { + data = ds3000_readreg(state, 0xa1); + data &= ~0x80; + data |= 0x40; + ds3000_writereg(state, 0xa1, data); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return -ETIMEDOUT; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +static void ds3000_release(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + + dprintk("%s\n", __func__); + kfree(state); +} + +static const struct dvb_frontend_ops ds3000_ops; + +struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c) +{ + struct ds3000_state *state; + int ret; + + dprintk("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->i2c = i2c; + state->prevUCBS2 = 0; + + /* check if the demod is present */ + ret = ds3000_readreg(state, 0x00) & 0xfe; + if (ret != 0xe0) { + kfree(state); + printk(KERN_ERR "Invalid probe, probably not a DS3000\n"); + return NULL; + } + + printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n", + ds3000_readreg(state, 0x02), + ds3000_readreg(state, 0x01)); + + memcpy(&state->frontend.ops, &ds3000_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* + * Some devices like T480 starts with voltage on. Be sure + * to turn voltage off during init, as this can otherwise + * interfere with Unicable SCR systems. + */ + ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF); + return &state->frontend; +} +EXPORT_SYMBOL_GPL(ds3000_attach); + +static int ds3000_set_carrier_offset(struct dvb_frontend *fe, + s32 carrier_offset_khz) +{ + struct ds3000_state *state = fe->demodulator_priv; + s32 tmp; + + tmp = carrier_offset_khz; + tmp *= 65536; + tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE); + + if (tmp < 0) + tmp += 65536; + + ds3000_writereg(state, 0x5f, tmp >> 8); + ds3000_writereg(state, 0x5e, tmp & 0xff); + + return 0; +} + +static int ds3000_set_frontend(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + int i; + enum fe_status status; + s32 offset_khz; + u32 frequency; + u16 value; + + dprintk("%s() ", __func__); + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + /* Tune */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* ds3000 global reset */ + ds3000_writereg(state, 0x07, 0x80); + ds3000_writereg(state, 0x07, 0x00); + /* ds3000 built-in uC reset */ + ds3000_writereg(state, 0xb2, 0x01); + /* ds3000 software reset */ + ds3000_writereg(state, 0x00, 0x01); + + switch (c->delivery_system) { + case SYS_DVBS: + /* initialise the demod in DVB-S mode */ + for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs_init_tab[i], + ds3000_dvbs_init_tab[i + 1]); + value = ds3000_readreg(state, 0xfe); + value &= 0xc0; + value |= 0x1b; + ds3000_writereg(state, 0xfe, value); + break; + case SYS_DVBS2: + /* initialise the demod in DVB-S2 mode */ + for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs2_init_tab[i], + ds3000_dvbs2_init_tab[i + 1]); + if (c->symbol_rate >= 30000000) + ds3000_writereg(state, 0xfe, 0x54); + else + ds3000_writereg(state, 0xfe, 0x98); + break; + default: + return -EINVAL; + } + + /* enable 27MHz clock output */ + ds3000_writereg(state, 0x29, 0x80); + /* enable ac coupling */ + ds3000_writereg(state, 0x25, 0x8a); + + if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) || + (c->symbol_rate > ds3000_ops.info.symbol_rate_max)) { + dprintk("%s() symbol_rate %u out of range (%u ... %u)\n", + __func__, c->symbol_rate, + ds3000_ops.info.symbol_rate_min, + ds3000_ops.info.symbol_rate_max); + return -EINVAL; + } + + /* enhance symbol rate performance */ + if ((c->symbol_rate / 1000) <= 5000) { + value = 29777 / (c->symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x0d); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x10); + ds3000_writereg(state, 0xc7, 0x0e); + } else if ((c->symbol_rate / 1000) <= 10000) { + value = 92166 / (c->symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x07); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x09); + ds3000_writereg(state, 0xc7, 0x12); + } else if ((c->symbol_rate / 1000) <= 20000) { + value = 64516 / (c->symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0e); + ds3000_writereg(state, 0xc4, 0x07); + ds3000_writereg(state, 0xc7, 0x18); + } else { + value = 129032 / (c->symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0a); + ds3000_writereg(state, 0xc4, 0x05); + ds3000_writereg(state, 0xc7, 0x24); + } + + /* normalized symbol rate rounded to the closest integer */ + value = (((c->symbol_rate / 1000) << 16) + + (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; + ds3000_writereg(state, 0x61, value & 0x00ff); + ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); + + /* co-channel interference cancellation disabled */ + ds3000_writereg(state, 0x56, 0x00); + + /* equalizer disabled */ + ds3000_writereg(state, 0x76, 0x00); + + /*ds3000_writereg(state, 0x08, 0x03); + ds3000_writereg(state, 0xfd, 0x22); + ds3000_writereg(state, 0x08, 0x07); + ds3000_writereg(state, 0xfd, 0x42); + ds3000_writereg(state, 0x08, 0x07);*/ + + if (state->config->ci_mode) { + switch (c->delivery_system) { + case SYS_DVBS: + default: + ds3000_writereg(state, 0xfd, 0x80); + break; + case SYS_DVBS2: + ds3000_writereg(state, 0xfd, 0x01); + break; + } + } + + /* ds3000 out of software reset */ + ds3000_writereg(state, 0x00, 0x00); + /* start ds3000 built-in uC */ + ds3000_writereg(state, 0xb2, 0x00); + + if (fe->ops.tuner_ops.get_frequency) { + fe->ops.tuner_ops.get_frequency(fe, &frequency); + offset_khz = frequency - c->frequency; + ds3000_set_carrier_offset(fe, offset_khz); + } + + for (i = 0; i < 30 ; i++) { + ds3000_read_status(fe, &status); + if (status & FE_HAS_LOCK) + break; + + msleep(10); + } + + return 0; +} + +static int ds3000_tune(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + if (re_tune) { + int ret = ds3000_set_frontend(fe); + if (ret) + return ret; + } + + *delay = HZ / 5; + + return ds3000_read_status(fe, status); +} + +static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + + dprintk("%s()\n", __func__); + return DVBFE_ALGO_HW; +} + +/* + * Initialise or wake up device + * + * Power config will reset and load initial firmware if required + */ +static int ds3000_initfe(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + int ret; + + dprintk("%s()\n", __func__); + /* hard reset */ + ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); + msleep(1); + + /* Load the firmware if required */ + ret = ds3000_firmware_ondemand(fe); + if (ret != 0) { + printk(KERN_ERR "%s: Unable initialize firmware\n", __func__); + return ret; + } + + return 0; +} + +static const struct dvb_frontend_ops ds3000_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Montage Technology DS3000", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = ds3000_release, + + .init = ds3000_initfe, + .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, + .read_status = ds3000_read_status, + .read_ber = ds3000_read_ber, + .read_signal_strength = ds3000_read_signal_strength, + .read_snr = ds3000_read_snr, + .read_ucblocks = ds3000_read_ucblocks, + .set_voltage = ds3000_set_voltage, + .set_tone = ds3000_set_tone, + .diseqc_send_master_cmd = ds3000_send_diseqc_msg, + .diseqc_send_burst = ds3000_diseqc_send_burst, + .get_frontend_algo = ds3000_get_algo, + + .set_frontend = ds3000_set_frontend, + .tune = ds3000_tune, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_DESCRIPTION("DVB Frontend module for Montage Technology DS3000 hardware"); +MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE); diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h new file mode 100644 index 0000000000..cd24fa4d25 --- /dev/null +++ b/drivers/media/dvb-frontends/ds3000.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009-2012 TurboSight.com + + */ + +#ifndef DS3000_H +#define DS3000_H + +#include <linux/dvb/frontend.h> + +struct ds3000_config { + /* the demodulator's i2c address */ + u8 demod_address; + u8 ci_mode; + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + /* Hook for Lock LED */ + void (*set_lock_led)(struct dvb_frontend *fe, int offon); +}; + +#if IS_REACHABLE(CONFIG_DVB_DS3000) +extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_DS3000 */ +#endif /* DS3000_H */ diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c new file mode 100644 index 0000000000..ef697ab6bc --- /dev/null +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * descriptions + helper functions for simple dvb plls. + * + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/idr.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "dvb-pll.h" + +#define dprintk(fmt, arg...) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg) + +struct dvb_pll_priv { + /* pll number */ + int nr; + + /* i2c details */ + int pll_i2c_address; + struct i2c_adapter *i2c; + + /* the PLL descriptor */ + const struct dvb_pll_desc *pll_desc; + + /* cached frequency/bandwidth */ + u32 frequency; + u32 bandwidth; +}; + +#define DVB_PLL_MAX 64 +static DEFINE_IDA(pll_ida); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +static unsigned int id[DVB_PLL_MAX] = + { [ 0 ... (DVB_PLL_MAX-1) ] = DVB_PLL_UNDEFINED }; +module_param_array(id, int, NULL, 0644); +MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)"); + +/* ----------------------------------------------------------- */ + +struct dvb_pll_desc { + const char *name; + u32 min; + u32 max; + u32 iffreq; + void (*set)(struct dvb_frontend *fe, u8 *buf); + u8 *initdata; + u8 *initdata2; + u8 *sleepdata; + int count; + struct { + u32 limit; + u32 stepsize; + u8 config; + u8 cb; + } entries[]; +}; + +/* ----------------------------------------------------------- */ +/* descriptions */ + +static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { + .name = "Thomson dtt7579", + .min = 177 * MHz, + .max = 858 * MHz, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0xb4, 0x03 }, + .count = 4, + .entries = { + { 443250000, 166667, 0xb4, 0x02 }, + { 542000000, 166667, 0xb4, 0x08 }, + { 771000000, 166667, 0xbc, 0x08 }, + { 999999999, 166667, 0xf4, 0x08 }, + }, +}; + +static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf) +{ + u32 bw = fe->dtv_property_cache.bandwidth_hz; + if (bw == 7000000) + buf[3] |= 0x10; +} + +static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = { + .name = "Thomson dtt759x", + .min = 177 * MHz, + .max = 896 * MHz, + .set = thomson_dtt759x_bw, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0x84, 0x03 }, + .count = 5, + .entries = { + { 264000000, 166667, 0xb4, 0x02 }, + { 470000000, 166667, 0xbc, 0x02 }, + { 735000000, 166667, 0xbc, 0x08 }, + { 835000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x08 }, + }, +}; + +static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf) +{ + u32 bw = fe->dtv_property_cache.bandwidth_hz; + if (bw == 8000000) + buf[3] ^= 0x10; +} + +static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { + .name = "Thomson dtt7520x", + .min = 185 * MHz, + .max = 900 * MHz, + .set = thomson_dtt7520x_bw, + .iffreq = 36166667, + .count = 7, + .entries = { + { 305000000, 166667, 0xb4, 0x12 }, + { 405000000, 166667, 0xbc, 0x12 }, + { 445000000, 166667, 0xbc, 0x12 }, + { 465000000, 166667, 0xf4, 0x18 }, + { 735000000, 166667, 0xfc, 0x18 }, + { 835000000, 166667, 0xbc, 0x18 }, + { 999999999, 166667, 0xfc, 0x18 }, + }, +}; + +static const struct dvb_pll_desc dvb_pll_lg_z201 = { + .name = "LG z201", + .min = 174 * MHz, + .max = 862 * MHz, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0xbc, 0x03 }, + .count = 5, + .entries = { + { 157500000, 166667, 0xbc, 0x01 }, + { 443250000, 166667, 0xbc, 0x02 }, + { 542000000, 166667, 0xbc, 0x04 }, + { 830000000, 166667, 0xf4, 0x04 }, + { 999999999, 166667, 0xfc, 0x04 }, + }, +}; + +static const struct dvb_pll_desc dvb_pll_unknown_1 = { + .name = "unknown 1", /* used by dntv live dvb-t */ + .min = 174 * MHz, + .max = 862 * MHz, + .iffreq= 36166667, + .count = 9, + .entries = { + { 150000000, 166667, 0xb4, 0x01 }, + { 173000000, 166667, 0xbc, 0x01 }, + { 250000000, 166667, 0xb4, 0x02 }, + { 400000000, 166667, 0xbc, 0x02 }, + { 420000000, 166667, 0xf4, 0x02 }, + { 470000000, 166667, 0xfc, 0x02 }, + { 600000000, 166667, 0xbc, 0x08 }, + { 730000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x08 }, + }, +}; + +/* Infineon TUA6010XS + * used in Thomson Cable Tuner + */ +static const struct dvb_pll_desc dvb_pll_tua6010xs = { + .name = "Infineon TUA6010XS", + .min = 44250 * kHz, + .max = 858 * MHz, + .iffreq= 36125000, + .count = 3, + .entries = { + { 115750000, 62500, 0x8e, 0x03 }, + { 403250000, 62500, 0x8e, 0x06 }, + { 999999999, 62500, 0x8e, 0x85 }, + }, +}; + +/* Panasonic env57h1xd5 (some Philips PLL ?) */ +static const struct dvb_pll_desc dvb_pll_env57h1xd5 = { + .name = "Panasonic ENV57H1XD5", + .min = 44250 * kHz, + .max = 858 * MHz, + .iffreq= 36125000, + .count = 4, + .entries = { + { 153000000, 166667, 0xc2, 0x41 }, + { 470000000, 166667, 0xc2, 0x42 }, + { 526000000, 166667, 0xc2, 0x84 }, + { 999999999, 166667, 0xc2, 0xa4 }, + }, +}; + +/* Philips TDA6650/TDA6651 + * used in Panasonic ENV77H11D5 + */ +static void tda665x_bw(struct dvb_frontend *fe, u8 *buf) +{ + u32 bw = fe->dtv_property_cache.bandwidth_hz; + if (bw == 8000000) + buf[3] |= 0x08; +} + +static const struct dvb_pll_desc dvb_pll_tda665x = { + .name = "Philips TDA6650/TDA6651", + .min = 44250 * kHz, + .max = 858 * MHz, + .set = tda665x_bw, + .iffreq= 36166667, + .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab }, + .count = 12, + .entries = { + { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, + { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, + { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, + { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, + { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, + { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, + { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, + { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, + } +}; + +/* Infineon TUA6034 + * used in LG TDTP E102P + */ +static void tua6034_bw(struct dvb_frontend *fe, u8 *buf) +{ + u32 bw = fe->dtv_property_cache.bandwidth_hz; + if (bw == 7000000) + buf[3] |= 0x08; +} + +static const struct dvb_pll_desc dvb_pll_tua6034 = { + .name = "Infineon TUA6034", + .min = 44250 * kHz, + .max = 858 * MHz, + .iffreq= 36166667, + .count = 3, + .set = tua6034_bw, + .entries = { + { 174500000, 62500, 0xce, 0x01 }, + { 230000000, 62500, 0xce, 0x02 }, + { 999999999, 62500, 0xce, 0x04 }, + }, +}; + +/* ALPS TDED4 + * used in Nebula-Cards and USB boxes + */ +static void tded4_bw(struct dvb_frontend *fe, u8 *buf) +{ + u32 bw = fe->dtv_property_cache.bandwidth_hz; + if (bw == 8000000) + buf[3] |= 0x04; +} + +static const struct dvb_pll_desc dvb_pll_tded4 = { + .name = "ALPS TDED4", + .min = 47 * MHz, + .max = 863 * MHz, + .iffreq= 36166667, + .set = tded4_bw, + .count = 4, + .entries = { + { 153000000, 166667, 0x85, 0x01 }, + { 470000000, 166667, 0x85, 0x02 }, + { 823000000, 166667, 0x85, 0x08 }, + { 999999999, 166667, 0x85, 0x88 }, + } +}; + +/* ALPS TDHU2 + * used in AverTVHD MCE A180 + */ +static const struct dvb_pll_desc dvb_pll_tdhu2 = { + .name = "ALPS TDHU2", + .min = 54 * MHz, + .max = 864 * MHz, + .iffreq= 44000000, + .count = 4, + .entries = { + { 162000000, 62500, 0x85, 0x01 }, + { 426000000, 62500, 0x85, 0x02 }, + { 782000000, 62500, 0x85, 0x08 }, + { 999999999, 62500, 0x85, 0x88 }, + } +}; + +/* Samsung TBMV30111IN / TBMV30712IN1 + * used in Air2PC ATSC - 2nd generation (nxt2002) + */ +static const struct dvb_pll_desc dvb_pll_samsung_tbmv = { + .name = "Samsung TBMV30111IN / TBMV30712IN1", + .min = 54 * MHz, + .max = 860 * MHz, + .iffreq= 44000000, + .count = 6, + .entries = { + { 172000000, 166667, 0xb4, 0x01 }, + { 214000000, 166667, 0xb4, 0x02 }, + { 467000000, 166667, 0xbc, 0x02 }, + { 721000000, 166667, 0xbc, 0x08 }, + { 841000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x02 }, + } +}; + +/* + * Philips SD1878 Tuner. + */ +static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { + .name = "Philips SD1878", + .min = 950 * MHz, + .max = 2150 * MHz, + .iffreq= 249, /* zero-IF, offset 249 is to round up */ + .count = 4, + .entries = { + { 1250000, 500, 0xc4, 0x00}, + { 1450000, 500, 0xc4, 0x40}, + { 2050000, 500, 0xc4, 0x80}, + { 2150000, 500, 0xc4, 0xc0}, + }, +}; + +static void opera1_bw(struct dvb_frontend *fe, u8 *buf) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_pll_priv *priv = fe->tuner_priv; + u32 b_w = (c->symbol_rate * 27) / 32000; + struct i2c_msg msg = { + .addr = priv->pll_i2c_address, + .flags = 0, + .buf = buf, + .len = 4 + }; + int result; + u8 lpf; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + pr_err("%s: i2c_transfer failed:%d", + __func__, result); + + if (b_w <= 10000) + lpf = 0xc; + else if (b_w <= 12000) + lpf = 0x2; + else if (b_w <= 14000) + lpf = 0xa; + else if (b_w <= 16000) + lpf = 0x6; + else if (b_w <= 18000) + lpf = 0xe; + else if (b_w <= 20000) + lpf = 0x1; + else if (b_w <= 22000) + lpf = 0x9; + else if (b_w <= 24000) + lpf = 0x5; + else if (b_w <= 26000) + lpf = 0xd; + else if (b_w <= 28000) + lpf = 0x3; + else + lpf = 0xb; + buf[2] ^= 0x1c; /* Flip bits 3-5 */ + /* Set lpf */ + buf[2] |= ((lpf >> 2) & 0x3) << 3; + buf[3] |= (lpf & 0x3) << 2; + + return; +} + +static const struct dvb_pll_desc dvb_pll_opera1 = { + .name = "Opera Tuner", + .min = 900 * MHz, + .max = 2250 * MHz, + .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 }, + .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 }, + .iffreq= 0, + .set = opera1_bw, + .count = 8, + .entries = { + { 1064000, 500, 0xf9, 0xc2 }, + { 1169000, 500, 0xf9, 0xe2 }, + { 1299000, 500, 0xf9, 0x20 }, + { 1444000, 500, 0xf9, 0x40 }, + { 1606000, 500, 0xf9, 0x60 }, + { 1777000, 500, 0xf9, 0x80 }, + { 1941000, 500, 0xf9, 0xa0 }, + { 2250000, 500, 0xf9, 0xc0 }, + } +}; + +static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { + .addr = priv->pll_i2c_address, + .flags = 0, + .buf = buf, + .len = 4 + }; + int result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + pr_err("%s: i2c_transfer failed:%d", + __func__, result); + + buf[2] = 0x9e; + buf[3] = 0x90; + + return; +} + +/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ +static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { + .name = "Samsung DTOS403IH102A", + .min = 44250 * kHz, + .max = 858 * MHz, + .iffreq = 36125000, + .count = 8, + .set = samsung_dtos403ih102a_set, + .entries = { + { 135000000, 62500, 0xbe, 0x01 }, + { 177000000, 62500, 0xf6, 0x01 }, + { 370000000, 62500, 0xbe, 0x02 }, + { 450000000, 62500, 0xf6, 0x02 }, + { 466000000, 62500, 0xfe, 0x02 }, + { 538000000, 62500, 0xbe, 0x08 }, + { 826000000, 62500, 0xf6, 0x08 }, + { 999999999, 62500, 0xfe, 0x08 }, + } +}; + +/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ +static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { + .name = "Samsung TDTC9251DH0", + .min = 48 * MHz, + .max = 863 * MHz, + .iffreq = 36166667, + .count = 3, + .entries = { + { 157500000, 166667, 0xcc, 0x09 }, + { 443000000, 166667, 0xcc, 0x0a }, + { 863000000, 166667, 0xcc, 0x08 }, + } +}; + +/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ +static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { + .name = "Samsung TBDU18132", + .min = 950 * MHz, + .max = 2150 * MHz, /* guesses */ + .iffreq = 0, + .count = 2, + .entries = { + { 1550000, 125, 0x84, 0x82 }, + { 4095937, 125, 0x84, 0x80 }, + } + /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported + * by this driver. The two extra bits are 0x60 in the third byte. 15 + * bits is enough for over 4 GHz, which is enough to cover the range + * of this tuner. We could use the additional divisor bits by adding + * more entries, e.g. + { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, }, + { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, }, + { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */ +}; + +/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ +static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { + .name = "Samsung TBMU24112", + .min = 950 * MHz, + .max = 2150 * MHz, /* guesses */ + .iffreq = 0, + .count = 2, + .entries = { + { 1500000, 125, 0x84, 0x18 }, + { 9999999, 125, 0x84, 0x08 }, + } +}; + +/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */ +/* byte 4 : 1 * * AGD R3 R2 R1 R0 + * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 + * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 + * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 + * 47 - 153 0 * 0 0 0 0 0 1 0x01 + * 153 - 430 0 * 0 0 0 0 1 0 0x02 + * 430 - 822 0 * 0 0 1 0 0 0 0x08 + * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ +static const struct dvb_pll_desc dvb_pll_alps_tdee4 = { + .name = "ALPS TDEE4", + .min = 47 * MHz, + .max = 862 * MHz, + .iffreq = 36125000, + .count = 4, + .entries = { + { 153000000, 62500, 0x95, 0x01 }, + { 430000000, 62500, 0x95, 0x02 }, + { 822000000, 62500, 0x95, 0x08 }, + { 999999999, 62500, 0x95, 0x88 }, + } +}; + +/* Infineon TUA6034 ISDB-T, used in Friio */ +/* CP cur. 50uA, AGC takeover: 103dBuV, PORT3 on */ +static const struct dvb_pll_desc dvb_pll_tua6034_friio = { + .name = "Infineon TUA6034 ISDB-T (Friio)", + .min = 90 * MHz, + .max = 770 * MHz, + .iffreq = 57000000, + .initdata = (u8[]){ 4, 0x9a, 0x50, 0xb2, 0x08 }, + .sleepdata = (u8[]){ 4, 0x9a, 0x70, 0xb3, 0x0b }, + .count = 3, + .entries = { + { 170000000, 142857, 0xba, 0x09 }, + { 470000000, 142857, 0xba, 0x0a }, + { 770000000, 142857, 0xb2, 0x08 }, + } +}; + +/* Philips TDA6651 ISDB-T, used in Earthsoft PT1 */ +static const struct dvb_pll_desc dvb_pll_tda665x_earth_pt1 = { + .name = "Philips TDA6651 ISDB-T (EarthSoft PT1)", + .min = 90 * MHz, + .max = 770 * MHz, + .iffreq = 57000000, + .initdata = (u8[]){ 5, 0x0e, 0x7f, 0xc1, 0x80, 0x80 }, + .count = 10, + .entries = { + { 140000000, 142857, 0xc1, 0x81 }, + { 170000000, 142857, 0xc1, 0xa1 }, + { 220000000, 142857, 0xc1, 0x62 }, + { 330000000, 142857, 0xc1, 0xa2 }, + { 402000000, 142857, 0xc1, 0xe2 }, + { 450000000, 142857, 0xc1, 0x64 }, + { 550000000, 142857, 0xc1, 0x84 }, + { 600000000, 142857, 0xc1, 0xa4 }, + { 700000000, 142857, 0xc1, 0xc4 }, + { 770000000, 142857, 0xc1, 0xe4 }, + } +}; + +/* ----------------------------------------------------------- */ + +static const struct dvb_pll_desc *pll_list[] = { + [DVB_PLL_UNDEFINED] = NULL, + [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579, + [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x, + [DVB_PLL_THOMSON_DTT7520X] = &dvb_pll_thomson_dtt7520x, + [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201, + [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1, + [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs, + [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5, + [DVB_PLL_TUA6034] = &dvb_pll_tua6034, + [DVB_PLL_TDA665X] = &dvb_pll_tda665x, + [DVB_PLL_TDED4] = &dvb_pll_tded4, + [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4, + [DVB_PLL_TDHU2] = &dvb_pll_tdhu2, + [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, + [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, + [DVB_PLL_OPERA1] = &dvb_pll_opera1, + [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, + [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, + [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, + [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112, + [DVB_PLL_TUA6034_FRIIO] = &dvb_pll_tua6034_friio, + [DVB_PLL_TDA665X_EARTH_PT1] = &dvb_pll_tda665x_earth_pt1, +}; + +/* ----------------------------------------------------------- */ +/* code */ + +static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf, + const u32 frequency) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + const struct dvb_pll_desc *desc = priv->pll_desc; + u32 div; + int i; + + for (i = 0; i < desc->count; i++) { + if (frequency > desc->entries[i].limit) + continue; + break; + } + + if (debug) + dprintk("pll: %s: freq=%d | i=%d/%d\n", desc->name, + frequency, i, desc->count); + if (i == desc->count) + return -EINVAL; + + div = (frequency + desc->iffreq + + desc->entries[i].stepsize/2) / desc->entries[i].stepsize; + buf[0] = div >> 8; + buf[1] = div & 0xff; + buf[2] = desc->entries[i].config; + buf[3] = desc->entries[i].cb; + + if (desc->set) + desc->set(fe, buf); + + if (debug) + dprintk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", + desc->name, div, buf[0], buf[1], buf[2], buf[3]); + + // calculate the frequency we set it to + return (div * desc->entries[i].stepsize) - desc->iffreq; +} + +static void dvb_pll_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int dvb_pll_sleep(struct dvb_frontend *fe) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + + if (priv->i2c == NULL) + return -EINVAL; + + if (priv->pll_desc->sleepdata) { + struct i2c_msg msg = { .flags = 0, + .addr = priv->pll_i2c_address, + .buf = priv->pll_desc->sleepdata + 1, + .len = priv->pll_desc->sleepdata[0] }; + + int result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + return 0; + } + /* Shouldn't be called when initdata is NULL, maybe BUG()? */ + return -EINVAL; +} + +static int dvb_pll_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_pll_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = + { .addr = priv->pll_i2c_address, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int result; + u32 frequency = 0; + + if (priv->i2c == NULL) + return -EINVAL; + + result = dvb_pll_configure(fe, buf, c->frequency); + if (result < 0) + return result; + else + frequency = result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + + priv->frequency = frequency; + priv->bandwidth = c->bandwidth_hz; + + return 0; +} + +static int dvb_pll_calc_regs(struct dvb_frontend *fe, + u8 *buf, int buf_len) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_pll_priv *priv = fe->tuner_priv; + int result; + u32 frequency = 0; + + if (buf_len < 5) + return -EINVAL; + + result = dvb_pll_configure(fe, buf + 1, c->frequency); + if (result < 0) + return result; + else + frequency = result; + + buf[0] = priv->pll_i2c_address; + + priv->frequency = frequency; + priv->bandwidth = c->bandwidth_hz; + + return 5; +} + +static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int dvb_pll_init(struct dvb_frontend *fe) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + + if (priv->i2c == NULL) + return -EINVAL; + + if (priv->pll_desc->initdata) { + struct i2c_msg msg = { .flags = 0, + .addr = priv->pll_i2c_address, + .buf = priv->pll_desc->initdata + 1, + .len = priv->pll_desc->initdata[0] }; + + int result; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + return result; + if (priv->pll_desc->initdata2) { + msg.buf = priv->pll_desc->initdata2 + 1; + msg.len = priv->pll_desc->initdata2[0]; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + return result; + } + return 0; + } + /* Shouldn't be called when initdata is NULL, maybe BUG()? */ + return -EINVAL; +} + +static const struct dvb_tuner_ops dvb_pll_tuner_ops = { + .release = dvb_pll_release, + .sleep = dvb_pll_sleep, + .init = dvb_pll_init, + .set_params = dvb_pll_set_params, + .calc_regs = dvb_pll_calc_regs, + .get_frequency = dvb_pll_get_frequency, + .get_bandwidth = dvb_pll_get_bandwidth, +}; + +struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, + struct i2c_adapter *i2c, + unsigned int pll_desc_id) +{ + u8 *b1; + struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .len = 1 }; + struct dvb_pll_priv *priv = NULL; + int ret; + const struct dvb_pll_desc *desc; + int nr; + + b1 = kmalloc(1, GFP_KERNEL); + if (!b1) + return NULL; + + b1[0] = 0; + msg.buf = b1; + + nr = ida_simple_get(&pll_ida, 0, DVB_PLL_MAX, GFP_KERNEL); + if (nr < 0) { + kfree(b1); + return NULL; + } + + if (id[nr] > DVB_PLL_UNDEFINED && id[nr] < ARRAY_SIZE(pll_list)) + pll_desc_id = id[nr]; + + BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list)); + + desc = pll_list[pll_desc_id]; + + if (i2c != NULL) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer (i2c, &msg, 1); + if (ret != 1) + goto out; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL); + if (!priv) + goto out; + + priv->pll_i2c_address = pll_addr; + priv->i2c = i2c; + priv->pll_desc = desc; + priv->nr = nr; + + memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + strscpy(fe->ops.tuner_ops.info.name, desc->name, + sizeof(fe->ops.tuner_ops.info.name)); + + fe->ops.tuner_ops.info.frequency_min_hz = desc->min; + fe->ops.tuner_ops.info.frequency_max_hz = desc->max; + + dprintk("%s tuner, frequency range: %u...%u\n", + desc->name, desc->min, desc->max); + + if (!desc->initdata) + fe->ops.tuner_ops.init = NULL; + if (!desc->sleepdata) + fe->ops.tuner_ops.sleep = NULL; + + fe->tuner_priv = priv; + + if ((debug) || (id[priv->nr] == pll_desc_id)) { + dprintk("dvb-pll[%d]", priv->nr); + if (i2c != NULL) + pr_cont(" %d-%04x", i2c_adapter_id(i2c), pll_addr); + pr_cont(": id# %d (%s) attached, %s\n", pll_desc_id, desc->name, + id[priv->nr] == pll_desc_id ? + "insmod option" : "autodetected"); + } + + kfree(b1); + + return fe; +out: + kfree(b1); + ida_simple_remove(&pll_ida, nr); + + return NULL; +} +EXPORT_SYMBOL_GPL(dvb_pll_attach); + + +static int +dvb_pll_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct dvb_pll_config *cfg; + struct dvb_frontend *fe; + unsigned int desc_id; + + cfg = client->dev.platform_data; + fe = cfg->fe; + i2c_set_clientdata(client, fe); + desc_id = (unsigned int) id->driver_data; + + if (!dvb_pll_attach(fe, client->addr, client->adapter, desc_id)) + return -ENOMEM; + + /* + * Unset tuner_ops.release (== dvb_pll_release) + * which has been just set in the above dvb_pll_attach(), + * because if tuner_ops.release was left defined, + * this module would be 'put' twice on exit: + * once by dvb_frontend_detach() and another by dvb_module_release(). + * + * dvb_pll_release is instead executed in the i2c driver's .remove(), + * keeping dvb_pll_attach untouched for legacy (dvb_attach) drivers. + */ + fe->ops.tuner_ops.release = NULL; + dev_info(&client->dev, "DVB Simple Tuner attached.\n"); + return 0; +} + +static void dvb_pll_remove(struct i2c_client *client) +{ + struct dvb_frontend *fe = i2c_get_clientdata(client); + struct dvb_pll_priv *priv = fe->tuner_priv; + + ida_simple_remove(&pll_ida, priv->nr); + dvb_pll_release(fe); +} + + +static const struct i2c_device_id dvb_pll_id[] = { + {"dtt7579", DVB_PLL_THOMSON_DTT7579}, + {"dtt759x", DVB_PLL_THOMSON_DTT759X}, + {"z201", DVB_PLL_LG_Z201}, + {"unknown_1", DVB_PLL_UNKNOWN_1}, + {"tua6010xs", DVB_PLL_TUA6010XS}, + {"env57h1xd5", DVB_PLL_ENV57H1XD5}, + {"tua6034", DVB_PLL_TUA6034}, + {"tda665x", DVB_PLL_TDA665X}, + {"tded4", DVB_PLL_TDED4}, + {"tdhu2", DVB_PLL_TDHU2}, + {"tbmv", DVB_PLL_SAMSUNG_TBMV}, + {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261}, + {"opera1", DVB_PLL_OPERA1}, + {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A}, + {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0}, + {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132}, + {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112}, + {"tdee4", DVB_PLL_TDEE4}, + {"dtt7520x", DVB_PLL_THOMSON_DTT7520X}, + {"tua6034_friio", DVB_PLL_TUA6034_FRIIO}, + {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1}, + {} +}; + + +MODULE_DEVICE_TABLE(i2c, dvb_pll_id); + +static struct i2c_driver dvb_pll_driver = { + .driver = { + .name = "dvb_pll", + }, + .probe = dvb_pll_probe, + .remove = dvb_pll_remove, + .id_table = dvb_pll_id, +}; + +module_i2c_driver(dvb_pll_driver); + +MODULE_DESCRIPTION("dvb pll library"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h new file mode 100644 index 0000000000..7183888874 --- /dev/null +++ b/drivers/media/dvb-frontends/dvb-pll.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * descriptions + helper functions for simple dvb plls. + */ + +#ifndef __DVB_PLL_H__ +#define __DVB_PLL_H__ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +#define DVB_PLL_UNDEFINED 0 +#define DVB_PLL_THOMSON_DTT7579 1 +#define DVB_PLL_THOMSON_DTT759X 2 +#define DVB_PLL_LG_Z201 3 +#define DVB_PLL_UNKNOWN_1 4 +#define DVB_PLL_TUA6010XS 5 +#define DVB_PLL_ENV57H1XD5 6 +#define DVB_PLL_TUA6034 7 +#define DVB_PLL_TDA665X 8 +#define DVB_PLL_TDED4 9 +#define DVB_PLL_TDHU2 10 +#define DVB_PLL_SAMSUNG_TBMV 11 +#define DVB_PLL_PHILIPS_SD1878_TDA8261 12 +#define DVB_PLL_OPERA1 13 +#define DVB_PLL_SAMSUNG_DTOS403IH102A 14 +#define DVB_PLL_SAMSUNG_TDTC9251DH0 15 +#define DVB_PLL_SAMSUNG_TBDU18132 16 +#define DVB_PLL_SAMSUNG_TBMU24112 17 +#define DVB_PLL_TDEE4 18 +#define DVB_PLL_THOMSON_DTT7520X 19 +#define DVB_PLL_TUA6034_FRIIO 20 +#define DVB_PLL_TDA665X_EARTH_PT1 21 + +struct dvb_pll_config { + struct dvb_frontend *fe; +}; + +#if IS_REACHABLE(CONFIG_DVB_PLL) +/** + * dvb_pll_attach - Attach a dvb-pll to the supplied frontend structure. + * + * @fe: Frontend to attach to. + * @pll_addr: i2c address of the PLL (if used). + * @i2c: i2c adapter to use (set to NULL if not used). + * @pll_desc_id: dvb_pll_desc to use. + * + * return: Frontend pointer on success, NULL on failure + */ +extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, + int pll_addr, + struct i2c_adapter *i2c, + unsigned int pll_desc_id); +#else +static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, + int pll_addr, + struct i2c_adapter *i2c, + unsigned int pll_desc_id) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c new file mode 100644 index 0000000000..9ff1ebaa5e --- /dev/null +++ b/drivers/media/dvb-frontends/dvb_dummy_fe.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Dummy Frontend + * + * Written by Emard <emard@softhome.net> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "dvb_dummy_fe.h" + + +struct dvb_dummy_fe_state { + struct dvb_frontend frontend; +}; + + +static int dvb_dummy_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC + | FE_HAS_LOCK; + + return 0; +} + +static int dvb_dummy_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + *strength = 0; + return 0; +} + +static int dvb_dummy_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +/* + * Should only be implemented if it actually reads something from the hardware. + * Also, it should check for the locks, in order to avoid report wrong data + * to userspace. + */ +static int dvb_dummy_fe_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + return 0; +} + +static int dvb_dummy_fe_set_frontend(struct dvb_frontend *fe) +{ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int dvb_dummy_fe_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int dvb_dummy_fe_init(struct dvb_frontend *fe) +{ + return 0; +} + +static int dvb_dummy_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + return 0; +} + +static int dvb_dummy_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + return 0; +} + +static void dvb_dummy_fe_release(struct dvb_frontend *fe) +{ + struct dvb_dummy_fe_state *state = fe->demodulator_priv; + + kfree(state); +} + +static const struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops; + +struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void) +{ + struct dvb_dummy_fe_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (!state) + return NULL; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, + &dvb_dummy_fe_ofdm_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + return &state->frontend; +} +EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach); + +static const struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops; + +struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) +{ + struct dvb_dummy_fe_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (!state) + return NULL; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, + &dvb_dummy_fe_qpsk_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + return &state->frontend; +} +EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach); + +static const struct dvb_frontend_ops dvb_dummy_fe_qam_ops; + +struct dvb_frontend *dvb_dummy_fe_qam_attach(void) +{ + struct dvb_dummy_fe_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (!state) + return NULL; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, + &dvb_dummy_fe_qam_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + return &state->frontend; +} +EXPORT_SYMBOL(dvb_dummy_fe_qam_attach); + +static const struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Dummy DVB-T", + .frequency_min_hz = 0, + .frequency_max_hz = 863250 * kHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, +}; + +static const struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "Dummy DVB-C", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + /* symbol_rate_min: SACLK/64 == (XIN/2)/64 */ + .symbol_rate_min = (57840000 / 2) / 64, + .symbol_rate_max = (57840000 / 2) / 4, /* SACLK/4 */ + .caps = FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO | + FE_CAN_INVERSION_AUTO + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, +}; + +static const struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Dummy DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 250 * kHz, + .frequency_tolerance_hz = 29500 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, + + .set_voltage = dvb_dummy_fe_set_voltage, + .set_tone = dvb_dummy_fe_set_tone, +}; + +MODULE_DESCRIPTION("DVB DUMMY Frontend"); +MODULE_AUTHOR("Emard"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.h b/drivers/media/dvb-frontends/dvb_dummy_fe.h new file mode 100644 index 0000000000..463abf5ebd --- /dev/null +++ b/drivers/media/dvb-frontends/dvb_dummy_fe.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Dummy Frontend + * + * Written by Emard <emard@softhome.net> + */ + +#ifndef DVB_DUMMY_FE_H +#define DVB_DUMMY_FE_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +#if IS_REACHABLE(CONFIG_DVB_DUMMY_FE) +struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void); +struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void); +struct dvb_frontend *dvb_dummy_fe_qam_attach(void); +#else +static inline struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct dvb_frontend *dvb_dummy_fe_qam_attach(void) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_DUMMY_FE */ + +#endif // DVB_DUMMY_FE_H diff --git a/drivers/media/dvb-frontends/ec100.c b/drivers/media/dvb-frontends/ec100.c new file mode 100644 index 0000000000..2ad0a3c2f7 --- /dev/null +++ b/drivers/media/dvb-frontends/ec100.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + */ + +#include <media/dvb_frontend.h> +#include "ec100.h" + +struct ec100_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct ec100_config config; + + u16 ber; +}; + +/* write single register */ +static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val) +{ + int ret; + u8 buf[2] = {reg, val}; + struct i2c_msg msg[1] = { + { + .addr = state->config.demod_address, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(state->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n", + KBUILD_MODNAME, ret, reg); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read single register */ +static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = state->config.demod_address, + .flags = 0, + .len = 1, + .buf = ® + }, { + .addr = state->config.demod_address, + .flags = I2C_M_RD, + .len = 1, + .buf = val + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%02x\n", + KBUILD_MODNAME, ret, reg); + ret = -EREMOTEIO; + } + + return ret; +} + +static int ec100_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp, tmp2; + + dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", + __func__, c->frequency, c->bandwidth_hz); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + ret = ec100_write_reg(state, 0x04, 0x06); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x67, 0x58); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x05, 0x18); + if (ret) + goto error; + + /* reg/bw | 6 | 7 | 8 + -------+------+------+------ + A 0x1b | 0xa1 | 0xe7 | 0x2c + A 0x1c | 0x55 | 0x63 | 0x72 + -------+------+------+------ + B 0x1b | 0xb7 | 0x00 | 0x49 + B 0x1c | 0x55 | 0x64 | 0x72 */ + + switch (c->bandwidth_hz) { + case 6000000: + tmp = 0xb7; + tmp2 = 0x55; + break; + case 7000000: + tmp = 0x00; + tmp2 = 0x64; + break; + case 8000000: + default: + tmp = 0x49; + tmp2 = 0x72; + } + + ret = ec100_write_reg(state, 0x1b, tmp); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x1c, tmp2); + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */ + if (ret) + goto error; + ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */ + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x08, 0x24); + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x00, 0x00); /* go */ + if (ret) + goto error; + ret = ec100_write_reg(state, 0x00, 0x20); /* go */ + if (ret) + goto error; + + return ret; +error: + dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int ec100_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 300; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int ec100_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + *status = 0; + + ret = ec100_read_reg(state, 0x42, &tmp); + if (ret) + goto error; + + if (tmp & 0x80) { + /* bit7 set - have lock */ + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + } else { + ret = ec100_read_reg(state, 0x01, &tmp); + if (ret) + goto error; + + if (tmp & 0x10) { + /* bit4 set - have signal */ + *status |= FE_HAS_SIGNAL; + if (!(tmp & 0x01)) { + /* bit0 clear - have ~valid signal */ + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI; + } + } + } + + return ret; +error: + dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp, tmp2; + u16 ber2; + + *ber = 0; + + ret = ec100_read_reg(state, 0x65, &tmp); + if (ret) + goto error; + ret = ec100_read_reg(state, 0x66, &tmp2); + if (ret) + goto error; + + ber2 = (tmp2 << 8) | tmp; + + /* if counter overflow or clear */ + if (ber2 < state->ber) + *ber = ber2; + else + *ber = ber2 - state->ber; + + state->ber = ber2; + + return ret; +error: + dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + + ret = ec100_read_reg(state, 0x24, &tmp); + if (ret) { + *strength = 0; + goto error; + } + + *strength = ((tmp << 8) | tmp); + + return ret; +error: + dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static void ec100_release(struct dvb_frontend *fe) +{ + struct ec100_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops ec100_ops; + +struct dvb_frontend *ec100_attach(const struct ec100_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct ec100_state *state = NULL; + u8 tmp; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct ec100_config)); + + /* check if the demod is there */ + ret = ec100_read_reg(state, 0x33, &tmp); + if (ret || tmp != 0x0b) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ec100_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(ec100_attach); + +static const struct dvb_frontend_ops ec100_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "E3C EC100 DVB-T", + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS + }, + + .release = ec100_release, + .set_frontend = ec100_set_frontend, + .get_tune_settings = ec100_get_tune_settings, + .read_status = ec100_read_status, + .read_ber = ec100_read_ber, + .read_signal_strength = ec100_read_signal_strength, + .read_snr = ec100_read_snr, + .read_ucblocks = ec100_read_ucblocks, +}; + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ec100.h b/drivers/media/dvb-frontends/ec100.h new file mode 100644 index 0000000000..398dba65f6 --- /dev/null +++ b/drivers/media/dvb-frontends/ec100.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + */ + +#ifndef EC100_H +#define EC100_H + +#include <linux/dvb/frontend.h> + +struct ec100_config { + /* demodulator's I2C address */ + u8 demod_address; +}; + + +#if IS_REACHABLE(CONFIG_DVB_EC100) +extern struct dvb_frontend *ec100_attach(const struct ec100_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ec100_attach( + const struct ec100_config *config, struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* EC100_H */ diff --git a/drivers/media/dvb-frontends/eds1547.h b/drivers/media/dvb-frontends/eds1547.h new file mode 100644 index 0000000000..bb85a6e270 --- /dev/null +++ b/drivers/media/dvb-frontends/eds1547.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* eds1547.h Earda EDS-1547 tuner support +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information +*/ + +#ifndef EDS1547 +#define EDS1547 + +static u8 stv0288_earda_inittab[] = { + 0x01, 0x57, + 0x02, 0x20, + 0x03, 0x8e, + 0x04, 0x8e, + 0x05, 0x12, + 0x06, 0x00, + 0x07, 0x00, + 0x09, 0x00, + 0x0a, 0x04, + 0x0b, 0x00, + 0x0c, 0x00, + 0x0d, 0x00, + 0x0e, 0xd4, + 0x0f, 0x30, + 0x11, 0x44, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0x45, + 0x16, 0xb7, + 0x17, 0x9c, + 0x18, 0x00, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0x00, + 0x23, 0x00, + 0x2b, 0xff, + 0x2c, 0xf7, + 0x30, 0x00, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbd, + 0x3a, 0x00, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x20, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x50, 0x10, + 0x51, 0x36, + 0x52, 0x09, + 0x53, 0x94, + 0x54, 0x62, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x00, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0x00, + 0x5f, 0xff, + 0x70, 0x00, + 0x71, 0x00, + 0x72, 0x00, + 0x74, 0x00, + 0x75, 0x00, + 0x76, 0x00, + 0x81, 0x00, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x00, + 0x85, 0x00, + 0x88, 0x00, + 0x89, 0x00, + 0x8a, 0x00, + 0x8b, 0x00, + 0x8c, 0x00, + 0x90, 0x00, + 0x91, 0x00, + 0x92, 0x00, + 0x93, 0x00, + 0x94, 0x1c, + 0x97, 0x00, + 0xa0, 0x48, + 0xa1, 0x00, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x00, + 0xf0, 0x00, + 0xf1, 0x00, + 0xf2, 0xc0, + 0xff,0xff, +}; + +static struct stv0288_config earda_config = { + .demod_address = 0x68, + .min_delay_ms = 100, + .inittab = stv0288_earda_inittab, +}; + +#endif diff --git a/drivers/media/dvb-frontends/gp8psk-fe.c b/drivers/media/dvb-frontends/gp8psk-fe.c new file mode 100644 index 0000000000..ed671e951a --- /dev/null +++ b/drivers/media/dvb-frontends/gp8psk-fe.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Frontend driver for the GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module + * + * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) + * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) + * + * Thanks to GENPIX for the sample code used to implement this module. + * + * This module is based off the vp7045 and vp702x modules + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "gp8psk-fe.h" +#include <media/dvb_frontend.h> + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +struct gp8psk_fe_state { + struct dvb_frontend fe; + void *priv; + const struct gp8psk_fe_ops *ops; + bool is_rev1; + u8 lock; + u16 snr; + unsigned long next_status_check; + unsigned long status_check_interval; +}; + +static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 status; + + st->ops->in(st->priv, GET_8PSK_CONFIG, 0, 0, &status, 1); + return status & bmDCtuned; +} + +static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + return st->ops->out(st->priv, SET_8PSK_CONFIG, mode, 0, NULL, 0); +} + +static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) +{ + u8 buf[6]; + if (time_after(jiffies,st->next_status_check)) { + st->ops->in(st->priv, GET_SIGNAL_LOCK, 0, 0, &st->lock, 1); + st->ops->in(st->priv, GET_SIGNAL_STRENGTH, 0, 0, buf, 6); + st->snr = (buf[1]) << 8 | buf[0]; + st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; + } + return 0; +} + +static int gp8psk_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + + if (st->lock) + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + + if (*status & FE_HAS_LOCK) + st->status_check_interval = 1000; + else + st->status_check_interval = 100; + return 0; +} + +/* not supported by this Frontend */ +static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + (void) fe; + *ber = 0; + return 0; +} + +/* not supported by this Frontend */ +static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + (void) fe; + *unc = 0; + return 0; +} + +static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + /* snr is reported in dBu*256 */ + *snr = st->snr; + return 0; +} + +static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + /* snr is reported in dBu*256 */ + /* snr / 38.4 ~= 100% strength */ + /* snr * 17 returns 100% strength as 65535 */ + if (st->snr > 0xf00) + *strength = 0xffff; + else + *strength = (st->snr << 4) + st->snr; /* snr*17 */ + return 0; +} + +static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 cmd[10]; + u32 freq = c->frequency * 1000; + + dprintk("%s()\n", __func__); + + cmd[4] = freq & 0xff; + cmd[5] = (freq >> 8) & 0xff; + cmd[6] = (freq >> 16) & 0xff; + cmd[7] = (freq >> 24) & 0xff; + + /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ + if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) + c->delivery_system = SYS_TURBO; + + switch (c->delivery_system) { + case SYS_DVBS: + if (c->modulation != QPSK) { + dprintk("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + c->fec_inner = FEC_AUTO; + break; + case SYS_DVBS2: /* kept for backwards compatibility */ + dprintk("%s: DVB-S2 delivery system selected\n", __func__); + break; + case SYS_TURBO: + dprintk("%s: Turbo-FEC delivery system selected\n", __func__); + break; + + default: + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + cmd[0] = c->symbol_rate & 0xff; + cmd[1] = (c->symbol_rate >> 8) & 0xff; + cmd[2] = (c->symbol_rate >> 16) & 0xff; + cmd[3] = (c->symbol_rate >> 24) & 0xff; + switch (c->modulation) { + case QPSK: + if (st->is_rev1) + if (gp8psk_tuned_to_DCII(fe)) + st->ops->reload(st->priv); + switch (c->fec_inner) { + case FEC_1_2: + cmd[9] = 0; break; + case FEC_2_3: + cmd[9] = 1; break; + case FEC_3_4: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_7_8: + cmd[9] = 4; break; + case FEC_AUTO: + cmd[9] = 5; break; + default: + cmd[9] = 5; break; + } + if (c->delivery_system == SYS_TURBO) + cmd[8] = ADV_MOD_TURBO_QPSK; + else + cmd[8] = ADV_MOD_DVB_QPSK; + break; + case PSK_8: /* PSK_8 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_8PSK; + switch (c->fec_inner) { + case FEC_2_3: + cmd[9] = 0; break; + case FEC_3_4: + cmd[9] = 1; break; + case FEC_3_5: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_8_9: + cmd[9] = 4; break; + default: + cmd[9] = 0; break; + } + break; + case QAM_16: /* QAM_16 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_16QAM; + cmd[9] = 0; + break; + default: /* Unknown modulation */ + dprintk("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + + if (st->is_rev1) + gp8psk_set_tuner_mode(fe, 0); + st->ops->out(st->priv, TUNE_8PSK, 0, 0, cmd, 10); + + st->lock = 0; + st->next_status_check = jiffies; + st->status_check_interval = 200; + + return 0; +} + +static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, m->msg[0], 0, + m->msg, m->msg_len)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 cmd; + + dprintk("%s\n", __func__); + + /* These commands are certainly wrong */ + cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; + + if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, cmd, 0, + &cmd, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + if (st->ops->out(st->priv, SET_22KHZ_TONE, + (tone == SEC_TONE_ON), 0, NULL, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + if (st->ops->out(st->priv, SET_LNB_VOLTAGE, + voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + return st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0); +} + +static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 cmd = sw_cmd & 0x7f; + + if (st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0)) + return -EINVAL; + + if (st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), + 0, NULL, 0)) + return -EINVAL; + + return 0; +} + +static void gp8psk_fe_release(struct dvb_frontend* fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + kfree(st); +} + +static const struct dvb_frontend_ops gp8psk_fe_ops; + +struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, + void *priv, bool is_rev1) +{ + struct gp8psk_fe_state *st; + + if (!ops || !ops->in || !ops->out || !ops->reload) { + pr_err("Error! gp8psk-fe ops not defined.\n"); + return NULL; + } + + st = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); + if (!st) + return NULL; + + memcpy(&st->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); + st->fe.demodulator_priv = st; + st->ops = ops; + st->priv = priv; + st->is_rev1 = is_rev1; + + pr_info("Frontend %sattached\n", is_rev1 ? "revision 1 " : ""); + + return &st->fe; +} +EXPORT_SYMBOL_GPL(gp8psk_fe_attach); + +static const struct dvb_frontend_ops gp8psk_fe_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Genpix DVB-S", + .frequency_min_hz = 800 * MHz, + .frequency_max_hz = 2250 * MHz, + .frequency_stepsize_hz = 100 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + /* + * FE_CAN_QAM_16 is for compatibility + * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) + */ + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC + }, + + .release = gp8psk_fe_release, + + .init = NULL, + .sleep = NULL, + + .set_frontend = gp8psk_fe_set_frontend, + + .get_tune_settings = gp8psk_fe_get_tune_settings, + + .read_status = gp8psk_fe_read_status, + .read_ber = gp8psk_fe_read_ber, + .read_signal_strength = gp8psk_fe_read_signal_strength, + .read_snr = gp8psk_fe_read_snr, + .read_ucblocks = gp8psk_fe_read_unc_blocks, + + .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, + .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, + .set_tone = gp8psk_fe_set_tone, + .set_voltage = gp8psk_fe_set_voltage, + .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, + .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage +}; + +MODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>"); +MODULE_DESCRIPTION("Frontend Driver for Genpix DVB-S"); +MODULE_VERSION("1.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/gp8psk-fe.h b/drivers/media/dvb-frontends/gp8psk-fe.h new file mode 100644 index 0000000000..2805a3b67d --- /dev/null +++ b/drivers/media/dvb-frontends/gp8psk-fe.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * gp8psk_fe driver + */ + +#ifndef GP8PSK_FE_H +#define GP8PSK_FE_H + +#include <linux/types.h> + +/* gp8psk commands */ + +#define GET_8PSK_CONFIG 0x80 /* in */ +#define SET_8PSK_CONFIG 0x81 +#define I2C_WRITE 0x83 +#define I2C_READ 0x84 +#define ARM_TRANSFER 0x85 +#define TUNE_8PSK 0x86 +#define GET_SIGNAL_STRENGTH 0x87 /* in */ +#define LOAD_BCM4500 0x88 +#define BOOT_8PSK 0x89 /* in */ +#define START_INTERSIL 0x8A /* in */ +#define SET_LNB_VOLTAGE 0x8B +#define SET_22KHZ_TONE 0x8C +#define SEND_DISEQC_COMMAND 0x8D +#define SET_DVB_MODE 0x8E +#define SET_DN_SWITCH 0x8F +#define GET_SIGNAL_LOCK 0x90 /* in */ +#define GET_FW_VERS 0x92 +#define GET_SERIAL_NUMBER 0x93 /* in */ +#define USE_EXTRA_VOLT 0x94 +#define GET_FPGA_VERS 0x95 +#define CW3K_INIT 0x9d + +/* PSK_configuration bits */ +#define bm8pskStarted 0x01 +#define bm8pskFW_Loaded 0x02 +#define bmIntersilOn 0x04 +#define bmDVBmode 0x08 +#define bm22kHz 0x10 +#define bmSEL18V 0x20 +#define bmDCtuned 0x40 +#define bmArmed 0x80 + +/* Satellite modulation modes */ +#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ +#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ +#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ +#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ + +#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ +#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ +#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ +#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ +#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ +#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ + +/* firmware revision id's */ +#define GP8PSK_FW_REV1 0x020604 +#define GP8PSK_FW_REV2 0x020704 +#define GP8PSK_FW_VERS(_fw_vers) \ + ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) + +struct gp8psk_fe_ops { + int (*in)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen); + int (*out)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen); + int (*reload)(void *priv); +}; + +struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, + void *priv, bool is_rev1); + +#endif diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c new file mode 100644 index 0000000000..f127adee3e --- /dev/null +++ b/drivers/media/dvb-frontends/helene.c @@ -0,0 +1,1120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * helene.c + * + * Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <linux/types.h> +#include "helene.h" +#include <media/dvb_frontend.h> + +#define MAX_WRITE_REGSIZE 20 + +enum helene_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct helene_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum helene_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); + enum helene_xtal xtal; +}; + +#define TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system) \ + (((tv_system) != SONY_HELENE_DTV_DVBC_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC_8)\ + && ((tv_system) != SONY_HELENE_DTV_DVBC2_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC2_8)) + +#define HELENE_AUTO 0xff +#define HELENE_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define HELENE_BW_6 0x00 +#define HELENE_BW_7 0x01 +#define HELENE_BW_8 0x02 +#define HELENE_BW_1_7 0x03 + +enum helene_tv_system_t { + SONY_HELENE_TV_SYSTEM_UNKNOWN, + /* Terrestrial Analog */ + SONY_HELENE_ATV_MN_EIAJ, + /**< System-M (Japan) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_SAP, + /**< System-M (US) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_A2, + /**< System-M (Korea) (IF: Fp=5.9MHz in default) */ + SONY_HELENE_ATV_BG, + /**< System-B/G (IF: Fp=7.3MHz in default) */ + SONY_HELENE_ATV_I, + /**< System-I (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_DK, + /**< System-D/K (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L, + /**< System-L (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L_DASH, + /**< System-L DASH (IF: Fp=2.2MHz in default) */ + /* Terrestrial/Cable Digital */ + SONY_HELENE_DTV_8VSB, + /**< ATSC 8VSB (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_QAM, + /**< US QAM (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_ISDBT_6, + /**< ISDB-T 6MHzBW (IF: Fc=3.55MHz in default) */ + SONY_HELENE_DTV_ISDBT_7, + /**< ISDB-T 7MHzBW (IF: Fc=4.15MHz in default) */ + SONY_HELENE_DTV_ISDBT_8, + /**< ISDB-T 8MHzBW (IF: Fc=4.75MHz in default) */ + SONY_HELENE_DTV_DVBT_5, + /**< DVB-T 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_6, + /**< DVB-T 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_7, + /**< DVB-T 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT_8, + /**< DVB-T 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBT2_1_7, + /**< DVB-T2 1.7MHzBW (IF: Fc=3.5MHz in default) */ + SONY_HELENE_DTV_DVBT2_5, + /**< DVB-T2 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_6, + /**< DVB-T2 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_7, + /**< DVB-T2 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT2_8, + /**< DVB-T2 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBC_6, + /**< DVB-C 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC_8, + /**< DVB-C 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DVBC2_6, + /**< DVB-C2 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC2_8, + /**< DVB-C2 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DTMB, + /**< DTMB (IF: Fc=5.1MHz in default) */ + /* Satellite */ + SONY_HELENE_STV_ISDBS, + /**< ISDB-S */ + SONY_HELENE_STV_DVBS, + /**< DVB-S */ + SONY_HELENE_STV_DVBS2, + /**< DVB-S2 */ + + SONY_HELENE_ATV_MIN = SONY_HELENE_ATV_MN_EIAJ, + /**< Minimum analog terrestrial system */ + SONY_HELENE_ATV_MAX = SONY_HELENE_ATV_L_DASH, + /**< Maximum analog terrestrial system */ + SONY_HELENE_DTV_MIN = SONY_HELENE_DTV_8VSB, + /**< Minimum digital terrestrial system */ + SONY_HELENE_DTV_MAX = SONY_HELENE_DTV_DTMB, + /**< Maximum digital terrestrial system */ + SONY_HELENE_TERR_TV_SYSTEM_NUM, + /**< Number of supported terrestrial broadcasting system */ + SONY_HELENE_STV_MIN = SONY_HELENE_STV_ISDBS, + /**< Minimum satellite system */ + SONY_HELENE_STV_MAX = SONY_HELENE_STV_DVBS2 + /**< Maximum satellite system */ +}; + +struct helene_terr_adjust_param_t { + /* < Addr:0x69 Bit[6:4] : RFVGA gain. + * 0xFF means Auto. (RF_GAIN_SEL = 1) + */ + uint8_t RF_GAIN; + /* < Addr:0x69 Bit[3:0] : IF_BPF gain. + */ + uint8_t IF_BPF_GC; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF <= 172MHz) + */ + uint8_t RFOVLD_DET_LV1_VL; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t RFOVLD_DET_LV1_VH; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF > 464MHz) + */ + uint8_t RFOVLD_DET_LV1_U; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF <= 172MHz) + */ + uint8_t IFOVLD_DET_LV_VL; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t IFOVLD_DET_LV_VH; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF > 464MHz) + */ + uint8_t IFOVLD_DET_LV_U; + /* < Addr:0x6D Bit[5:4] : + * IF filter center offset. + */ + uint8_t IF_BPF_F0; + /* < Addr:0x6D Bit[1:0] : + * 6MHzBW(0x00) or 7MHzBW(0x01) + * or 8MHzBW(0x02) or 1.7MHzBW(0x03) + */ + uint8_t BW; + /* < Addr:0x6E Bit[4:0] : + * 5bit signed. IF offset (kHz) = FIF_OFFSET x 50 + */ + uint8_t FIF_OFFSET; + /* < Addr:0x6F Bit[4:0] : + * 5bit signed. BW offset (kHz) = + * BW_OFFSET x 50 (BW_OFFSET x 10 in 1.7MHzBW) + */ + uint8_t BW_OFFSET; + /* < Addr:0x9C Bit[0] : + * Local polarity. (0: Upper Local, 1: Lower Local) + */ + uint8_t IS_LOWERLOCAL; +}; + +static const struct helene_terr_adjust_param_t +terr_params[SONY_HELENE_TERR_TV_SYSTEM_NUM] = { + /*< SONY_HELENE_TV_SYSTEM_UNKNOWN */ + {HELENE_AUTO, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(0), 0x00}, + /* Analog */ + /**< SONY_HELENE_ATV_MN_EIAJ (System-M (Japan)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_SAP (System-M (US)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(3), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_A2 (System-M (Korea)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_7, HELENE_OFFSET(11), HELENE_OFFSET(5), 0x00}, + /**< SONY_HELENE_ATV_BG (System-B/G) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_I (System-I) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_DK (System-D/K) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_L (System-L) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(-1), HELENE_OFFSET(4), 0x00}, + /**< SONY_HELENE_ATV_L_DASH (System-L DASH) */ + /* Digital */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x03, 0x03, 0x03, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_8VSB (ATSC 8VSB) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_QAM (US QAM) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-9), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_6 (ISDB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-7), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_7 (ISDB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-5), HELENE_OFFSET(-7), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_8 (ISDB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_5 (DVB-T 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_6 (DVB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT_7 (DVB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT_8 (DVB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_1_7, HELENE_OFFSET(-10), HELENE_OFFSET(-10), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_1_7 (DVB-T2 1.7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_5 (DVB-T2 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_6 (DVB-T2 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_7 (DVB-T2 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_8 (DVB-T2 8MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-4), 0x00}, + /**< SONY_HELENE_DTV_DVBC_6 (DVB-C 6MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBC_8 (DVB-C 8MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-2), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_6 (DVB-C2 6MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(0), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_8 (DVB-C2 8MHzBW) */ + {HELENE_AUTO, 0x04, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(1), 0x00} + /**< SONY_HELENE_DTV_DTMB (DTMB) */ +}; + +static void helene_i2c_debug(struct helene_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "helene: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("helene: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int helene_write_regs(struct helene_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 > sizeof(buf)) { + dev_warn(&priv->i2c->dev, + "wr reg=%04x: len=%d vs %zu is too big!\n", + reg, len + 1, sizeof(buf)); + return -E2BIG; + } + + helene_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int helene_write_reg(struct helene_priv *priv, u8 reg, u8 val) +{ + u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return helene_write_regs(priv, reg, &tmp, 1); +} + +static int helene_read_regs(struct helene_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + helene_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int helene_read_reg(struct helene_priv *priv, u8 reg, u8 *val) +{ + return helene_read_regs(priv, reg, val, 1); +} + +static int helene_set_reg_bits(struct helene_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = helene_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return helene_write_reg(priv, reg, data); +} + +static int helene_enter_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x0); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + priv->state = STATE_SLEEP; + return 0; +} + +static int helene_leave_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC4); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x40); + + priv->state = STATE_ACTIVE; + return 0; +} + +static int helene_init(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return helene_leave_power_save(priv); +} + +static void helene_release(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int helene_sleep(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + helene_enter_power_save(priv); + return 0; +} + +static enum helene_tv_system_t helene_get_tv_system(struct dvb_frontend *fe) +{ + enum helene_tv_system_t system = SONY_HELENE_TV_SYSTEM_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT_8; + else { + system = SONY_HELENE_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT2_8; + else { + system = SONY_HELENE_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBS) { + system = SONY_HELENE_STV_DVBS; + } else if (p->delivery_system == SYS_DVBS2) { + system = SONY_HELENE_STV_DVBS2; + } else if (p->delivery_system == SYS_ISDBS) { + system = SONY_HELENE_STV_ISDBS; + } else if (p->delivery_system == SYS_ISDBT) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_ISDBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_ISDBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_ISDBT_8; + else { + system = SONY_HELENE_DTV_ISDBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): HELENE DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, + p->bandwidth_hz); + return system; +} + +static int helene_set_params_s(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency; + uint32_t frequency4kHz = 0; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz sr=%uKsps\n", + __func__, frequencykHz, symbol_rate); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_err(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + /* RF switch turn to satellite */ + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + frequency = roundup(p->frequency / 1000, 1); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x15, 0x02); + + /* RFIN matching in power save (Sat) reset */ + helene_write_reg(priv, 0x43, 0x06); + + /* Analog block setting (0x6A, 0x6B) */ + data[0] = 0x00; + data[1] = 0x00; + helene_write_regs(priv, 0x6A, data, 2); + helene_write_reg(priv, 0x75, 0x99); + helene_write_reg(priv, 0x9D, 0x00); + + /* Tuning setting for CPU (0x61) */ + helene_write_reg(priv, 0x61, 0x07); + + /* Satellite mode select (0x01) */ + helene_write_reg(priv, 0x01, 0x01); + + /* Clock enable for internal logic block, CPU wake-up (0x04, 0x05) */ + data[0] = 0xC4; + data[1] = 0x40; + + switch (priv->xtal) { + case SONY_HELENE_XTAL_16000: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_20500: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_24000: + data[2] = 0x03; + break; + case SONY_HELENE_XTAL_41000: + data[2] = 0x05; + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + + /* Setting for analog block (0x07). LOOPFILTER INTERNAL */ + data[3] = 0x80; + + /* Tuning setting for analog block + * (0x08, 0x09, 0x0A, 0x0B). LOOPFILTER INTERNAL + */ + if (priv->xtal == SONY_HELENE_XTAL_20500) + data[4] = 0x58; + else + data[4] = 0x70; + + data[5] = 0x1E; + data[6] = 0x02; + data[7] = 0x24; + + /* Enable for analog block (0x0C, 0x0D, 0x0E). SAT LNA ON */ + data[8] = 0x0F; + data[8] |= 0xE0; /* POWERSAVE_TERR_RF_ACTIVE */ + data[9] = 0x02; + data[10] = 0x1E; + + /* Setting for LPF cutoff frequency (0x0F) */ + switch (tv_system) { + case SONY_HELENE_STV_ISDBS: + data[11] = 0x22; /* 22MHz */ + break; + case SONY_HELENE_STV_DVBS: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 47 + + (40000-1)) / 40000); + else + data[11] = (uint8_t)((symbol_rate * 27 + + (40000-1)) / 40000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + case SONY_HELENE_STV_DVBS2: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 11 + + (10000-1)) / 10000); + else + data[11] = (uint8_t)((symbol_rate * 3 + + (5000-1)) / 5000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown standard %d\n", + __func__, tv_system); + return -EINVAL; + } + + /* RF tuning frequency setting (0x10, 0x11, 0x12) */ + frequency4kHz = (frequencykHz + 2) / 4; + data[12] = (uint8_t)(frequency4kHz & 0xFF); /* FRF_L */ + data[13] = (uint8_t)((frequency4kHz >> 8) & 0xFF); /* FRF_M */ + /* FRF_H (bit[3:0]) */ + data[14] = (uint8_t)((frequency4kHz >> 16) & 0x0F); + + /* Tuning command (0x13) */ + data[15] = 0xFF; + + /* Setting for IQOUT_LIMIT (0x14) 0.75Vpp */ + data[16] = 0x00; + + /* Enable IQ output (0x15) */ + data[17] = 0x01; + + helene_write_regs(priv, 0x04, data, 18); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_set_params_t(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency / 1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, frequencykHz); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + if (priv->state == STATE_SLEEP) + helene_leave_power_save(priv); + + /* Initial setting for internal analog block (0x91, 0x92) */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[0] = 0x16; + data[1] = 0x26; + } else { + data[0] = 0x10; + data[1] = 0x20; + } + helene_write_regs(priv, 0x91, data, 2); + + /* Setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) + data[0] = 0x90; + else + data[0] = 0x00; + + /* Setting for local polarity (0x9D) */ + data[1] = (uint8_t)(terr_params[tv_system].IS_LOWERLOCAL & 0x01); + helene_write_regs(priv, 0x9C, data, 2); + + /* Enable for analog block */ + data[0] = 0xEE; + data[1] = 0x02; + data[2] = 0x1E; + data[3] = 0x67; /* Tuning setting for CPU */ + + /* Setting for PLL reference divider for xtal=24MHz */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) + data[4] = 0x18; + else + data[4] = 0x03; + + /* Tuning setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) { + data[5] = 0x38; + data[6] = 0x1E; + data[7] = 0x02; + data[8] = 0x24; + } else if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[5] = 0x1C; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x1C; + } else { + data[5] = 0xB4; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x30; + } + helene_write_regs(priv, 0x5E, data, 9); + + /* LT_AMP_EN should be 0 */ + helene_set_reg_bits(priv, 0x67, 0x0, 0x02); + + /* Setting for IFOUT_LIMIT */ + data[0] = 0x00; /* 1.5Vpp */ + + /* RF_GAIN setting */ + if (terr_params[tv_system].RF_GAIN == HELENE_AUTO) + data[1] = 0x80; /* RF_GAIN_SEL = 1 */ + else + data[1] = (uint8_t)((terr_params[tv_system].RF_GAIN + << 4) & 0x70); + + /* IF_BPF_GC setting */ + data[1] |= (uint8_t)(terr_params[tv_system].IF_BPF_GC & 0x0F); + + /* Setting for internal RFAGC (0x6A, 0x6B, 0x6C) */ + data[2] = 0x00; + if (frequencykHz <= 172000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VL + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VL + & 0x07); + } else if (frequencykHz <= 464000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VH + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VH + & 0x07); + } else { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_U + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_U + & 0x07); + } + data[4] |= 0x20; + + /* Setting for IF frequency and bandwidth */ + + /* IF filter center frequency offset (IF_BPF_F0) (0x6D) */ + data[5] = (uint8_t)((terr_params[tv_system].IF_BPF_F0 << 4) & 0x30); + + /* IF filter band width (BW) (0x6D) */ + data[5] |= (uint8_t)(terr_params[tv_system].BW & 0x03); + + /* IF frequency offset value (FIF_OFFSET) (0x6E) */ + data[6] = (uint8_t)(terr_params[tv_system].FIF_OFFSET & 0x1F); + + /* IF band width offset value (BW_OFFSET) (0x6F) */ + data[7] = (uint8_t)(terr_params[tv_system].BW_OFFSET & 0x1F); + + /* RF tuning frequency setting (0x70, 0x71, 0x72) */ + data[8] = (uint8_t)(frequencykHz & 0xFF); /* FRF_L */ + data[9] = (uint8_t)((frequencykHz >> 8) & 0xFF); /* FRF_M */ + data[10] = (uint8_t)((frequencykHz >> 16) + & 0x0F); /* FRF_H (bit[3:0]) */ + + /* Tuning command */ + data[11] = 0xFF; + + /* Enable IF output, AGC and IFOUT pin selection (0x74) */ + data[12] = 0x01; + + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[13] = 0xD9; + data[14] = 0x0F; + data[15] = 0x24; + data[16] = 0x87; + } else { + data[13] = 0x99; + data[14] = 0x00; + data[15] = 0x24; + data[16] = 0x87; + } + + helene_write_regs(priv, 0x68, data, 17); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->delivery_system == SYS_DVBT || + p->delivery_system == SYS_DVBT2 || + p->delivery_system == SYS_ISDBT || + p->delivery_system == SYS_DVBC_ANNEX_A) + return helene_set_params_t(fe); + + return helene_set_params_s(fe); +} + +static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct helene_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static const struct dvb_tuner_ops helene_tuner_ops_t = { + .info = { + .name = "Sony HELENE Ter tuner", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1200 * MHz, + .frequency_step_hz = 25 * kHz, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params_t, + .get_frequency = helene_get_frequency, +}; + +static const struct dvb_tuner_ops helene_tuner_ops_s = { + .info = { + .name = "Sony HELENE Sat tuner", + .frequency_min_hz = 500 * MHz, + .frequency_max_hz = 2500 * MHz, + .frequency_step_hz = 1 * MHz, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params_s, + .get_frequency = helene_get_frequency, +}; + +static const struct dvb_tuner_ops helene_tuner_ops = { + .info = { + .name = "Sony HELENE Sat/Ter tuner", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 2500 * MHz, + .frequency_step_hz = 25 * kHz, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params, + .get_frequency = helene_get_frequency, +}; + +/* power-on tuner + * call once after reset + */ +static int helene_x_pon(struct helene_priv *priv) +{ + /* RFIN matching in power save (terrestrial) = ACTIVE */ + /* RFIN matching in power save (satellite) = ACTIVE */ + u8 dataT[] = { 0x06, 0x00, 0x02, 0x00 }; + /* SAT_RF_ACTIVE = true, lnaOff = false, terrRfActive = true */ + u8 dataS[] = { 0x05, 0x06 }; + u8 cdata[] = {0x7A, 0x01}; + u8 data[20]; + u8 rdata[2]; + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + helene_write_reg(priv, 0x67, dataT[3]); + helene_write_reg(priv, 0x43, dataS[1]); + helene_write_regs(priv, 0x5E, dataT, 3); + helene_write_reg(priv, 0x0C, dataS[0]); + + /* Initial setting for internal logic block */ + helene_write_regs(priv, 0x99, cdata, sizeof(cdata)); + + /* 0x81 - 0x94 */ + if (priv->xtal == SONY_HELENE_XTAL_16000) + data[0] = 0x10; /* xtal 16 MHz */ + else + data[0] = 0x18; /* xtal 24 MHz */ + data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */ + data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */ + data[3] = 0x80; /* REFOUT signal output 500mVpp */ + data[4] = 0x00; /* GPIO settings */ + data[5] = 0x00; /* GPIO settings */ + data[6] = 0xC4; /* Clock enable for internal logic block */ + data[7] = 0x40; /* Start CPU boot-up */ + data[8] = 0x10; /* For burst-write */ + + /* Setting for internal RFAGC */ + data[9] = 0x00; + data[10] = 0x45; + data[11] = 0x75; + + data[12] = 0x07; /* Setting for analog block */ + + /* Initial setting for internal analog block */ + data[13] = 0x1C; + data[14] = 0x3F; + data[15] = 0x02; + data[16] = 0x10; + data[17] = 0x20; + data[18] = 0x0A; + data[19] = 0x00; + + helene_write_regs(priv, 0x81, data, sizeof(data)); + + /* Setting for internal RFAGC */ + helene_write_reg(priv, 0x9B, 0x00); + + msleep(20); + + /* Check CPU_STT/CPU_ERR */ + helene_read_regs(priv, 0x1A, rdata, sizeof(rdata)); + + if (rdata[0] != 0x00) { + dev_err(&priv->i2c->dev, + "HELENE tuner CPU error 0x%x\n", rdata[0]); + return -EIO; + } + + /* VCO current setting */ + cdata[0] = 0x90; + cdata[1] = 0x06; + helene_write_regs(priv, 0x17, cdata, sizeof(cdata)); + msleep(20); + helene_read_reg(priv, 0x19, data); + helene_write_reg(priv, 0x95, (uint8_t)((data[0] >> 4) & 0x0F)); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x00); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + /* Load capacitance control setting for crystal oscillator */ + helene_write_reg(priv, 0x80, 0x01); + + /* Satellite initial setting */ + cdata[0] = 0x07; + cdata[1] = 0x00; + helene_write_regs(priv, 0x41, cdata, sizeof(cdata)); + + dev_info(&priv->i2c->dev, + "HELENE tuner x_pon done\n"); + + return 0; +} + +struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) { + kfree(priv); + return NULL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL_GPL(helene_attach_s); + +struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) { + kfree(priv); + return NULL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL_GPL(helene_attach); + +static int helene_probe(struct i2c_client *client) +{ + struct helene_config *config = client->dev.platform_data; + struct dvb_frontend *fe = config->fe; + struct device *dev = &client->dev; + struct helene_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->i2c_address = client->addr; + priv->i2c = client->adapter; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + i2c_set_clientdata(client, priv); + + dev_info(dev, "Sony HELENE attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + + return 0; +} + +static const struct i2c_device_id helene_id[] = { + { "helene", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, helene_id); + +static struct i2c_driver helene_driver = { + .driver = { + .name = "helene", + }, + .probe = helene_probe, + .id_table = helene_id, +}; +module_i2c_driver(helene_driver); + +MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver"); +MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h new file mode 100644 index 0000000000..32e0b1fb26 --- /dev/null +++ b/drivers/media/dvb-frontends/helene.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * helene.h + * + * Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#ifndef __DVB_HELENE_H__ +#define __DVB_HELENE_H__ + +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +enum helene_xtal { + SONY_HELENE_XTAL_16000, /* 16 MHz */ + SONY_HELENE_XTAL_20500, /* 20.5 MHz */ + SONY_HELENE_XTAL_24000, /* 24 MHz */ + SONY_HELENE_XTAL_41000 /* 41 MHz */ +}; + +/** + * struct helene_config - the configuration of 'Helene' tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + * @xtal: Cristal frequency as described by &enum helene_xtal + * @fe: Frontend for which connects this tuner + */ +struct helene_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); + enum helene_xtal xtal; + + struct dvb_frontend *fe; +}; + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +/** + * helene_attach - Attach a helene tuner (terrestrial and cable standards) + * + * @fe: frontend to be attached + * @config: pointer to &struct helene_config with tuner configuration. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); + +/** + * helene_attach_s - Attach a helene tuner (satellite standards) + * + * @fe: frontend to be attached + * @config: pointer to &struct helene_config with tuner configuration. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c new file mode 100644 index 0000000000..0330b78a5b --- /dev/null +++ b/drivers/media/dvb-frontends/horus3a.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * horus3a.h + * + * Sony Horus3A DVB-S/S2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <linux/types.h> +#include "horus3a.h" +#include <media/dvb_frontend.h> + +#define MAX_WRITE_REGSIZE 5 + +enum horus3a_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct horus3a_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum horus3a_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); +}; + +static void horus3a_i2c_debug(struct horus3a_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("horus3a: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int horus3a_write_regs(struct horus3a_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 > sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + + horus3a_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val) +{ + u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return horus3a_write_regs(priv, reg, &tmp, 1); +} + +static int horus3a_enter_power_save(struct horus3a_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + /* IQ Generator disable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* MDIV_EN = 0 */ + horus3a_write_reg(priv, 0x29, 0x70); + /* VCO disable preparation */ + horus3a_write_reg(priv, 0x28, 0x3e); + /* VCO buffer disable */ + horus3a_write_reg(priv, 0x2a, 0x19); + /* VCO calibration disable */ + horus3a_write_reg(priv, 0x1c, 0x00); + /* Power save setting (xtal is not stopped) */ + data[0] = 0xC0; + /* LNA is Disabled */ + data[1] = 0xA7; + /* 0x11 - 0x12 */ + horus3a_write_regs(priv, 0x11, data, sizeof(data)); + priv->state = STATE_SLEEP; + return 0; +} + +static int horus3a_leave_power_save(struct horus3a_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + /* Leave power save */ + data[0] = 0x00; + /* LNA is Disabled */ + data[1] = 0xa7; + /* 0x11 - 0x12 */ + horus3a_write_regs(priv, 0x11, data, sizeof(data)); + /* VCO buffer enable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* VCO calibration enable */ + horus3a_write_reg(priv, 0x1c, 0xc0); + /* MDIV_EN = 1 */ + horus3a_write_reg(priv, 0x29, 0x71); + usleep_range(5000, 7000); + priv->state = STATE_ACTIVE; + return 0; +} + +static int horus3a_init(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return 0; +} + +static void horus3a_release(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int horus3a_sleep(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + horus3a_enter_power_save(priv); + return 0; +} + +static int horus3a_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct horus3a_priv *priv = fe->tuner_priv; + u32 frequency = p->frequency; + u32 symbol_rate = p->symbol_rate/1000; + u8 mixdiv = 0; + u8 mdiv = 0; + u32 ms = 0; + u8 f_ctl = 0; + u8 g_ctl = 0; + u8 fc_lpf = 0; + u8 data[5]; + + dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n", + __func__, frequency, symbol_rate); + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + if (priv->state == STATE_SLEEP) + horus3a_leave_power_save(priv); + + /* frequency should be X MHz (X : integer) */ + frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000; + if (frequency <= 1155000) { + mixdiv = 4; + mdiv = 1; + } else { + mixdiv = 2; + mdiv = 0; + } + /* Assumed that fREF == 1MHz (1000kHz) */ + ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000); + if (ms > 0x7FFF) { /* 15 bit */ + dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n", + frequency); + return -EINVAL; + } + if (frequency < 975000) { + /* F_CTL=11100 G_CTL=001 */ + f_ctl = 0x1C; + g_ctl = 0x01; + } else if (frequency < 1050000) { + /* F_CTL=11000 G_CTL=010 */ + f_ctl = 0x18; + g_ctl = 0x02; + } else if (frequency < 1150000) { + /* F_CTL=10100 G_CTL=010 */ + f_ctl = 0x14; + g_ctl = 0x02; + } else if (frequency < 1250000) { + /* F_CTL=10000 G_CTL=011 */ + f_ctl = 0x10; + g_ctl = 0x03; + } else if (frequency < 1350000) { + /* F_CTL=01100 G_CTL=100 */ + f_ctl = 0x0C; + g_ctl = 0x04; + } else if (frequency < 1450000) { + /* F_CTL=01010 G_CTL=100 */ + f_ctl = 0x0A; + g_ctl = 0x04; + } else if (frequency < 1600000) { + /* F_CTL=00111 G_CTL=101 */ + f_ctl = 0x07; + g_ctl = 0x05; + } else if (frequency < 1800000) { + /* F_CTL=00100 G_CTL=010 */ + f_ctl = 0x04; + g_ctl = 0x02; + } else if (frequency < 2000000) { + /* F_CTL=00010 G_CTL=001 */ + f_ctl = 0x02; + g_ctl = 0x01; + } else { + /* F_CTL=00000 G_CTL=000 */ + f_ctl = 0x00; + g_ctl = 0x00; + } + /* LPF cutoff frequency setting */ + if (p->delivery_system == SYS_DVBS) { + /* + * rolloff = 0.35 + * SR <= 4.3 + * fc_lpf = 5 + * 4.3 < SR <= 10 + * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 = + * SR * 1.175 = SR * (47/40) + * 10 < SR + * fc_lpf = SR * (1 + rolloff) / 2 + 5 = + * SR * 0.675 + 5 = SR * (27/40) + 5 + * NOTE: The result should be round up. + */ + if (symbol_rate <= 4300) + fc_lpf = 5; + else if (symbol_rate <= 10000) + fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000); + else + fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 27, 40000) + 5; + /* 5 <= fc_lpf <= 36 */ + if (fc_lpf > 36) + fc_lpf = 36; + } else if (p->delivery_system == SYS_DVBS2) { + /* + * SR <= 4.5: + * fc_lpf = 5 + * 4.5 < SR <= 10: + * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 + * 10 < SR: + * fc_lpf = SR * (1 + rolloff) / 2 + 5 + * NOTE: The result should be round up. + */ + if (symbol_rate <= 4500) + fc_lpf = 5; + else if (symbol_rate <= 10000) + fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000); + else + fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5); + /* 5 <= fc_lpf <= 36 is valid */ + if (fc_lpf > 36) + fc_lpf = 36; + } else { + dev_err(&priv->i2c->dev, + "horus3a: invalid delivery system %d\n", + p->delivery_system); + return -EINVAL; + } + /* 0x00 - 0x04 */ + data[0] = (u8)((ms >> 7) & 0xFF); + data[1] = (u8)((ms << 1) & 0xFF); + data[2] = 0x00; + data[3] = 0x00; + data[4] = (u8)(mdiv << 7); + horus3a_write_regs(priv, 0x00, data, sizeof(data)); + /* Write G_CTL, F_CTL */ + horus3a_write_reg(priv, 0x09, (u8)((g_ctl << 5) | f_ctl)); + /* Write LPF cutoff frequency */ + horus3a_write_reg(priv, 0x37, (u8)(0x80 | (fc_lpf << 1))); + /* Start Calibration */ + horus3a_write_reg(priv, 0x05, 0x80); + /* IQ Generator enable */ + horus3a_write_reg(priv, 0x2a, 0x7b); + /* tuner stabilization time */ + msleep(60); + /* Store tuned frequency to the struct */ + priv->frequency = ms * 2 * 1000 / mixdiv; + return 0; +} + +static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + return 0; +} + +static const struct dvb_tuner_ops horus3a_tuner_ops = { + .info = { + .name = "Sony Horus3a", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, + }, + .init = horus3a_init, + .release = horus3a_release, + .sleep = horus3a_sleep, + .set_params = horus3a_set_params, + .get_frequency = horus3a_get_frequency, +}; + +struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c) +{ + u8 buf[3], val; + struct horus3a_priv *priv = NULL; + + priv = kzalloc(sizeof(struct horus3a_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* wait 4ms after power on */ + usleep_range(4000, 6000); + /* IQ Generator disable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* REF_R = Xtal Frequency */ + buf[0] = config->xtal_freq_mhz; + buf[1] = config->xtal_freq_mhz; + buf[2] = 0; + /* 0x6 - 0x8 */ + horus3a_write_regs(priv, 0x6, buf, 3); + /* IQ Out = Single Ended */ + horus3a_write_reg(priv, 0x0a, 0x40); + switch (config->xtal_freq_mhz) { + case 27: + val = 0x1f; + break; + case 24: + val = 0x10; + break; + case 16: + val = 0xc; + break; + default: + val = 0; + dev_warn(&priv->i2c->dev, + "horus3a: invalid xtal frequency %dMHz\n", + config->xtal_freq_mhz); + break; + } + val <<= 2; + horus3a_write_reg(priv, 0x0e, val); + horus3a_enter_power_save(priv); + usleep_range(3000, 5000); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HORUS3A attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL_GPL(horus3a_attach); + +MODULE_DESCRIPTION("Sony HORUS3A satellite tuner driver"); +MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h new file mode 100644 index 0000000000..91dbe20169 --- /dev/null +++ b/drivers/media/dvb-frontends/horus3a.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * horus3a.h + * + * Sony Horus3A DVB-S/S2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#ifndef __DVB_HORUS3A_H__ +#define __DVB_HORUS3A_H__ + +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +/** + * struct horus3a_config - the configuration of Horus3A tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct horus3a_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); +}; + +#if IS_REACHABLE(CONFIG_DVB_HORUS3A) +/** + * horus3a_attach - Attach a horus3a tuner + * + * @fe: frontend to be attached + * @config: pointer to &struct helene_config with tuner configuration. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c new file mode 100644 index 0000000000..7d28a743f9 --- /dev/null +++ b/drivers/media/dvb-frontends/isl6405.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * isl6405.c - driver for dual lnb supply and control ic ISL6405 + * + * Copyright (C) 2008 Hartmut Hackmann + * Copyright (C) 2006 Oliver Endriss + * + * the project's page is at https://linuxtv.org + */ +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "isl6405.h" + +struct isl6405 { + u8 config; + u8 override_or; + u8 override_and; + struct i2c_adapter *i2c; + u8 i2c_addr; +}; + +static int isl6405_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; + struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, + .buf = &isl6405->config, + .len = sizeof(isl6405->config) }; + + if (isl6405->override_or & 0x80) { + isl6405->config &= ~(ISL6405_VSEL2 | ISL6405_EN2); + switch (voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + isl6405->config |= ISL6405_EN2; + break; + case SEC_VOLTAGE_18: + isl6405->config |= (ISL6405_EN2 | ISL6405_VSEL2); + break; + default: + return -EINVAL; + } + } else { + isl6405->config &= ~(ISL6405_VSEL1 | ISL6405_EN1); + switch (voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + isl6405->config |= ISL6405_EN1; + break; + case SEC_VOLTAGE_18: + isl6405->config |= (ISL6405_EN1 | ISL6405_VSEL1); + break; + default: + return -EINVAL; + } + } + isl6405->config |= isl6405->override_or; + isl6405->config &= isl6405->override_and; + + return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int isl6405_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; + struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, + .buf = &isl6405->config, + .len = sizeof(isl6405->config) }; + + if (isl6405->override_or & 0x80) { + if (arg) + isl6405->config |= ISL6405_LLC2; + else + isl6405->config &= ~ISL6405_LLC2; + } else { + if (arg) + isl6405->config |= ISL6405_LLC1; + else + isl6405->config &= ~ISL6405_LLC1; + } + isl6405->config |= isl6405->override_or; + isl6405->config &= isl6405->override_and; + + return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void isl6405_release(struct dvb_frontend *fe) +{ + /* power off */ + isl6405_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, + u8 i2c_addr, u8 override_set, u8 override_clear) +{ + struct isl6405 *isl6405 = kmalloc(sizeof(struct isl6405), GFP_KERNEL); + if (!isl6405) + return NULL; + + /* default configuration */ + if (override_set & 0x80) + isl6405->config = ISL6405_ISEL2; + else + isl6405->config = ISL6405_ISEL1; + isl6405->i2c = i2c; + isl6405->i2c_addr = i2c_addr; + fe->sec_priv = isl6405; + + /* bits which should be forced to '1' */ + isl6405->override_or = override_set; + + /* bits which should be forced to '0' */ + isl6405->override_and = ~override_clear; + + /* detect if it is present or not */ + if (isl6405_set_voltage(fe, SEC_VOLTAGE_OFF)) { + kfree(isl6405); + fe->sec_priv = NULL; + return NULL; + } + + /* install release callback */ + fe->ops.release_sec = isl6405_release; + + /* override frontend ops */ + fe->ops.set_voltage = isl6405_set_voltage; + fe->ops.enable_high_lnb_voltage = isl6405_enable_high_lnb_voltage; + + return fe; +} +EXPORT_SYMBOL_GPL(isl6405_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405"); +MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h new file mode 100644 index 0000000000..c1cb131d24 --- /dev/null +++ b/drivers/media/dvb-frontends/isl6405.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * isl6405.h - driver for dual lnb supply and control ic ISL6405 + * + * Copyright (C) 2008 Hartmut Hackmann + * Copyright (C) 2006 Oliver Endriss + * + * the project's page is at https://linuxtv.org + */ + +#ifndef _ISL6405_H +#define _ISL6405_H + +#include <linux/dvb/frontend.h> + +/* system register bits */ + +/* this bit selects register (control) 1 or 2 + note that the bit maps are different */ + +#define ISL6405_SR 0x80 + +/* SR = 0 */ +#define ISL6405_OLF1 0x01 +#define ISL6405_EN1 0x02 +#define ISL6405_VSEL1 0x04 +#define ISL6405_LLC1 0x08 +#define ISL6405_ENT1 0x10 +#define ISL6405_ISEL1 0x20 +#define ISL6405_DCL 0x40 + +/* SR = 1 */ +#define ISL6405_OLF2 0x01 +#define ISL6405_OTF 0x02 +#define ISL6405_EN2 0x04 +#define ISL6405_VSEL2 0x08 +#define ISL6405_LLC2 0x10 +#define ISL6405_ENT2 0x20 +#define ISL6405_ISEL2 0x40 + +#if IS_REACHABLE(CONFIG_DVB_ISL6405) +/* override_set and override_clear control which system register bits (above) + * to always set & clear + */ +extern struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, + u8 i2c_addr, u8 override_set, u8 override_clear); +#else +static inline struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ISL6405 */ + +#endif diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c new file mode 100644 index 0000000000..2e9f6f12f8 --- /dev/null +++ b/drivers/media/dvb-frontends/isl6421.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * isl6421.h - driver for lnb supply and control ic ISL6421 + * + * Copyright (C) 2006 Andrew de Quincey + * Copyright (C) 2006 Oliver Endriss + * + * the project's page is at https://linuxtv.org + */ +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "isl6421.h" + +struct isl6421 { + u8 config; + u8 override_or; + u8 override_and; + struct i2c_adapter *i2c; + u8 i2c_addr; + bool is_off; +}; + +static int isl6421_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + int ret; + u8 buf; + bool is_off; + struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; + struct i2c_msg msg[2] = { + { + .addr = isl6421->i2c_addr, + .flags = 0, + .buf = &isl6421->config, + .len = 1, + }, { + .addr = isl6421->i2c_addr, + .flags = I2C_M_RD, + .buf = &buf, + .len = 1, + } + + }; + + isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); + + switch(voltage) { + case SEC_VOLTAGE_OFF: + is_off = true; + break; + case SEC_VOLTAGE_13: + is_off = false; + isl6421->config |= ISL6421_EN1; + break; + case SEC_VOLTAGE_18: + is_off = false; + isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1); + break; + default: + return -EINVAL; + } + + /* + * If LNBf were not powered on, disable dynamic current limit, as, + * according with datasheet, highly capacitive load on the output may + * cause a difficult start-up. + */ + if (isl6421->is_off && !is_off) + isl6421->config |= ISL6421_DCL; + + isl6421->config |= isl6421->override_or; + isl6421->config &= isl6421->override_and; + + ret = i2c_transfer(isl6421->i2c, msg, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + /* Store off status now in case future commands fail */ + isl6421->is_off = is_off; + + /* On overflow, the device will try again after 900 ms (typically) */ + if (!is_off && (buf & ISL6421_OLF1)) + msleep(1000); + + /* Re-enable dynamic current limit */ + if ((isl6421->config & ISL6421_DCL) && + !(isl6421->override_or & ISL6421_DCL)) { + isl6421->config &= ~ISL6421_DCL; + + ret = i2c_transfer(isl6421->i2c, msg, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + } + + /* Check if overload flag is active. If so, disable power */ + if (!is_off && (buf & ISL6421_OLF1)) { + isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); + ret = i2c_transfer(isl6421->i2c, msg, 1); + if (ret < 0) + return ret; + if (ret != 1) + return -EIO; + isl6421->is_off = true; + + dev_warn(&isl6421->i2c->dev, + "Overload current detected. disabling LNBf power\n"); + return -EINVAL; + } + + return 0; +} + +static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; + struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, + .buf = &isl6421->config, + .len = sizeof(isl6421->config) }; + + if (arg) + isl6421->config |= ISL6421_LLC1; + else + isl6421->config &= ~ISL6421_LLC1; + + isl6421->config |= isl6421->override_or; + isl6421->config &= isl6421->override_and; + + return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int isl6421_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; + struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, + .buf = &isl6421->config, + .len = sizeof(isl6421->config) }; + + switch (tone) { + case SEC_TONE_ON: + isl6421->config |= ISL6421_ENT1; + break; + case SEC_TONE_OFF: + isl6421->config &= ~ISL6421_ENT1; + break; + default: + return -EINVAL; + } + + isl6421->config |= isl6421->override_or; + isl6421->config &= isl6421->override_and; + + return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void isl6421_release(struct dvb_frontend *fe) +{ + /* power off */ + isl6421_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear, bool override_tone) +{ + struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); + if (!isl6421) + return NULL; + + /* default configuration */ + isl6421->config = ISL6421_ISEL1; + isl6421->i2c = i2c; + isl6421->i2c_addr = i2c_addr; + fe->sec_priv = isl6421; + + /* bits which should be forced to '1' */ + isl6421->override_or = override_set; + + /* bits which should be forced to '0' */ + isl6421->override_and = ~override_clear; + + /* detect if it is present or not */ + if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { + kfree(isl6421); + fe->sec_priv = NULL; + return NULL; + } + + isl6421->is_off = true; + + /* install release callback */ + fe->ops.release_sec = isl6421_release; + + /* override frontend ops */ + fe->ops.set_voltage = isl6421_set_voltage; + fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; + if (override_tone) + fe->ops.set_tone = isl6421_set_tone; + + return fe; +} +EXPORT_SYMBOL_GPL(isl6421_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421"); +MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h new file mode 100644 index 0000000000..e594877849 --- /dev/null +++ b/drivers/media/dvb-frontends/isl6421.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * isl6421.h - driver for lnb supply and control ic ISL6421 + * + * Copyright (C) 2006 Andrew de Quincey + * Copyright (C) 2006 Oliver Endriss + * + * the project's page is at https://linuxtv.org + */ + +#ifndef _ISL6421_H +#define _ISL6421_H + +#include <linux/dvb/frontend.h> + +/* system register bits */ +#define ISL6421_OLF1 0x01 +#define ISL6421_EN1 0x02 +#define ISL6421_VSEL1 0x04 +#define ISL6421_LLC1 0x08 +#define ISL6421_ENT1 0x10 +#define ISL6421_ISEL1 0x20 +#define ISL6421_DCL 0x40 + +#if IS_REACHABLE(CONFIG_DVB_ISL6421) +/* override_set and override_clear control which system register bits (above) to always set & clear */ +extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear, bool override_tone); +#else +static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear, bool override_tone) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_ISL6421 + +#endif diff --git a/drivers/media/dvb-frontends/isl6423.c b/drivers/media/dvb-frontends/isl6423.c new file mode 100644 index 0000000000..a0d0a38340 --- /dev/null +++ b/drivers/media/dvb-frontends/isl6423.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Intersil ISL6423 SEC and LNB Power supply controller + + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + +*/ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "isl6423.h" + +static unsigned int verbose; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "Set Verbosity level"); + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 +#define FE_DEBUGREG 4 + +#define dprintk(__y, __z, format, arg...) do { \ + if (__z) { \ + if ((verbose > FE_ERROR) && (verbose > __y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_NOTICE) && (verbose > __y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_INFO) && (verbose > __y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_DEBUG) && (verbose > __y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (verbose > __y) \ + printk(format, ##arg); \ + } \ +} while (0) + +struct isl6423_dev { + const struct isl6423_config *config; + struct i2c_adapter *i2c; + + u8 reg_3; + u8 reg_4; + + unsigned int verbose; +}; + +static int isl6423_write(struct isl6423_dev *isl6423, u8 reg) +{ + struct i2c_adapter *i2c = isl6423->i2c; + u8 addr = isl6423->config->addr; + int err = 0; + + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = ®, .len = 1 }; + + dprintk(FE_DEBUG, 1, "write reg %02X", reg); + err = i2c_transfer(i2c, &msg, 1); + if (err < 0) + goto exit; + return 0; + +exit: + dprintk(FE_ERROR, 1, "I/O error <%d>", err); + return err; +} + +static int isl6423_set_modulation(struct dvb_frontend *fe) +{ + struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; + const struct isl6423_config *config = isl6423->config; + int err = 0; + u8 reg_2 = 0; + + reg_2 = 0x01 << 5; + + if (config->mod_extern) + reg_2 |= (1 << 3); + else + reg_2 |= (1 << 4); + + err = isl6423_write(isl6423, reg_2); + if (err < 0) + goto exit; + return 0; + +exit: + dprintk(FE_ERROR, 1, "I/O error <%d>", err); + return err; +} + +static int isl6423_voltage_boost(struct dvb_frontend *fe, long arg) +{ + struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; + u8 reg_3 = isl6423->reg_3; + u8 reg_4 = isl6423->reg_4; + int err = 0; + + if (arg) { + /* EN = 1, VSPEN = 1, VBOT = 1 */ + reg_4 |= (1 << 4); + reg_4 |= 0x1; + reg_3 |= (1 << 3); + } else { + /* EN = 1, VSPEN = 1, VBOT = 0 */ + reg_4 |= (1 << 4); + reg_4 &= ~0x1; + reg_3 |= (1 << 3); + } + err = isl6423_write(isl6423, reg_3); + if (err < 0) + goto exit; + + err = isl6423_write(isl6423, reg_4); + if (err < 0) + goto exit; + + isl6423->reg_3 = reg_3; + isl6423->reg_4 = reg_4; + + return 0; +exit: + dprintk(FE_ERROR, 1, "I/O error <%d>", err); + return err; +} + + +static int isl6423_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; + u8 reg_3 = isl6423->reg_3; + u8 reg_4 = isl6423->reg_4; + int err = 0; + + switch (voltage) { + case SEC_VOLTAGE_OFF: + /* EN = 0 */ + reg_4 &= ~(1 << 4); + break; + + case SEC_VOLTAGE_13: + /* EN = 1, VSPEN = 1, VTOP = 0, VBOT = 0 */ + reg_4 |= (1 << 4); + reg_4 &= ~0x3; + reg_3 |= (1 << 3); + break; + + case SEC_VOLTAGE_18: + /* EN = 1, VSPEN = 1, VTOP = 1, VBOT = 0 */ + reg_4 |= (1 << 4); + reg_4 |= 0x2; + reg_4 &= ~0x1; + reg_3 |= (1 << 3); + break; + + default: + break; + } + err = isl6423_write(isl6423, reg_3); + if (err < 0) + goto exit; + + err = isl6423_write(isl6423, reg_4); + if (err < 0) + goto exit; + + isl6423->reg_3 = reg_3; + isl6423->reg_4 = reg_4; + + return 0; +exit: + dprintk(FE_ERROR, 1, "I/O error <%d>", err); + return err; +} + +static int isl6423_set_current(struct dvb_frontend *fe) +{ + struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; + u8 reg_3 = isl6423->reg_3; + const struct isl6423_config *config = isl6423->config; + int err = 0; + + switch (config->current_max) { + case SEC_CURRENT_275m: + /* 275mA */ + /* ISELH = 0, ISELL = 0 */ + reg_3 &= ~0x3; + break; + + case SEC_CURRENT_515m: + /* 515mA */ + /* ISELH = 0, ISELL = 1 */ + reg_3 &= ~0x2; + reg_3 |= 0x1; + break; + + case SEC_CURRENT_635m: + /* 635mA */ + /* ISELH = 1, ISELL = 0 */ + reg_3 &= ~0x1; + reg_3 |= 0x2; + break; + + case SEC_CURRENT_800m: + /* 800mA */ + /* ISELH = 1, ISELL = 1 */ + reg_3 |= 0x3; + break; + } + + err = isl6423_write(isl6423, reg_3); + if (err < 0) + goto exit; + + switch (config->curlim) { + case SEC_CURRENT_LIM_ON: + /* DCL = 0 */ + reg_3 &= ~0x10; + break; + + case SEC_CURRENT_LIM_OFF: + /* DCL = 1 */ + reg_3 |= 0x10; + break; + } + + err = isl6423_write(isl6423, reg_3); + if (err < 0) + goto exit; + + isl6423->reg_3 = reg_3; + + return 0; +exit: + dprintk(FE_ERROR, 1, "I/O error <%d>", err); + return err; +} + +static void isl6423_release(struct dvb_frontend *fe) +{ + isl6423_set_voltage(fe, SEC_VOLTAGE_OFF); + + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct isl6423_config *config) +{ + struct isl6423_dev *isl6423; + + isl6423 = kzalloc(sizeof(struct isl6423_dev), GFP_KERNEL); + if (!isl6423) + return NULL; + + isl6423->config = config; + isl6423->i2c = i2c; + fe->sec_priv = isl6423; + + /* SR3H = 0, SR3M = 1, SR3L = 0 */ + isl6423->reg_3 = 0x02 << 5; + /* SR4H = 0, SR4M = 1, SR4L = 1 */ + isl6423->reg_4 = 0x03 << 5; + + if (isl6423_set_current(fe)) + goto exit; + + if (isl6423_set_modulation(fe)) + goto exit; + + fe->ops.release_sec = isl6423_release; + fe->ops.set_voltage = isl6423_set_voltage; + fe->ops.enable_high_lnb_voltage = isl6423_voltage_boost; + isl6423->verbose = verbose; + + return fe; + +exit: + kfree(isl6423); + fe->sec_priv = NULL; + return NULL; +} +EXPORT_SYMBOL_GPL(isl6423_attach); + +MODULE_DESCRIPTION("ISL6423 SEC"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h new file mode 100644 index 0000000000..9fa87ceffd --- /dev/null +++ b/drivers/media/dvb-frontends/isl6423.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Intersil ISL6423 SEC and LNB Power supply controller + + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + +*/ + +#ifndef __ISL_6423_H +#define __ISL_6423_H + +#include <linux/dvb/frontend.h> + +enum isl6423_current { + SEC_CURRENT_275m = 0, + SEC_CURRENT_515m, + SEC_CURRENT_635m, + SEC_CURRENT_800m, +}; + +enum isl6423_curlim { + SEC_CURRENT_LIM_ON = 1, + SEC_CURRENT_LIM_OFF +}; + +struct isl6423_config { + enum isl6423_current current_max; + enum isl6423_curlim curlim; + u8 addr; + u8 mod_extern; +}; + +#if IS_REACHABLE(CONFIG_DVB_ISL6423) + + +extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct isl6423_config *config); + +#else +static inline struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct isl6423_config *config) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_ISL6423 */ + +#endif /* __ISL_6423_H */ diff --git a/drivers/media/dvb-frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c new file mode 100644 index 0000000000..f8f362f50e --- /dev/null +++ b/drivers/media/dvb-frontends/itd1000.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" + * + * Copyright (c) 2007-8 Patrick Boettcher <pb@linuxtv.org> + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> + +#include "itd1000.h" +#include "itd1000_priv.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define itd_dbg(args...) do { \ + if (debug) { \ + printk(KERN_DEBUG "ITD1000: " args);\ + } \ +} while (0) + +#define itd_warn(args...) do { \ + printk(KERN_WARNING "ITD1000: " args); \ +} while (0) + +#define itd_info(args...) do { \ + printk(KERN_INFO "ITD1000: " args); \ +} while (0) + +/* don't write more than one byte with flexcop behind */ +static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 len) +{ + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg msg = { + .addr = state->cfg->i2c_address, .flags = 0, .buf = buf, .len = len+1 + }; + + if (1 + len > sizeof(buf)) { + printk(KERN_WARNING + "itd1000: i2c wr reg=%04x: len=%d is too big!\n", + reg, len); + return -EINVAL; + } + + buf[0] = reg; + memcpy(&buf[1], v, len); + + /* itd_dbg("wr %02x: %02x\n", reg, v[0]); */ + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "itd1000 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int itd1000_read_reg(struct itd1000_state *state, u8 reg) +{ + u8 val; + struct i2c_msg msg[2] = { + { .addr = state->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }, + }; + + /* ugly flexcop workaround */ + itd1000_write_regs(state, (reg - 1) & 0xff, &state->shadow[(reg - 1) & 0xff], 1); + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + itd_warn("itd1000 I2C read failed\n"); + return -EREMOTEIO; + } + return val; +} + +static inline int itd1000_write_reg(struct itd1000_state *state, u8 r, u8 v) +{ + u8 tmp = v; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + int ret = itd1000_write_regs(state, r, &tmp, 1); + state->shadow[r] = tmp; + return ret; +} + + +static struct { + u32 symbol_rate; + u8 pgaext : 4; /* PLLFH */ + u8 bbgvmin : 4; /* BBGVMIN */ +} itd1000_lpf_pga[] = { + { 0, 0x8, 0x3 }, + { 5200000, 0x8, 0x3 }, + { 12200000, 0x4, 0x3 }, + { 15400000, 0x2, 0x3 }, + { 19800000, 0x2, 0x3 }, + { 21500000, 0x2, 0x3 }, + { 24500000, 0x2, 0x3 }, + { 28400000, 0x2, 0x3 }, + { 33400000, 0x2, 0x3 }, + { 34400000, 0x1, 0x4 }, + { 34400000, 0x1, 0x4 }, + { 38400000, 0x1, 0x4 }, + { 38400000, 0x1, 0x4 }, + { 40400000, 0x1, 0x4 }, + { 45400000, 0x1, 0x4 }, +}; + +static void itd1000_set_lpf_bw(struct itd1000_state *state, u32 symbol_rate) +{ + u8 i; + u8 con1 = itd1000_read_reg(state, CON1) & 0xfd; + u8 pllfh = itd1000_read_reg(state, PLLFH) & 0x0f; + u8 bbgvmin = itd1000_read_reg(state, BBGVMIN) & 0xf0; + u8 bw = itd1000_read_reg(state, BW) & 0xf0; + + itd_dbg("symbol_rate = %d\n", symbol_rate); + + /* not sure what is that ? - starting to download the table */ + itd1000_write_reg(state, CON1, con1 | (1 << 1)); + + for (i = 0; i < ARRAY_SIZE(itd1000_lpf_pga); i++) + if (symbol_rate < itd1000_lpf_pga[i].symbol_rate) { + itd_dbg("symrate: index: %d pgaext: %x, bbgvmin: %x\n", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin); + itd1000_write_reg(state, PLLFH, pllfh | (itd1000_lpf_pga[i].pgaext << 4)); + itd1000_write_reg(state, BBGVMIN, bbgvmin | (itd1000_lpf_pga[i].bbgvmin)); + itd1000_write_reg(state, BW, bw | (i & 0x0f)); + break; + } + + itd1000_write_reg(state, CON1, con1 | (0 << 1)); +} + +static struct { + u8 vcorg; + u32 fmax_rg; +} itd1000_vcorg[] = { + { 1, 920000 }, + { 2, 971000 }, + { 3, 1031000 }, + { 4, 1091000 }, + { 5, 1171000 }, + { 6, 1281000 }, + { 7, 1381000 }, + { 8, 500000 }, /* this is intentional. */ + { 9, 1451000 }, + { 10, 1531000 }, + { 11, 1631000 }, + { 12, 1741000 }, + { 13, 1891000 }, + { 14, 2071000 }, + { 15, 2250000 }, +}; + +static void itd1000_set_vco(struct itd1000_state *state, u32 freq_khz) +{ + u8 i; + u8 gvbb_i2c = itd1000_read_reg(state, GVBB_I2C) & 0xbf; + u8 vco_chp1_i2c = itd1000_read_reg(state, VCO_CHP1_I2C) & 0x0f; + u8 adcout; + + /* reserved bit again (reset ?) */ + itd1000_write_reg(state, GVBB_I2C, gvbb_i2c | (1 << 6)); + + for (i = 0; i < ARRAY_SIZE(itd1000_vcorg); i++) { + if (freq_khz < itd1000_vcorg[i].fmax_rg) { + itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | (itd1000_vcorg[i].vcorg << 4)); + msleep(1); + + adcout = itd1000_read_reg(state, PLLLOCK) & 0x0f; + + itd_dbg("VCO: %dkHz: %d -> ADCOUT: %d %02x\n", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c); + + if (adcout > 13) { + if (!(itd1000_vcorg[i].vcorg == 7 || itd1000_vcorg[i].vcorg == 15)) + itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg + 1) << 4)); + } else if (adcout < 2) { + if (!(itd1000_vcorg[i].vcorg == 1 || itd1000_vcorg[i].vcorg == 9)) + itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg - 1) << 4)); + } + break; + } + } +} + +static const struct { + u32 freq; + u8 values[10]; /* RFTR, RFST1 - RFST9 */ +} itd1000_fre_values[] = { + { 1075000, { 0x59, 0x1d, 0x1c, 0x17, 0x16, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1250000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1450000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1650000, { 0x69, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1750000, { 0x69, 0x1e, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1850000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, + { 1900000, { 0x69, 0x1d, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, + { 1950000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0d, 0x0b, 0x0a } }, + { 2050000, { 0x69, 0x1e, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0b, 0x0a } }, + { 2150000, { 0x69, 0x1d, 0x1c, 0x17, 0x15, 0x14, 0x13, 0x0f, 0x0e, 0x0b } } +}; + + +#define FREF 16 + +static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz) +{ + int i, j; + u32 plln, pllf; + u64 tmp; + + plln = (freq_khz * 1000) / 2 / FREF; + + /* Compute the factional part times 1000 */ + tmp = plln % 1000000; + plln /= 1000000; + + tmp *= 1048576; + do_div(tmp, 1000000); + pllf = (u32) tmp; + + state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF; + itd_dbg("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d\n", freq_khz, state->frequency, pllf, plln); + + itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */ + itd1000_write_reg(state, PLLNL, plln & 0xff); + itd1000_write_reg(state, PLLFH, (itd1000_read_reg(state, PLLFH) & 0xf0) | ((pllf >> 16) & 0x0f)); + itd1000_write_reg(state, PLLFM, (pllf >> 8) & 0xff); + itd1000_write_reg(state, PLLFL, (pllf >> 0) & 0xff); + + for (i = 0; i < ARRAY_SIZE(itd1000_fre_values); i++) { + if (freq_khz <= itd1000_fre_values[i].freq) { + itd_dbg("fre_values: %d\n", i); + itd1000_write_reg(state, RFTR, itd1000_fre_values[i].values[0]); + for (j = 0; j < 9; j++) + itd1000_write_reg(state, RFST1+j, itd1000_fre_values[i].values[j+1]); + break; + } + } + + itd1000_set_vco(state, freq_khz); +} + +static int itd1000_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct itd1000_state *state = fe->tuner_priv; + u8 pllcon1; + + itd1000_set_lo(state, c->frequency); + itd1000_set_lpf_bw(state, c->symbol_rate); + + pllcon1 = itd1000_read_reg(state, PLLCON1) & 0x7f; + itd1000_write_reg(state, PLLCON1, pllcon1 | (1 << 7)); + itd1000_write_reg(state, PLLCON1, pllcon1); + + return 0; +} + +static int itd1000_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct itd1000_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int itd1000_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + return 0; +} + +static u8 itd1000_init_tab[][2] = { + { PLLCON1, 0x65 }, /* Register does not change */ + { PLLNH, 0x80 }, /* Bits [7:6] do not change */ + { RESERVED_0X6D, 0x3b }, + { VCO_CHP2_I2C, 0x12 }, + { 0x72, 0xf9 }, /* No such regsister defined */ + { RESERVED_0X73, 0xff }, + { RESERVED_0X74, 0xb2 }, + { RESERVED_0X75, 0xc7 }, + { EXTGVBBRF, 0xf0 }, + { DIVAGCCK, 0x80 }, + { BBTR, 0xa0 }, + { RESERVED_0X7E, 0x4f }, + { 0x82, 0x88 }, /* No such regsister defined */ + { 0x83, 0x80 }, /* No such regsister defined */ + { 0x84, 0x80 }, /* No such regsister defined */ + { RESERVED_0X85, 0x74 }, + { RESERVED_0X86, 0xff }, + { RESERVED_0X88, 0x02 }, + { RESERVED_0X89, 0x16 }, + { RFST0, 0x1f }, + { RESERVED_0X94, 0x66 }, + { RESERVED_0X95, 0x66 }, + { RESERVED_0X96, 0x77 }, + { RESERVED_0X97, 0x99 }, + { RESERVED_0X98, 0xff }, + { RESERVED_0X99, 0xfc }, + { RESERVED_0X9A, 0xba }, + { RESERVED_0X9B, 0xaa }, +}; + +static u8 itd1000_reinit_tab[][2] = { + { VCO_CHP1_I2C, 0x8a }, + { BW, 0x87 }, + { GVBB_I2C, 0x03 }, + { BBGVMIN, 0x03 }, + { CON1, 0x2e }, +}; + + +static int itd1000_init(struct dvb_frontend *fe) +{ + struct itd1000_state *state = fe->tuner_priv; + int i; + + for (i = 0; i < ARRAY_SIZE(itd1000_init_tab); i++) + itd1000_write_reg(state, itd1000_init_tab[i][0], itd1000_init_tab[i][1]); + + for (i = 0; i < ARRAY_SIZE(itd1000_reinit_tab); i++) + itd1000_write_reg(state, itd1000_reinit_tab[i][0], itd1000_reinit_tab[i][1]); + + return 0; +} + +static int itd1000_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static void itd1000_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static const struct dvb_tuner_ops itd1000_tuner_ops = { + .info = { + .name = "Integrant ITD1000", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 125 * kHz, + }, + + .release = itd1000_release, + + .init = itd1000_init, + .sleep = itd1000_sleep, + + .set_params = itd1000_set_parameters, + .get_frequency = itd1000_get_frequency, + .get_bandwidth = itd1000_get_bandwidth +}; + + +struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) +{ + struct itd1000_state *state = NULL; + u8 i = 0; + + state = kzalloc(sizeof(struct itd1000_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->cfg = cfg; + state->i2c = i2c; + + i = itd1000_read_reg(state, 0); + if (i != 0) { + kfree(state); + return NULL; + } + itd_info("successfully identified (ID: %d)\n", i); + + memset(state->shadow, 0xff, sizeof(state->shadow)); + for (i = 0x65; i < 0x9c; i++) + state->shadow[i] = itd1000_read_reg(state, i); + + memcpy(&fe->ops.tuner_ops, &itd1000_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + + return fe; +} +EXPORT_SYMBOL_GPL(itd1000_attach); + +MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); +MODULE_DESCRIPTION("Integrant ITD1000 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h new file mode 100644 index 0000000000..3539c69ce2 --- /dev/null +++ b/drivers/media/dvb-frontends/itd1000.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" + * + * Copyright (c) 2007 Patrick Boettcher <pb@linuxtv.org> + */ + +#ifndef ITD1000_H +#define ITD1000_H + +struct dvb_frontend; +struct i2c_adapter; + +struct itd1000_config { + u8 i2c_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_TUNER_ITD1000) +extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg); +#else +static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/itd1000_priv.h b/drivers/media/dvb-frontends/itd1000_priv.h new file mode 100644 index 0000000000..f33157d2ec --- /dev/null +++ b/drivers/media/dvb-frontends/itd1000_priv.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" + * + * Copyright (c) 2007 Patrick Boettcher <pb@linuxtv.org> + */ + +#ifndef ITD1000_PRIV_H +#define ITD1000_PRIV_H + +struct itd1000_state { + struct itd1000_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; /* contains the value resulting from the LO-setting */ + + /* ugly workaround for flexcop's incapable i2c-controller + * FIXME, if possible + */ + u8 shadow[256]; +}; + +enum itd1000_register { + VCO_CHP1 = 0x65, + VCO_CHP2, + PLLCON1, + PLLNH, + PLLNL, + PLLFH, + PLLFM, + PLLFL, + RESERVED_0X6D, + PLLLOCK, + VCO_CHP2_I2C, + VCO_CHP1_I2C, + BW, + RESERVED_0X73 = 0x73, + RESERVED_0X74, + RESERVED_0X75, + GVBB, + GVRF, + GVBB_I2C, + EXTGVBBRF, + DIVAGCCK, + BBTR, + RFTR, + BBGVMIN, + RESERVED_0X7E, + RESERVED_0X85 = 0x85, + RESERVED_0X86, + CON1, + RESERVED_0X88, + RESERVED_0X89, + RFST0, + RFST1, + RFST2, + RFST3, + RFST4, + RFST5, + RFST6, + RFST7, + RFST8, + RFST9, + RESERVED_0X94, + RESERVED_0X95, + RESERVED_0X96, + RESERVED_0X97, + RESERVED_0X98, + RESERVED_0X99, + RESERVED_0X9A, + RESERVED_0X9B, +}; + +#endif diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c new file mode 100644 index 0000000000..3212e333d4 --- /dev/null +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner + * + * Copyright (C) 2010 Malcolm Priestley + */ + +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "ix2505v.h" + +static int ix2505v_debug; +#define dprintk(level, args...) do { \ + if (ix2505v_debug & level) \ + printk(KERN_DEBUG "ix2505v: " args); \ +} while (0) + +#define deb_info(args...) dprintk(0x01, args) +#define deb_i2c(args...) dprintk(0x02, args) + +struct ix2505v_state { + struct i2c_adapter *i2c; + const struct ix2505v_config *config; + u32 frequency; +}; + +/* + * Data read format of the Sharp IX2505V B0017 + * + * byte1: 1 | 1 | 0 | 0 | 0 | MA1 | MA0 | 1 + * byte2: POR | FL | RD2 | RD1 | RD0 | X | X | X + * + * byte1 = address + * byte2; + * POR = Power on Reset (VCC H=<2.2v L=>2.2v) + * FL = Phase Lock (H=lock L=unlock) + * RD0-2 = Reserved internal operations + * + * Only POR can be used to check the tuner is present + * + * Caution: after byte2 the I2C reverts to write mode continuing to read + * may corrupt tuning data. + * + */ + +static int ix2505v_read_status_reg(struct ix2505v_state *state) +{ + u8 addr = state->config->tuner_address; + u8 b2[] = {0}; + int ret; + + struct i2c_msg msg[1] = { + { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 1); + deb_i2c("Read %s ", __func__); + + return (ret == 1) ? (int) b2[0] : -1; +} + +static int ix2505v_write(struct ix2505v_state *state, u8 buf[], u8 count) +{ + struct i2c_msg msg[1] = { + { .addr = state->config->tuner_address, .flags = 0, + .buf = buf, .len = count }, + }; + + int ret; + + ret = i2c_transfer(state->i2c, msg, 1); + + if (ret != 1) { + deb_i2c("%s: i2c error, ret=%d\n", __func__, ret); + return -EIO; + } + + return 0; +} + +static void ix2505v_release(struct dvb_frontend *fe) +{ + struct ix2505v_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); + +} + +/* + * Data write format of the Sharp IX2505V B0017 + * + * byte1: 1 | 1 | 0 | 0 | 0 | 0(MA1)| 0(MA0)| 0 + * byte2: 0 | BG1 | BG2 | N8 | N7 | N6 | N5 | N4 + * byte3: N3 | N2 | N1 | A5 | A4 | A3 | A2 | A1 + * byte4: 1 | 1(C1) | 1(C0) | PD5 | PD4 | TM | 0(RTS)| 1(REF) + * byte5: BA2 | BA1 | BA0 | PSC | PD3 |PD2/TS2|DIV/TS1|PD0/TS0 + * + * byte1 = address + * + * Write order + * 1) byte1 -> byte2 -> byte3 -> byte4 -> byte5 + * 2) byte1 -> byte4 -> byte5 -> byte2 -> byte3 + * 3) byte1 -> byte2 -> byte3 -> byte4 + * 4) byte1 -> byte4 -> byte5 -> byte2 + * 5) byte1 -> byte2 -> byte3 + * 6) byte1 -> byte4 -> byte5 + * 7) byte1 -> byte2 + * 8) byte1 -> byte4 + * + * Recommended Setup + * 1 -> 8 -> 6 + */ + +static int ix2505v_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ix2505v_state *state = fe->tuner_priv; + u32 frequency = c->frequency; + u32 b_w = (c->symbol_rate * 27) / 32000; + u32 div_factor, N , A, x; + int ret = 0, len; + u8 gain, cc, ref, psc, local_osc, lpf; + u8 data[4] = {0}; + + if ((frequency < fe->ops.info.frequency_min_hz / kHz) + || (frequency > fe->ops.info.frequency_max_hz / kHz)) + return -EINVAL; + + if (state->config->tuner_gain) + gain = (state->config->tuner_gain < 4) + ? state->config->tuner_gain : 0; + else + gain = 0x0; + + if (state->config->tuner_chargepump) + cc = state->config->tuner_chargepump; + else + cc = 0x3; + + ref = 8; /* REF =1 */ + psc = 32; /* PSC = 0 */ + + div_factor = (frequency * ref) / 40; /* local osc = 4Mhz */ + x = div_factor / psc; + N = x/100; + A = ((x - (N * 100)) * psc) / 100; + + data[0] = ((gain & 0x3) << 5) | (N >> 3); + data[1] = (N << 5) | (A & 0x1f); + data[2] = 0x81 | ((cc & 0x3) << 5) ; /*PD5,PD4 & TM = 0|C1,C0|REF=1*/ + + deb_info("Frq=%d x=%d N=%d A=%d\n", frequency, x, N, A); + + if (frequency <= 1065000) + local_osc = (6 << 5) | 2; + else if (frequency <= 1170000) + local_osc = (7 << 5) | 2; + else if (frequency <= 1300000) + local_osc = (1 << 5); + else if (frequency <= 1445000) + local_osc = (2 << 5); + else if (frequency <= 1607000) + local_osc = (3 << 5); + else if (frequency <= 1778000) + local_osc = (4 << 5); + else if (frequency <= 1942000) + local_osc = (5 << 5); + else /*frequency up to 2150000*/ + local_osc = (6 << 5); + + data[3] = local_osc; /* all other bits set 0 */ + + if (b_w <= 10000) + lpf = 0xc; + else if (b_w <= 12000) + lpf = 0x2; + else if (b_w <= 14000) + lpf = 0xa; + else if (b_w <= 16000) + lpf = 0x6; + else if (b_w <= 18000) + lpf = 0xe; + else if (b_w <= 20000) + lpf = 0x1; + else if (b_w <= 22000) + lpf = 0x9; + else if (b_w <= 24000) + lpf = 0x5; + else if (b_w <= 26000) + lpf = 0xd; + else if (b_w <= 28000) + lpf = 0x3; + else + lpf = 0xb; + + deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); + deb_info("Data 0=[%4phN]\n", data); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + len = sizeof(data); + ret |= ix2505v_write(state, data, len); + + data[2] |= 0x4; /* set TM = 1 other bits same */ + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + len = 1; + ret |= ix2505v_write(state, &data[2], len); /* write byte 4 only */ + + msleep(10); + + data[2] |= ((lpf >> 2) & 0x3) << 3; /* lpf */ + data[3] |= (lpf & 0x3) << 2; + + deb_info("Data 2=[%x%x]\n", data[2], data[3]); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + len = 2; + ret |= ix2505v_write(state, &data[2], len); /* write byte 4 & 5 */ + + if (state->config->min_delay_ms) + msleep(state->config->min_delay_ms); + + state->frequency = frequency; + + return ret; +} + +static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ix2505v_state *state = fe->tuner_priv; + + *frequency = state->frequency; + + return 0; +} + +static const struct dvb_tuner_ops ix2505v_tuner_ops = { + .info = { + .name = "Sharp IX2505V (B0017)", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz + }, + .release = ix2505v_release, + .set_params = ix2505v_set_params, + .get_frequency = ix2505v_get_frequency, +}; + +struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, + const struct ix2505v_config *config, + struct i2c_adapter *i2c) +{ + struct ix2505v_state *state = NULL; + int ret; + + if (NULL == config) { + deb_i2c("%s: no config ", __func__); + goto error; + } + + state = kzalloc(sizeof(struct ix2505v_state), GFP_KERNEL); + if (NULL == state) + return NULL; + + state->config = config; + state->i2c = i2c; + + if (state->config->tuner_write_only) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = ix2505v_read_status_reg(state); + + if (ret & 0x80) { + deb_i2c("%s: No IX2505V found\n", __func__); + goto error; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + fe->tuner_priv = state; + + memcpy(&fe->ops.tuner_ops, &ix2505v_tuner_ops, + sizeof(struct dvb_tuner_ops)); + deb_i2c("%s: initialization (%s addr=0x%02x) ok\n", + __func__, fe->ops.tuner_ops.info.name, config->tuner_address); + + return fe; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(ix2505v_attach); + +module_param_named(debug, ix2505v_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +MODULE_DESCRIPTION("DVB IX2505V tuner driver"); +MODULE_AUTHOR("Malcolm Priestley"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h new file mode 100644 index 0000000000..1755691313 --- /dev/null +++ b/drivers/media/dvb-frontends/ix2505v.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner + * + * Copyright (C) 2010 Malcolm Priestley + */ + +#ifndef DVB_IX2505V_H +#define DVB_IX2505V_H + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +/** + * struct ix2505v_config - ix2505 attachment configuration + * + * @tuner_address: tuner address + * @tuner_gain: Baseband AMP gain control 0/1=0dB(default) 2=-2bB 3=-4dB + * @tuner_chargepump: Charge pump output +/- 0=120 1=260 2=555 3=1200(default) + * @min_delay_ms: delay after tune + * @tuner_write_only: disables reads + */ +struct ix2505v_config { + u8 tuner_address; + u8 tuner_gain; + u8 tuner_chargepump; + int min_delay_ms; + u8 tuner_write_only; + +}; + +#if IS_REACHABLE(CONFIG_DVB_IX2505V) +/** + * ix2505v_attach - Attach a ix2505v tuner to the supplied frontend structure. + * + * @fe: Frontend to attach to. + * @config: pointer to &struct ix2505v_config + * @i2c: pointer to &struct i2c_adapter. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, + const struct ix2505v_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, + const struct ix2505v_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* DVB_IX2505V_H */ diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c new file mode 100644 index 0000000000..fe5af2453d --- /dev/null +++ b/drivers/media/dvb-frontends/l64781.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + driver for LSI L64781 COFDM demodulator + + Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH + Marko Kohtala <marko.kohtala@luukku.com> + + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <media/dvb_frontend.h> +#include "l64781.h" + + +struct l64781_state { + struct i2c_adapter* i2c; + const struct l64781_config* config; + struct dvb_frontend frontend; + + /* private demodulator data */ + unsigned int first:1; +}; + +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "l64781: " args); \ + } while (0) + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + + +static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) + dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", + __func__, reg, ret); + + return (ret != 1) ? -1 : 0; +} + +static int l64781_readreg (struct l64781_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) return ret; + + return b1[0]; +} + +static void apply_tps (struct l64781_state* state) +{ + l64781_writereg (state, 0x2a, 0x00); + l64781_writereg (state, 0x2a, 0x01); + + /* This here is a little bit questionable because it enables + the automatic update of TPS registers. I think we'd need to + handle the IRQ from FE to update some other registers as + well, or at least implement some magic to tuning to correct + to the TPS received from transmission. */ + l64781_writereg (state, 0x2a, 0x02); +} + + +static void reset_afc (struct l64781_state* state) +{ + /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for + timing offset */ + l64781_writereg (state, 0x07, 0x9e); /* stall AFC */ + l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */ + l64781_writereg (state, 0x09, 0); + l64781_writereg (state, 0x0a, 0); + l64781_writereg (state, 0x07, 0x8e); + l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */ + l64781_writereg (state, 0x11, 0x80); /* stall TIM */ + l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */ + l64781_writereg (state, 0x12, 0); + l64781_writereg (state, 0x13, 0); + l64781_writereg (state, 0x11, 0x00); +} + +static int reset_and_configure (struct l64781_state* state) +{ + u8 buf [] = { 0x06 }; + struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; + // NOTE: this is correct in writing to address 0x00 + + return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV; +} + +static int apply_frontend_param(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct l64781_state* state = fe->demodulator_priv; + /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ + static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; + /* QPSK, QAM_16, QAM_64 */ + static const u8 qam_tab [] = { 2, 4, 0, 6 }; + static const u8 guard_tab [] = { 1, 2, 4, 8 }; + /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ + static const u32 ppm = 8000; + u32 ddfs_offset_fixed; +/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ +/* bw_tab[p->bandWidth]<<10)/15625; */ + u32 init_freq; + u32 spi_bias; + u8 val0x04; + u8 val0x05; + u8 val0x06; + int bw; + + switch (p->bandwidth_hz) { + case 8000000: + bw = 8; + break; + case 7000000: + bw = 7; + break; + case 6000000: + bw = 6; + break; + default: + return -EINVAL; + } + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + if (p->inversion != INVERSION_ON && + p->inversion != INVERSION_OFF) + return -EINVAL; + + if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && + p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && + p->code_rate_HP != FEC_7_8) + return -EINVAL; + + if (p->hierarchy != HIERARCHY_NONE && + (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && + p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && + p->code_rate_LP != FEC_7_8)) + return -EINVAL; + + if (p->modulation != QPSK && p->modulation != QAM_16 && + p->modulation != QAM_64) + return -EINVAL; + + if (p->transmission_mode != TRANSMISSION_MODE_2K && + p->transmission_mode != TRANSMISSION_MODE_8K) + return -EINVAL; + + if ((int)p->guard_interval < GUARD_INTERVAL_1_32 || + p->guard_interval > GUARD_INTERVAL_1_4) + return -EINVAL; + + if ((int)p->hierarchy < HIERARCHY_NONE || + p->hierarchy > HIERARCHY_4) + return -EINVAL; + + ddfs_offset_fixed = 0x4000-(ppm<<16)/bw/1000000; + + /* This works up to 20000 ppm, it overflows if too large ppm! */ + init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / + bw & 0xFFFFFF); + + /* SPI bias calculation is slightly modified to fit in 32bit */ + /* will work for high ppm only... */ + spi_bias = 378 * (1 << 10); + spi_bias *= 16; + spi_bias *= bw; + spi_bias *= qam_tab[p->modulation]; + spi_bias /= p->code_rate_HP + 1; + spi_bias /= (guard_tab[p->guard_interval] + 32); + spi_bias *= 1000; + spi_bias /= 1000 + ppm/1000; + spi_bias *= p->code_rate_HP; + + val0x04 = (p->transmission_mode << 2) | p->guard_interval; + val0x05 = fec_tab[p->code_rate_HP]; + + if (p->hierarchy != HIERARCHY_NONE) + val0x05 |= (p->code_rate_LP - FEC_1_2) << 3; + + val0x06 = (p->hierarchy << 2) | p->modulation; + + l64781_writereg (state, 0x04, val0x04); + l64781_writereg (state, 0x05, val0x05); + l64781_writereg (state, 0x06, val0x06); + + reset_afc (state); + + /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ + l64781_writereg (state, 0x15, + p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); + l64781_writereg (state, 0x16, init_freq & 0xff); + l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff); + l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff); + + l64781_writereg (state, 0x1b, spi_bias & 0xff); + l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff); + l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) | + (p->inversion == INVERSION_ON ? 0x80 : 0x00)); + + l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff); + l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); + + l64781_readreg (state, 0x00); /* clear interrupt registers... */ + l64781_readreg (state, 0x01); /* dto. */ + + apply_tps (state); + + return 0; +} + +static int get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct l64781_state* state = fe->demodulator_priv; + int tmp; + + + tmp = l64781_readreg(state, 0x04); + switch(tmp & 3) { + case 0: + p->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + p->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + p->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + p->guard_interval = GUARD_INTERVAL_1_4; + break; + } + switch((tmp >> 2) & 3) { + case 0: + p->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + p->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + printk(KERN_WARNING "Unexpected value for transmission_mode\n"); + } + + tmp = l64781_readreg(state, 0x05); + switch(tmp & 7) { + case 0: + p->code_rate_HP = FEC_1_2; + break; + case 1: + p->code_rate_HP = FEC_2_3; + break; + case 2: + p->code_rate_HP = FEC_3_4; + break; + case 3: + p->code_rate_HP = FEC_5_6; + break; + case 4: + p->code_rate_HP = FEC_7_8; + break; + default: + printk("Unexpected value for code_rate_HP\n"); + } + switch((tmp >> 3) & 7) { + case 0: + p->code_rate_LP = FEC_1_2; + break; + case 1: + p->code_rate_LP = FEC_2_3; + break; + case 2: + p->code_rate_LP = FEC_3_4; + break; + case 3: + p->code_rate_LP = FEC_5_6; + break; + case 4: + p->code_rate_LP = FEC_7_8; + break; + default: + printk("Unexpected value for code_rate_LP\n"); + } + + tmp = l64781_readreg(state, 0x06); + switch(tmp & 3) { + case 0: + p->modulation = QPSK; + break; + case 1: + p->modulation = QAM_16; + break; + case 2: + p->modulation = QAM_64; + break; + default: + printk(KERN_WARNING "Unexpected value for modulation\n"); + } + switch((tmp >> 2) & 7) { + case 0: + p->hierarchy = HIERARCHY_NONE; + break; + case 1: + p->hierarchy = HIERARCHY_1; + break; + case 2: + p->hierarchy = HIERARCHY_2; + break; + case 3: + p->hierarchy = HIERARCHY_4; + break; + default: + printk("Unexpected value for hierarchy\n"); + } + + + tmp = l64781_readreg (state, 0x1d); + p->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; + + tmp = (int) (l64781_readreg (state, 0x08) | + (l64781_readreg (state, 0x09) << 8) | + (l64781_readreg (state, 0x0a) << 16)); + p->frequency += tmp; + + return 0; +} + +static int l64781_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct l64781_state* state = fe->demodulator_priv; + int sync = l64781_readreg (state, 0x32); + int gain = l64781_readreg (state, 0x0e); + + l64781_readreg (state, 0x00); /* clear interrupt registers... */ + l64781_readreg (state, 0x01); /* dto. */ + + *status = 0; + + if (gain > 5) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x02) /* VCXO locked, this criteria should be ok */ + *status |= FE_HAS_CARRIER; + + if (sync & 0x20) + *status |= FE_HAS_VITERBI; + + if (sync & 0x40) + *status |= FE_HAS_SYNC; + + if (sync == 0x7f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int l64781_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct l64781_state* state = fe->demodulator_priv; + + /* XXX FIXME: set up counting period (reg 0x26...0x28) + */ + *ber = l64781_readreg (state, 0x39) + | (l64781_readreg (state, 0x3a) << 8); + + return 0; +} + +static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct l64781_state* state = fe->demodulator_priv; + + u8 gain = l64781_readreg (state, 0x0e); + *signal_strength = (gain << 8) | gain; + + return 0; +} + +static int l64781_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct l64781_state* state = fe->demodulator_priv; + + u8 avg_quality = 0xff - l64781_readreg (state, 0x33); + *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ + + return 0; +} + +static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct l64781_state* state = fe->demodulator_priv; + + *ucblocks = l64781_readreg (state, 0x37) + | (l64781_readreg (state, 0x38) << 8); + + return 0; +} + +static int l64781_sleep(struct dvb_frontend* fe) +{ + struct l64781_state* state = fe->demodulator_priv; + + /* Power down */ + return l64781_writereg (state, 0x3e, 0x5a); +} + +static int l64781_init(struct dvb_frontend* fe) +{ + struct l64781_state* state = fe->demodulator_priv; + + reset_and_configure (state); + + /* Power up */ + l64781_writereg (state, 0x3e, 0xa5); + + /* Reset hard */ + l64781_writereg (state, 0x2a, 0x04); + l64781_writereg (state, 0x2a, 0x00); + + /* Set tuner specific things */ + /* AFC_POL, set also in reset_afc */ + l64781_writereg (state, 0x07, 0x8e); + + /* Use internal ADC */ + l64781_writereg (state, 0x0b, 0x81); + + /* AGC loop gain, and polarity is positive */ + l64781_writereg (state, 0x0c, 0x84); + + /* Internal ADC outputs two's complement */ + l64781_writereg (state, 0x0d, 0x8c); + + /* With ppm=8000, it seems the DTR_SENSITIVITY will result in + value of 2 with all possible bandwidths and guard + intervals, which is the initial value anyway. */ + /*l64781_writereg (state, 0x19, 0x92);*/ + + /* Everything is two's complement, soft bit and CSI_OUT too */ + l64781_writereg (state, 0x1e, 0x09); + + /* delay a bit after first init attempt */ + if (state->first) { + state->first = 0; + msleep(200); + } + + return 0; +} + +static int l64781_get_tune_settings(struct dvb_frontend* fe, + struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 4000; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void l64781_release(struct dvb_frontend* fe) +{ + struct l64781_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops l64781_ops; + +struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c) +{ + struct l64781_state* state = NULL; + int reg0x3e = -1; + u8 b0 [] = { 0x1a }; + u8 b1 [] = { 0x00 }; + struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->first = 1; + + /* + * the L64781 won't show up before we send the reset_and_configure() + * broadcast. If nothing responds there is no L64781 on the bus... + */ + if (reset_and_configure(state) < 0) { + dprintk("No response to reset and configure broadcast...\n"); + goto error; + } + + /* The chip always responds to reads */ + if (i2c_transfer(state->i2c, msg, 2) != 2) { + dprintk("No response to read on I2C bus\n"); + goto error; + } + + /* Save current register contents for bailout */ + reg0x3e = l64781_readreg(state, 0x3e); + + /* Reading the POWER_DOWN register always returns 0 */ + if (reg0x3e != 0) { + dprintk("Device doesn't look like L64781\n"); + goto error; + } + + /* Turn the chip off */ + l64781_writereg (state, 0x3e, 0x5a); + + /* Responds to all reads with 0 */ + if (l64781_readreg(state, 0x1a) != 0) { + dprintk("Read 1 returned unexpected value\n"); + goto error; + } + + /* Turn the chip on */ + l64781_writereg (state, 0x3e, 0xa5); + + /* Responds with register default value */ + if (l64781_readreg(state, 0x1a) != 0xa1) { + dprintk("Read 2 returned unexpected value\n"); + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &l64781_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (reg0x3e >= 0) + l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */ + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops l64781_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "LSI L64781 DVB-T", + /* .frequency_min_hz = ???,*/ + /* .frequency_max_hz = ???,*/ + .frequency_stepsize_hz = 166666, + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_MUTE_TS + }, + + .release = l64781_release, + + .init = l64781_init, + .sleep = l64781_sleep, + + .set_frontend = apply_frontend_param, + .get_frontend = get_frontend, + .get_tune_settings = l64781_get_tune_settings, + + .read_status = l64781_read_status, + .read_ber = l64781_read_ber, + .read_signal_strength = l64781_read_signal_strength, + .read_snr = l64781_read_snr, + .read_ucblocks = l64781_read_ucblocks, +}; + +MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(l64781_attach); diff --git a/drivers/media/dvb-frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h new file mode 100644 index 0000000000..41d55f627f --- /dev/null +++ b/drivers/media/dvb-frontends/l64781.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + driver for LSI L64781 COFDM demodulator + + Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH + Marko Kohtala <marko.kohtala@luukku.com> + + +*/ + +#ifndef L64781_H +#define L64781_H + +#include <linux/dvb/frontend.h> + +struct l64781_config +{ + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_L64781) +extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_L64781 + +#endif // L64781_H diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c new file mode 100644 index 0000000000..fe700aa56b --- /dev/null +++ b/drivers/media/dvb-frontends/lg2160.c @@ -0,0 +1,1434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for LG2160 - ATSC/MH + * + * Copyright (C) 2010 Michael Krufky <mkrufky@linuxtv.org> + */ + +#include <linux/jiffies.h> +#include <linux/dvb/frontend.h> +#include "lg2160.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); + +#define DBG_INFO 1 +#define DBG_REG 2 + +#define lg_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt, __func__, ##arg) + +#define lg_info(fmt, arg...) printk(KERN_INFO "lg2160: " fmt, ##arg) +#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) +#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) +#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ + lg_printk(KERN_DEBUG, fmt, ##arg) +#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ + lg_printk(KERN_DEBUG, fmt, ##arg) + +#define lg_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + lg_err("error %d on line %d\n", ret, __LINE__); \ + __ret; \ +}) + +struct lg216x_state { + struct i2c_adapter *i2c_adap; + const struct lg2160_config *cfg; + + struct dvb_frontend frontend; + + u32 current_frequency; + u8 parade_id; + u8 fic_ver; + unsigned int last_reset; +}; + +/* ------------------------------------------------------------------------ */ + +static int lg216x_write_reg(struct lg216x_state *state, u16 reg, u8 val) +{ + int ret; + u8 buf[] = { reg >> 8, reg & 0xff, val }; + struct i2c_msg msg = { + .addr = state->cfg->i2c_addr, .flags = 0, + .buf = buf, .len = 3, + }; + + lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); + + ret = i2c_transfer(state->i2c_adap, &msg, 1); + + if (ret != 1) { + lg_err("error (addr %02x %02x <- %02x, err = %i)\n", + msg.buf[0], msg.buf[1], msg.buf[2], ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +static int lg216x_read_reg(struct lg216x_state *state, u16 reg, u8 *val) +{ + int ret; + u8 reg_buf[] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[] = { + { .addr = state->cfg->i2c_addr, + .flags = 0, .buf = reg_buf, .len = 2 }, + { .addr = state->cfg->i2c_addr, + .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + lg_reg("reg: 0x%04x\n", reg); + + ret = i2c_transfer(state->i2c_adap, msg, 2); + + if (ret != 2) { + lg_err("error (addr %02x reg %04x error (ret == %i)\n", + state->cfg->i2c_addr, reg, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +struct lg216x_reg { + u16 reg; + u8 val; +}; + +static int lg216x_write_regs(struct lg216x_state *state, + struct lg216x_reg *regs, int len) +{ + int i, ret; + + lg_reg("writing %d registers...\n", len); + + for (i = 0; i < len; i++) { + ret = lg216x_write_reg(state, regs[i].reg, regs[i].val); + if (lg_fail(ret)) + return ret; + } + return 0; +} + +static int lg216x_set_reg_bit(struct lg216x_state *state, + u16 reg, int bit, int onoff) +{ + u8 val; + int ret; + + lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); + + ret = lg216x_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~(1 << bit); + val |= (onoff & 1) << bit; + + ret = lg216x_write_reg(state, reg, val); + lg_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + if (state->cfg->deny_i2c_rptr) + return 0; + + lg_dbg("(%d)\n", enable); + + ret = lg216x_set_reg_bit(state, 0x0000, 0, enable ? 0 : 1); + + msleep(1); + + return ret; +} + +static int lg216x_soft_reset(struct lg216x_state *state) +{ + int ret; + + lg_dbg("\n"); + + ret = lg216x_write_reg(state, 0x0002, 0x00); + if (lg_fail(ret)) + goto fail; + + msleep(20); + ret = lg216x_write_reg(state, 0x0002, 0x01); + if (lg_fail(ret)) + goto fail; + + state->last_reset = jiffies_to_msecs(jiffies); +fail: + return ret; +} + +static int lg216x_initialize(struct lg216x_state *state) +{ + int ret; + + static struct lg216x_reg lg2160_init[] = { +#if 0 + { .reg = 0x0015, .val = 0xe6 }, +#else + { .reg = 0x0015, .val = 0xf7 }, + { .reg = 0x001b, .val = 0x52 }, + { .reg = 0x0208, .val = 0x00 }, + { .reg = 0x0209, .val = 0x82 }, + { .reg = 0x0210, .val = 0xf9 }, + { .reg = 0x020a, .val = 0x00 }, + { .reg = 0x020b, .val = 0x82 }, + { .reg = 0x020d, .val = 0x28 }, + { .reg = 0x020f, .val = 0x14 }, +#endif + }; + + static struct lg216x_reg lg2161_init[] = { + { .reg = 0x0000, .val = 0x41 }, + { .reg = 0x0001, .val = 0xfb }, + { .reg = 0x0216, .val = 0x00 }, + { .reg = 0x0219, .val = 0x00 }, + { .reg = 0x021b, .val = 0x55 }, + { .reg = 0x0606, .val = 0x0a }, + }; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_write_regs(state, + lg2160_init, ARRAY_SIZE(lg2160_init)); + break; + case LG2161: + ret = lg216x_write_regs(state, + lg2161_init, ARRAY_SIZE(lg2161_init)); + break; + default: + ret = -EINVAL; + break; + } + if (lg_fail(ret)) + goto fail; + + ret = lg216x_soft_reset(state); + lg_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_set_if(struct lg216x_state *state) +{ + u8 val; + int ret; + + lg_dbg("%d KHz\n", state->cfg->if_khz); + + ret = lg216x_read_reg(state, 0x0132, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfb; + val |= (0 == state->cfg->if_khz) ? 0x04 : 0x00; + + ret = lg216x_write_reg(state, 0x0132, val); + lg_fail(ret); + + /* if NOT zero IF, 6 MHz is the default */ +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_agc_fix(struct lg216x_state *state, + int if_agc_fix, int rf_agc_fix) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xf3; + val |= (if_agc_fix) ? 0x08 : 0x00; + val |= (rf_agc_fix) ? 0x04 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} + +#if 0 +static int lg2160_agc_freeze(struct lg216x_state *state, + int if_agc_freeze, int rf_agc_freeze) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xcf; + val |= (if_agc_freeze) ? 0x20 : 0x00; + val |= (rf_agc_freeze) ? 0x10 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} +#endif + +static int lg2160_agc_polarity(struct lg216x_state *state, + int if_agc_polarity, int rf_agc_polarity) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfc; + val |= (if_agc_polarity) ? 0x02 : 0x00; + val |= (rf_agc_polarity) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_tuner_pwr_save_polarity(struct lg216x_state *state, + int polarity) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0008, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfe; + val |= (polarity) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, 0x0008, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_spectrum_polarity(struct lg216x_state *state, + int inverted) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0132, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfd; + val |= (inverted) ? 0x02 : 0x00; + + ret = lg216x_write_reg(state, 0x0132, val); + lg_fail(ret); +fail: + return lg216x_soft_reset(state); +} + +static int lg2160_tuner_pwr_save(struct lg216x_state *state, int onoff) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0007, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xbf; + val |= (onoff) ? 0x40 : 0x00; + + ret = lg216x_write_reg(state, 0x0007, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_set_parade(struct lg216x_state *state, int id) +{ + int ret; + + ret = lg216x_write_reg(state, 0x013e, id & 0x7f); + if (lg_fail(ret)) + goto fail; + + state->parade_id = id & 0x7f; +fail: + return ret; +} + +static int lg216x_set_ensemble(struct lg216x_state *state, int id) +{ + int ret; + u16 reg; + u8 val; + + switch (state->cfg->lg_chip) { + case LG2160: + reg = 0x0400; + break; + case LG2161: + default: + reg = 0x0500; + break; + } + + ret = lg216x_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfe; + val |= (id) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, reg, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_set_spi_clock(struct lg216x_state *state) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0014, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xf3; + val |= (state->cfg->spi_clock << 2); + + ret = lg216x_write_reg(state, 0x0014, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2161_set_output_interface(struct lg216x_state *state) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0014, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~0x07; + val |= state->cfg->output_if; /* FIXME: needs sanity check */ + + ret = lg216x_write_reg(state, 0x0014, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_enable_fic(struct lg216x_state *state, int onoff) +{ + int ret; + + ret = lg216x_write_reg(state, 0x0017, 0x23); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_write_reg(state, 0x0016, 0xfc); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_write_reg(state, 0x0016, + 0xfc | ((onoff) ? 0x02 : 0x00)); + break; + case LG2161: + ret = lg216x_write_reg(state, 0x0016, (onoff) ? 0x10 : 0x00); + break; + } + if (lg_fail(ret)) + goto fail; + + ret = lg216x_initialize(state); + if (lg_fail(ret)) + goto fail; + + if (onoff) { + ret = lg216x_write_reg(state, 0x0017, 0x03); + lg_fail(ret); + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_fic_version(struct lg216x_state *state, u8 *ficver) +{ + u8 val; + int ret; + + *ficver = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0128, &val); + if (lg_fail(ret)) + goto fail; + + *ficver = (val >> 3) & 0x1f; +fail: + return ret; +} + +#if 0 +static int lg2160_get_parade_id(struct lg216x_state *state, u8 *id) +{ + u8 val; + int ret; + + *id = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0123, &val); + if (lg_fail(ret)) + goto fail; + + *id = val & 0x7f; +fail: + return ret; +} +#endif + +static int lg216x_get_nog(struct lg216x_state *state, u8 *nog) +{ + u8 val; + int ret; + + *nog = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0124, &val); + if (lg_fail(ret)) + goto fail; + + *nog = ((val >> 4) & 0x07) + 1; +fail: + return ret; +} + +static int lg216x_get_tnog(struct lg216x_state *state, u8 *tnog) +{ + u8 val; + int ret; + + *tnog = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0125, &val); + if (lg_fail(ret)) + goto fail; + + *tnog = val & 0x1f; +fail: + return ret; +} + +static int lg216x_get_sgn(struct lg216x_state *state, u8 *sgn) +{ + u8 val; + int ret; + + *sgn = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0124, &val); + if (lg_fail(ret)) + goto fail; + + *sgn = val & 0x0f; +fail: + return ret; +} + +static int lg216x_get_prc(struct lg216x_state *state, u8 *prc) +{ + u8 val; + int ret; + + *prc = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0125, &val); + if (lg_fail(ret)) + goto fail; + + *prc = ((val >> 5) & 0x07) + 1; +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_rs_frame_mode(struct lg216x_state *state, + enum atscmh_rs_frame_mode *rs_framemode) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0410, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0513, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch ((val >> 4) & 0x03) { +#if 1 + default: +#endif + case 0x00: + *rs_framemode = ATSCMH_RSFRAME_PRI_ONLY; + break; + case 0x01: + *rs_framemode = ATSCMH_RSFRAME_PRI_SEC; + break; +#if 0 + default: + *rs_framemode = ATSCMH_RSFRAME_RES; + break; +#endif + } +fail: + return ret; +} + +static +int lg216x_get_rs_frame_ensemble(struct lg216x_state *state, + enum atscmh_rs_frame_ensemble *rs_frame_ens) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0400, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0500, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + val &= 0x01; + *rs_frame_ens = (enum atscmh_rs_frame_ensemble) val; +fail: + return ret; +} + +static int lg216x_get_rs_code_mode(struct lg216x_state *state, + enum atscmh_rs_code_mode *rs_code_pri, + enum atscmh_rs_code_mode *rs_code_sec) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0410, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0513, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + *rs_code_pri = (enum atscmh_rs_code_mode) ((val >> 2) & 0x03); + *rs_code_sec = (enum atscmh_rs_code_mode) (val & 0x03); +fail: + return ret; +} + +static int lg216x_get_sccc_block_mode(struct lg216x_state *state, + enum atscmh_sccc_block_mode *sccc_block) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0315, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0511, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch (val & 0x03) { + case 0x00: + *sccc_block = ATSCMH_SCCC_BLK_SEP; + break; + case 0x01: + *sccc_block = ATSCMH_SCCC_BLK_COMB; + break; + default: + *sccc_block = ATSCMH_SCCC_BLK_RES; + break; + } +fail: + return ret; +} + +static int lg216x_get_sccc_code_mode(struct lg216x_state *state, + enum atscmh_sccc_code_mode *mode_a, + enum atscmh_sccc_code_mode *mode_b, + enum atscmh_sccc_code_mode *mode_c, + enum atscmh_sccc_code_mode *mode_d) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0316, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0512, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch ((val >> 6) & 0x03) { + case 0x00: + *mode_a = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_a = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_a = ATSCMH_SCCC_CODE_RES; + break; + } + + switch ((val >> 4) & 0x03) { + case 0x00: + *mode_b = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_b = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_b = ATSCMH_SCCC_CODE_RES; + break; + } + + switch ((val >> 2) & 0x03) { + case 0x00: + *mode_c = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_c = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_c = ATSCMH_SCCC_CODE_RES; + break; + } + + switch (val & 0x03) { + case 0x00: + *mode_d = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_d = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_d = ATSCMH_SCCC_CODE_RES; + break; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +static int lg216x_read_fic_err_count(struct lg216x_state *state, u8 *err) +{ + u8 fic_err; + int ret; + + *err = 0; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0012, &fic_err); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x001e, &fic_err); + break; + } + if (lg_fail(ret)) + goto fail; + + *err = fic_err; +fail: + return ret; +} + +static int lg2160_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + u8 crc_err1, crc_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0411, &crc_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0412, &crc_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)(((crc_err2 & 0x0f) << 8) | crc_err1); +fail: + return ret; +} + +static int lg2161_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + u8 crc_err; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0612, &crc_err); + if (lg_fail(ret)) + goto fail; + + *err = (u16)crc_err; +fail: + return ret; +} + +static int lg216x_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_crc_err_count(state, err); + break; + case LG2161: + ret = lg2161_read_crc_err_count(state, err); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int lg2160_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + u8 rs_err1, rs_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0413, &rs_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0414, &rs_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)(((rs_err2 & 0x0f) << 8) | rs_err1); +fail: + return ret; +} + +static int lg2161_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + u8 rs_err1, rs_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0613, &rs_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0614, &rs_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)((rs_err1 << 8) | rs_err2); +fail: + return ret; +} + +static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_rs_err_count(state, err); + break; + case LG2161: + ret = lg2161_read_rs_err_count(state, err); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("\n"); + + c->modulation = VSB_8; + c->frequency = state->current_frequency; + c->delivery_system = SYS_ATSCMH; + + ret = lg216x_get_fic_version(state, + &c->atscmh_fic_ver); + if (lg_fail(ret)) + goto fail; + if (state->fic_ver != c->atscmh_fic_ver) { + state->fic_ver = c->atscmh_fic_ver; + +#if 0 + ret = lg2160_get_parade_id(state, + &c->atscmh_parade_id); + if (lg_fail(ret)) + goto fail; +/* #else */ + c->atscmh_parade_id = state->parade_id; +#endif + ret = lg216x_get_nog(state, + &c->atscmh_nog); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_tnog(state, + &c->atscmh_tnog); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sgn(state, + &c->atscmh_sgn); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_prc(state, + &c->atscmh_prc); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_get_rs_frame_mode(state, + (enum atscmh_rs_frame_mode *) + &c->atscmh_rs_frame_mode); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_rs_frame_ensemble(state, + (enum atscmh_rs_frame_ensemble *) + &c->atscmh_rs_frame_ensemble); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_rs_code_mode(state, + (enum atscmh_rs_code_mode *) + &c->atscmh_rs_code_mode_pri, + (enum atscmh_rs_code_mode *) + &c->atscmh_rs_code_mode_sec); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sccc_block_mode(state, + (enum atscmh_sccc_block_mode *) + &c->atscmh_sccc_block_mode); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sccc_code_mode(state, + (enum atscmh_sccc_code_mode *) + &c->atscmh_sccc_code_mode_a, + (enum atscmh_sccc_code_mode *) + &c->atscmh_sccc_code_mode_b, + (enum atscmh_sccc_code_mode *) + &c->atscmh_sccc_code_mode_c, + (enum atscmh_sccc_code_mode *) + &c->atscmh_sccc_code_mode_d); + if (lg_fail(ret)) + goto fail; + } +#if 0 + ret = lg216x_read_fic_err_count(state, + (u8 *)&c->atscmh_fic_err); + if (lg_fail(ret)) + goto fail; + ret = lg216x_read_crc_err_count(state, + &c->atscmh_crc_err); + if (lg_fail(ret)) + goto fail; + ret = lg216x_read_rs_err_count(state, + &c->atscmh_rs_err); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + if (((c->atscmh_rs_err >= 240) && + (c->atscmh_crc_err >= 240)) && + ((jiffies_to_msecs(jiffies) - state->last_reset) > 6000)) + ret = lg216x_soft_reset(state); + break; + case LG2161: + /* no fix needed here (as far as we know) */ + ret = 0; + break; + } + lg_fail(ret); +#endif +fail: + return ret; +} + +static int lg2160_set_frontend(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + lg_dbg("(%d)\n", fe->dtv_property_cache.frequency); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = fe->dtv_property_cache.frequency; + } + + ret = lg2160_agc_fix(state, 0, 0); + if (lg_fail(ret)) + goto fail; + ret = lg2160_agc_polarity(state, 0, 0); + if (lg_fail(ret)) + goto fail; + ret = lg2160_tuner_pwr_save_polarity(state, 1); + if (lg_fail(ret)) + goto fail; + ret = lg216x_set_if(state); + if (lg_fail(ret)) + goto fail; + ret = lg2160_spectrum_polarity(state, state->cfg->spectral_inversion); + if (lg_fail(ret)) + goto fail; + + /* be tuned before this point */ + ret = lg216x_soft_reset(state); + if (lg_fail(ret)) + goto fail; + + ret = lg2160_tuner_pwr_save(state, 0); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_set_spi_clock(state); + if (lg_fail(ret)) + goto fail; + break; + case LG2161: + ret = lg2161_set_output_interface(state); + if (lg_fail(ret)) + goto fail; + break; + } + + ret = lg216x_set_parade(state, fe->dtv_property_cache.atscmh_parade_id); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_set_ensemble(state, + fe->dtv_property_cache.atscmh_rs_frame_ensemble); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_initialize(state); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_enable_fic(state, 1); + lg_fail(ret); + + lg216x_get_frontend(fe, c); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ + u8 val; + int ret; + + *acq_lock = 0; + *sync_lock = 0; + + ret = lg216x_read_reg(state, 0x011b, &val); + if (lg_fail(ret)) + goto fail; + + *sync_lock = (val & 0x20) ? 0 : 1; + *acq_lock = (val & 0x40) ? 0 : 1; +fail: + return ret; +} + +#ifdef USE_LG2161_LOCK_BITS +static int lg2161_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ + u8 val; + int ret; + + *acq_lock = 0; + *sync_lock = 0; + + ret = lg216x_read_reg(state, 0x0304, &val); + if (lg_fail(ret)) + goto fail; + + *sync_lock = (val & 0x80) ? 0 : 1; + + ret = lg216x_read_reg(state, 0x011b, &val); + if (lg_fail(ret)) + goto fail; + + *acq_lock = (val & 0x40) ? 0 : 1; +fail: + return ret; +} +#endif + +static int lg216x_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ +#ifdef USE_LG2161_LOCK_BITS + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_lock_status(state, acq_lock, sync_lock); + break; + case LG2161: + ret = lg2161_read_lock_status(state, acq_lock, sync_lock); + break; + default: + ret = -EINVAL; + break; + } + return ret; +#else + return lg2160_read_lock_status(state, acq_lock, sync_lock); +#endif +} + +static int lg216x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret, acq_lock, sync_lock; + + *status = 0; + + ret = lg216x_read_lock_status(state, &acq_lock, &sync_lock); + if (lg_fail(ret)) + goto fail; + + lg_dbg("%s%s\n", + acq_lock ? "SIGNALEXIST " : "", + sync_lock ? "SYNCLOCK" : ""); + + if (acq_lock) + *status |= FE_HAS_SIGNAL; + if (sync_lock) + *status |= FE_HAS_SYNC; + + if (*status) + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; + +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lg216x_state *state = fe->demodulator_priv; + u8 snr1, snr2; + int ret; + + *snr = 0; + + ret = lg216x_read_reg(state, 0x0202, &snr1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0203, &snr2); + if (lg_fail(ret)) + goto fail; + + if ((snr1 == 0xba) || (snr2 == 0xdf)) + *snr = 0; + else +#if 1 + *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 >> 4); +#else /* BCD */ + *snr = (snr2 | (snr1 << 8)); +#endif +fail: + return ret; +} + +static int lg2161_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lg216x_state *state = fe->demodulator_priv; + u8 snr1, snr2; + int ret; + + *snr = 0; + + ret = lg216x_read_reg(state, 0x0302, &snr1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0303, &snr2); + if (lg_fail(ret)) + goto fail; + + if ((snr1 == 0xba) || (snr2 == 0xfd)) + *snr = 0; + else + + *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 & 0x0f); +fail: + return ret; +} + +static int lg216x_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ +#if 0 + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + struct lg216x_state *state = fe->demodulator_priv; + u16 snr; + int ret; +#endif + *strength = 0; +#if 0 + ret = fe->ops.read_snr(fe, &snr); + if (lg_fail(ret)) + goto fail; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; +fail: + return ret; +#else + return 0; +#endif +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ +#if 0 + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + ret = lg216x_read_rs_err_count(state, + &fe->dtv_property_cache.atscmh_rs_err); + if (lg_fail(ret)) + goto fail; + + *ucblocks = fe->dtv_property_cache.atscmh_rs_err; +fail: +#else + *ucblocks = 0; +#endif + return 0; +} + +static int lg216x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 500; + lg_dbg("\n"); + return 0; +} + +static void lg216x_release(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + lg_dbg("\n"); + kfree(state); +} + +static const struct dvb_frontend_ops lg2160_ops = { + .delsys = { SYS_ATSCMH }, + .info = { + .name = "LG Electronics LG2160 ATSC/MH Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + }, + .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, +#if 0 + .init = lg216x_init, + .sleep = lg216x_sleep, +#endif + .set_frontend = lg2160_set_frontend, + .get_frontend = lg216x_get_frontend, + .get_tune_settings = lg216x_get_tune_settings, + .read_status = lg216x_read_status, +#if 0 + .read_ber = lg216x_read_ber, +#endif + .read_signal_strength = lg216x_read_signal_strength, + .read_snr = lg2160_read_snr, + .read_ucblocks = lg216x_read_ucblocks, + .release = lg216x_release, +}; + +static const struct dvb_frontend_ops lg2161_ops = { + .delsys = { SYS_ATSCMH }, + .info = { + .name = "LG Electronics LG2161 ATSC/MH Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + }, + .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, +#if 0 + .init = lg216x_init, + .sleep = lg216x_sleep, +#endif + .set_frontend = lg2160_set_frontend, + .get_frontend = lg216x_get_frontend, + .get_tune_settings = lg216x_get_tune_settings, + .read_status = lg216x_read_status, +#if 0 + .read_ber = lg216x_read_ber, +#endif + .read_signal_strength = lg216x_read_signal_strength, + .read_snr = lg2161_read_snr, + .read_ucblocks = lg216x_read_ucblocks, + .release = lg216x_release, +}; + +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap) +{ + struct lg216x_state *state = NULL; + + lg_dbg("(%d-%04x)\n", + i2c_adap ? i2c_adapter_id(i2c_adap) : 0, + config ? config->i2c_addr : 0); + + state = kzalloc(sizeof(struct lg216x_state), GFP_KERNEL); + if (!state) + return NULL; + + state->cfg = config; + state->i2c_adap = i2c_adap; + state->fic_ver = 0xff; + state->parade_id = 0xff; + + switch (config->lg_chip) { + default: + lg_warn("invalid chip requested, defaulting to LG2160"); + fallthrough; + case LG2160: + memcpy(&state->frontend.ops, &lg2160_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LG2161: + memcpy(&state->frontend.ops, &lg2161_ops, + sizeof(struct dvb_frontend_ops)); + break; + } + + state->frontend.demodulator_priv = state; + state->current_frequency = -1; + /* parade 1 by default */ + state->frontend.dtv_property_cache.atscmh_parade_id = 1; + + return &state->frontend; +} +EXPORT_SYMBOL_GPL(lg2160_attach); + +MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.3"); diff --git a/drivers/media/dvb-frontends/lg2160.h b/drivers/media/dvb-frontends/lg2160.h new file mode 100644 index 0000000000..17ddf68226 --- /dev/null +++ b/drivers/media/dvb-frontends/lg2160.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for LG2160 - ATSC/MH + * + * Copyright (C) 2010 Michael Krufky <mkrufky@linuxtv.org> + */ + +#ifndef _LG2160_H_ +#define _LG2160_H_ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +enum lg_chip_type { + LG2160 = 0, + LG2161 = 1, +}; + +#define LG2161_1019 LG2161 +#define LG2161_1040 LG2161 + +enum lg2160_spi_clock { + LG2160_SPI_3_125_MHZ = 0, + LG2160_SPI_6_25_MHZ = 1, + LG2160_SPI_12_5_MHZ = 2, +}; + +#if 0 +enum lg2161_oif { + LG2161_OIF_EBI2_SLA = 1, + LG2161_OIF_SDIO_SLA = 2, + LG2161_OIF_SPI_SLA = 3, + LG2161_OIF_SPI_MAS = 4, + LG2161_OIF_SERIAL_TS = 7, +}; +#endif + +struct lg2160_config { + u8 i2c_addr; + + /* user defined IF frequency in KHz */ + u16 if_khz; + + /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ + unsigned int deny_i2c_rptr:1; + + /* spectral inversion - 0:disabled 1:enabled */ + unsigned int spectral_inversion:1; + + unsigned int output_if; + enum lg2160_spi_clock spi_clock; + enum lg_chip_type lg_chip; +}; + +#if IS_REACHABLE(CONFIG_DVB_LG2160) +extern +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap); +#else +static inline +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LG2160 */ + +#endif /* _LG2160_H_ */ diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c new file mode 100644 index 0000000000..bdc8311e1c --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -0,0 +1,1202 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM + * + * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org> + * + * LGDT3304 support by Jarod Wilson <jarod@redhat.com> + */ + +#include <asm/div64.h> +#include <linux/dvb/frontend.h> +#include <linux/slab.h> +#include <linux/int_log.h> +#include "lgdt3305.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); + +#define DBG_INFO 1 +#define DBG_REG 2 + +#define lg_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt, __func__, ##arg) + +#define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg) +#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) +#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) +#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ + lg_printk(KERN_DEBUG, fmt, ##arg) +#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ + lg_printk(KERN_DEBUG, fmt, ##arg) + +#define lg_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + lg_err("error %d on line %d\n", ret, __LINE__); \ + __ret; \ +}) + +struct lgdt3305_state { + struct i2c_adapter *i2c_adap; + const struct lgdt3305_config *cfg; + + struct dvb_frontend frontend; + + enum fe_modulation current_modulation; + u32 current_frequency; + u32 snr; +}; + +/* ------------------------------------------------------------------------ */ + +/* FIXME: verify & document the LGDT3304 registers */ + +#define LGDT3305_GEN_CTRL_1 0x0000 +#define LGDT3305_GEN_CTRL_2 0x0001 +#define LGDT3305_GEN_CTRL_3 0x0002 +#define LGDT3305_GEN_STATUS 0x0003 +#define LGDT3305_GEN_CONTROL 0x0007 +#define LGDT3305_GEN_CTRL_4 0x000a +#define LGDT3305_DGTL_AGC_REF_1 0x0012 +#define LGDT3305_DGTL_AGC_REF_2 0x0013 +#define LGDT3305_CR_CTR_FREQ_1 0x0106 +#define LGDT3305_CR_CTR_FREQ_2 0x0107 +#define LGDT3305_CR_CTR_FREQ_3 0x0108 +#define LGDT3305_CR_CTR_FREQ_4 0x0109 +#define LGDT3305_CR_MSE_1 0x011b +#define LGDT3305_CR_MSE_2 0x011c +#define LGDT3305_CR_LOCK_STATUS 0x011d +#define LGDT3305_CR_CTRL_7 0x0126 +#define LGDT3305_AGC_POWER_REF_1 0x0300 +#define LGDT3305_AGC_POWER_REF_2 0x0301 +#define LGDT3305_AGC_DELAY_PT_1 0x0302 +#define LGDT3305_AGC_DELAY_PT_2 0x0303 +#define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306 +#define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307 +#define LGDT3305_IFBW_1 0x0308 +#define LGDT3305_IFBW_2 0x0309 +#define LGDT3305_AGC_CTRL_1 0x030c +#define LGDT3305_AGC_CTRL_4 0x0314 +#define LGDT3305_EQ_MSE_1 0x0413 +#define LGDT3305_EQ_MSE_2 0x0414 +#define LGDT3305_EQ_MSE_3 0x0415 +#define LGDT3305_PT_MSE_1 0x0417 +#define LGDT3305_PT_MSE_2 0x0418 +#define LGDT3305_PT_MSE_3 0x0419 +#define LGDT3305_FEC_BLOCK_CTRL 0x0504 +#define LGDT3305_FEC_LOCK_STATUS 0x050a +#define LGDT3305_FEC_PKT_ERR_1 0x050c +#define LGDT3305_FEC_PKT_ERR_2 0x050d +#define LGDT3305_TP_CTRL_1 0x050e +#define LGDT3305_BERT_PERIOD 0x0801 +#define LGDT3305_BERT_ERROR_COUNT_1 0x080a +#define LGDT3305_BERT_ERROR_COUNT_2 0x080b +#define LGDT3305_BERT_ERROR_COUNT_3 0x080c +#define LGDT3305_BERT_ERROR_COUNT_4 0x080d + +static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val) +{ + int ret; + u8 buf[] = { reg >> 8, reg & 0xff, val }; + struct i2c_msg msg = { + .addr = state->cfg->i2c_addr, .flags = 0, + .buf = buf, .len = 3, + }; + + lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); + + ret = i2c_transfer(state->i2c_adap, &msg, 1); + + if (ret != 1) { + lg_err("error (addr %02x %02x <- %02x, err = %i)\n", + msg.buf[0], msg.buf[1], msg.buf[2], ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val) +{ + int ret; + u8 reg_buf[] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[] = { + { .addr = state->cfg->i2c_addr, + .flags = 0, .buf = reg_buf, .len = 2 }, + { .addr = state->cfg->i2c_addr, + .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + lg_reg("reg: 0x%04x\n", reg); + + ret = i2c_transfer(state->i2c_adap, msg, 2); + + if (ret != 2) { + lg_err("error (addr %02x reg %04x error (ret == %i)\n", + state->cfg->i2c_addr, reg, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +#define read_reg(state, reg) \ +({ \ + u8 __val; \ + int ret = lgdt3305_read_reg(state, reg, &__val); \ + if (lg_fail(ret)) \ + __val = 0; \ + __val; \ +}) + +static int lgdt3305_set_reg_bit(struct lgdt3305_state *state, + u16 reg, int bit, int onoff) +{ + u8 val; + int ret; + + lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); + + ret = lgdt3305_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~(1 << bit); + val |= (onoff & 1) << bit; + + ret = lgdt3305_write_reg(state, reg, val); +fail: + return ret; +} + +struct lgdt3305_reg { + u16 reg; + u8 val; +}; + +static int lgdt3305_write_regs(struct lgdt3305_state *state, + struct lgdt3305_reg *regs, int len) +{ + int i, ret; + + lg_reg("writing %d registers...\n", len); + + for (i = 0; i < len - 1; i++) { + ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val); + if (lg_fail(ret)) + return ret; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3305_soft_reset(struct lgdt3305_state *state) +{ + int ret; + + lg_dbg("\n"); + + ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0); + if (lg_fail(ret)) + goto fail; + + msleep(20); + ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1); +fail: + return ret; +} + +static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, + enum lgdt3305_mpeg_mode mode) +{ + lg_dbg("(%d)\n", mode); + return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); +} + +static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state) +{ + u8 val; + int ret; + enum lgdt3305_tp_clock_edge edge = state->cfg->tpclk_edge; + enum lgdt3305_tp_clock_mode mode = state->cfg->tpclk_mode; + enum lgdt3305_tp_valid_polarity valid = state->cfg->tpvalid_polarity; + + lg_dbg("edge = %d, valid = %d\n", edge, valid); + + ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~0x09; + + if (edge) + val |= 0x08; + if (mode) + val |= 0x40; + if (valid) + val |= 0x01; + + ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_soft_reset(state); +fail: + return ret; +} + +static int lgdt3305_set_modulation(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + u8 opermode; + int ret; + + lg_dbg("\n"); + + ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode); + if (lg_fail(ret)) + goto fail; + + opermode &= ~0x03; + + switch (p->modulation) { + case VSB_8: + opermode |= 0x03; + break; + case QAM_64: + opermode |= 0x00; + break; + case QAM_256: + opermode |= 0x01; + break; + default: + return -EINVAL; + } + ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode); +fail: + return ret; +} + +static int lgdt3305_set_filter_extension(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + int val; + + switch (p->modulation) { + case VSB_8: + val = 0; + break; + case QAM_64: + case QAM_256: + val = 1; + break; + default: + return -EINVAL; + } + lg_dbg("val = %d\n", val); + + return lgdt3305_set_reg_bit(state, 0x043f, 2, val); +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3305_passband_digital_agc(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + u16 agc_ref; + + switch (p->modulation) { + case VSB_8: + agc_ref = 0x32c4; + break; + case QAM_64: + agc_ref = 0x2a00; + break; + case QAM_256: + agc_ref = 0x2a80; + break; + default: + return -EINVAL; + } + + lg_dbg("agc ref: 0x%04x\n", agc_ref); + + lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8); + lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff); + + return 0; +} + +static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + u16 ifbw, rfbw, agcdelay; + + switch (p->modulation) { + case VSB_8: + agcdelay = 0x04c0; + rfbw = 0x8000; + ifbw = 0x8000; + break; + case QAM_64: + case QAM_256: + agcdelay = 0x046b; + rfbw = 0x8889; + /* FIXME: investigate optimal ifbw & rfbw values for the + * DT3304 and re-write this switch..case block */ + if (state->cfg->demod_chip == LGDT3304) + ifbw = 0x6666; + else /* (state->cfg->demod_chip == LGDT3305) */ + ifbw = 0x8888; + break; + default: + return -EINVAL; + } + + if (state->cfg->rf_agc_loop) { + lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw); + + /* rf agc loop filter bandwidth */ + lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1, + agcdelay >> 8); + lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2, + agcdelay & 0xff); + + lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1, + rfbw >> 8); + lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2, + rfbw & 0xff); + } else { + lg_dbg("ifbw: 0x%04x\n", ifbw); + + /* if agc loop filter bandwidth */ + lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8); + lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff); + } + + return 0; +} + +static int lgdt3305_agc_setup(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + int lockdten, acqen; + + switch (p->modulation) { + case VSB_8: + lockdten = 0; + acqen = 0; + break; + case QAM_64: + case QAM_256: + lockdten = 1; + acqen = 1; + break; + default: + return -EINVAL; + } + + lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); + + /* control agc function */ + switch (state->cfg->demod_chip) { + case LGDT3304: + lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); + lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); + break; + case LGDT3305: + lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); + lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); + break; + default: + return -EINVAL; + } + + return lgdt3305_rfagc_loop(state, p); +} + +static int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + u16 usref = 0; + + switch (p->modulation) { + case VSB_8: + if (state->cfg->usref_8vsb) + usref = state->cfg->usref_8vsb; + break; + case QAM_64: + if (state->cfg->usref_qam64) + usref = state->cfg->usref_qam64; + break; + case QAM_256: + if (state->cfg->usref_qam256) + usref = state->cfg->usref_qam256; + break; + default: + return -EINVAL; + } + + if (usref) { + lg_dbg("set manual mode: 0x%04x\n", usref); + + lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1); + + lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1, + 0xff & (usref >> 8)); + lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2, + 0xff & (usref >> 0)); + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3305_spectral_inversion(struct lgdt3305_state *state, + struct dtv_frontend_properties *p, + int inversion) +{ + int ret; + + lg_dbg("(%d)\n", inversion); + + switch (p->modulation) { + case VSB_8: + ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7, + inversion ? 0xf9 : 0x79); + break; + case QAM_64: + case QAM_256: + ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL, + inversion ? 0xfd : 0xff); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int lgdt3305_set_if(struct lgdt3305_state *state, + struct dtv_frontend_properties *p) +{ + u16 if_freq_khz; + u8 nco1, nco2, nco3, nco4; + u64 nco; + + switch (p->modulation) { + case VSB_8: + if_freq_khz = state->cfg->vsb_if_khz; + break; + case QAM_64: + case QAM_256: + if_freq_khz = state->cfg->qam_if_khz; + break; + default: + return -EINVAL; + } + + nco = if_freq_khz / 10; + + switch (p->modulation) { + case VSB_8: + nco <<= 24; + do_div(nco, 625); + break; + case QAM_64: + case QAM_256: + nco <<= 28; + do_div(nco, 625); + break; + default: + return -EINVAL; + } + + nco1 = (nco >> 24) & 0x3f; + nco1 |= 0x40; + nco2 = (nco >> 16) & 0xff; + nco3 = (nco >> 8) & 0xff; + nco4 = nco & 0xff; + + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4); + + lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n", + if_freq_khz, nco1, nco2, nco3, nco4); + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + + if (state->cfg->deny_i2c_rptr) + return 0; + + lg_dbg("(%d)\n", enable); + + return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5, + enable ? 0 : 1); +} + +static int lgdt3305_sleep(struct dvb_frontend *fe) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + u8 gen_ctrl_3, gen_ctrl_4; + + lg_dbg("\n"); + + gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3); + gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4); + + /* hold in software reset while sleeping */ + gen_ctrl_3 &= ~0x01; + /* tristate the IF-AGC pin */ + gen_ctrl_3 |= 0x02; + /* tristate the RF-AGC pin */ + gen_ctrl_3 |= 0x04; + + /* disable vsb/qam module */ + gen_ctrl_4 &= ~0x01; + /* disable adc module */ + gen_ctrl_4 &= ~0x02; + + lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3); + lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4); + + return 0; +} + +static int lgdt3305_init(struct dvb_frontend *fe) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + int ret; + + static struct lgdt3305_reg lgdt3304_init_data[] = { + { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, + { .reg = 0x000d, .val = 0x02, }, + { .reg = 0x000e, .val = 0x02, }, + { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, + { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, + { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, + { .reg = 0x0112, .val = 0x17, }, + { .reg = 0x0113, .val = 0x15, }, + { .reg = 0x0114, .val = 0x18, }, + { .reg = 0x0115, .val = 0xff, }, + { .reg = 0x0116, .val = 0x3c, }, + { .reg = 0x0214, .val = 0x67, }, + { .reg = 0x0424, .val = 0x8d, }, + { .reg = 0x0427, .val = 0x12, }, + { .reg = 0x0428, .val = 0x4f, }, + { .reg = LGDT3305_IFBW_1, .val = 0x80, }, + { .reg = LGDT3305_IFBW_2, .val = 0x00, }, + { .reg = 0x030a, .val = 0x08, }, + { .reg = 0x030b, .val = 0x9b, }, + { .reg = 0x030d, .val = 0x00, }, + { .reg = 0x030e, .val = 0x1c, }, + { .reg = 0x0314, .val = 0xe1, }, + { .reg = 0x000d, .val = 0x82, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, + }; + + static struct lgdt3305_reg lgdt3305_init_data[] = { + { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, + { .reg = LGDT3305_GEN_CTRL_2, .val = 0xb0, }, + { .reg = LGDT3305_GEN_CTRL_3, .val = 0x01, }, + { .reg = LGDT3305_GEN_CONTROL, .val = 0x6f, }, + { .reg = LGDT3305_GEN_CTRL_4, .val = 0x03, }, + { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, + { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, + { .reg = LGDT3305_CR_CTRL_7, .val = 0x79, }, + { .reg = LGDT3305_AGC_POWER_REF_1, .val = 0x32, }, + { .reg = LGDT3305_AGC_POWER_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_AGC_DELAY_PT_1, .val = 0x0d, }, + { .reg = LGDT3305_AGC_DELAY_PT_2, .val = 0x30, }, + { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, .val = 0x80, }, + { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, .val = 0x00, }, + { .reg = LGDT3305_IFBW_1, .val = 0x80, }, + { .reg = LGDT3305_IFBW_2, .val = 0x00, }, + { .reg = LGDT3305_AGC_CTRL_1, .val = 0x30, }, + { .reg = LGDT3305_AGC_CTRL_4, .val = 0x61, }, + { .reg = LGDT3305_FEC_BLOCK_CTRL, .val = 0xff, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x1b, }, + }; + + lg_dbg("\n"); + + switch (state->cfg->demod_chip) { + case LGDT3304: + ret = lgdt3305_write_regs(state, lgdt3304_init_data, + ARRAY_SIZE(lgdt3304_init_data)); + break; + case LGDT3305: + ret = lgdt3305_write_regs(state, lgdt3305_init_data, + ARRAY_SIZE(lgdt3305_init_data)); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_soft_reset(state); +fail: + return ret; +} + +static int lgdt3304_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct lgdt3305_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("(%d, %d)\n", p->frequency, p->modulation); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = p->frequency; + } + + ret = lgdt3305_set_modulation(state, p); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_passband_digital_agc(state, p); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_agc_setup(state, p); + if (lg_fail(ret)) + goto fail; + + /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ + switch (p->modulation) { + case VSB_8: + lgdt3305_write_reg(state, 0x030d, 0x00); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); + break; + case QAM_64: + case QAM_256: + lgdt3305_write_reg(state, 0x030d, 0x14); + ret = lgdt3305_set_if(state, p); + if (lg_fail(ret)) + goto fail; + break; + default: + return -EINVAL; + } + + + ret = lgdt3305_spectral_inversion(state, p, + state->cfg->spectral_inversion + ? 1 : 0); + if (lg_fail(ret)) + goto fail; + + state->current_modulation = p->modulation; + + ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); + if (lg_fail(ret)) + goto fail; + + /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ + ret = lgdt3305_mpeg_mode_polarity(state); +fail: + return ret; +} + +static int lgdt3305_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct lgdt3305_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("(%d, %d)\n", p->frequency, p->modulation); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = p->frequency; + } + + ret = lgdt3305_set_modulation(state, p); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_passband_digital_agc(state, p); + if (lg_fail(ret)) + goto fail; + ret = lgdt3305_set_agc_power_ref(state, p); + if (lg_fail(ret)) + goto fail; + ret = lgdt3305_agc_setup(state, p); + if (lg_fail(ret)) + goto fail; + + /* low if */ + ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f); + if (lg_fail(ret)) + goto fail; + ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_set_if(state, p); + if (lg_fail(ret)) + goto fail; + ret = lgdt3305_spectral_inversion(state, p, + state->cfg->spectral_inversion + ? 1 : 0); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_set_filter_extension(state, p); + if (lg_fail(ret)) + goto fail; + + state->current_modulation = p->modulation; + + ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); + if (lg_fail(ret)) + goto fail; + + /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ + ret = lgdt3305_mpeg_mode_polarity(state); +fail: + return ret; +} + +static int lgdt3305_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + + lg_dbg("\n"); + + p->modulation = state->current_modulation; + p->frequency = state->current_frequency; + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state, + int *locked) +{ + u8 val; + int ret; + char *cr_lock_state = ""; + + *locked = 0; + + ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val); + if (lg_fail(ret)) + goto fail; + + switch (state->current_modulation) { + case QAM_256: + case QAM_64: + if (val & (1 << 1)) + *locked = 1; + + switch (val & 0x07) { + case 0: + cr_lock_state = "QAM UNLOCK"; + break; + case 4: + cr_lock_state = "QAM 1stLock"; + break; + case 6: + cr_lock_state = "QAM 2ndLock"; + break; + case 7: + cr_lock_state = "QAM FinalLock"; + break; + default: + cr_lock_state = "CLOCKQAM-INVALID!"; + break; + } + break; + case VSB_8: + if (val & (1 << 7)) { + *locked = 1; + cr_lock_state = "CLOCKVSB"; + } + break; + default: + ret = -EINVAL; + } + lg_dbg("(%d) %s\n", *locked, cr_lock_state); +fail: + return ret; +} + +static int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state, + int *locked) +{ + u8 val; + int ret, mpeg_lock, fec_lock, viterbi_lock; + + *locked = 0; + + switch (state->current_modulation) { + case QAM_256: + case QAM_64: + ret = lgdt3305_read_reg(state, + LGDT3305_FEC_LOCK_STATUS, &val); + if (lg_fail(ret)) + goto fail; + + mpeg_lock = (val & (1 << 0)) ? 1 : 0; + fec_lock = (val & (1 << 2)) ? 1 : 0; + viterbi_lock = (val & (1 << 3)) ? 1 : 0; + + *locked = mpeg_lock && fec_lock && viterbi_lock; + + lg_dbg("(%d) %s%s%s\n", *locked, + mpeg_lock ? "mpeg lock " : "", + fec_lock ? "fec lock " : "", + viterbi_lock ? "viterbi lock" : ""); + break; + case VSB_8: + default: + ret = -EINVAL; + } +fail: + return ret; +} + +static int lgdt3305_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + u8 val; + int ret, signal, inlock, nofecerr, snrgood, + cr_lock, fec_lock, sync_lock; + + *status = 0; + + ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val); + if (lg_fail(ret)) + goto fail; + + signal = (val & (1 << 4)) ? 1 : 0; + inlock = (val & (1 << 3)) ? 0 : 1; + sync_lock = (val & (1 << 2)) ? 1 : 0; + nofecerr = (val & (1 << 1)) ? 1 : 0; + snrgood = (val & (1 << 0)) ? 1 : 0; + + lg_dbg("%s%s%s%s%s\n", + signal ? "SIGNALEXIST " : "", + inlock ? "INLOCK " : "", + sync_lock ? "SYNCLOCK " : "", + nofecerr ? "NOFECERR " : "", + snrgood ? "SNRGOOD " : ""); + + ret = lgdt3305_read_cr_lock_status(state, &cr_lock); + if (lg_fail(ret)) + goto fail; + + if (signal) + *status |= FE_HAS_SIGNAL; + if (cr_lock) + *status |= FE_HAS_CARRIER; + if (nofecerr) + *status |= FE_HAS_VITERBI; + if (sync_lock) + *status |= FE_HAS_SYNC; + + switch (state->current_modulation) { + case QAM_256: + case QAM_64: + /* signal bit is unreliable on the DT3304 in QAM mode */ + if (((LGDT3304 == state->cfg->demod_chip)) && (cr_lock)) + *status |= FE_HAS_SIGNAL; + + ret = lgdt3305_read_fec_lock_status(state, &fec_lock); + if (lg_fail(ret)) + goto fail; + + if (fec_lock) + *status |= FE_HAS_LOCK; + break; + case VSB_8: + if (inlock) + *status |= FE_HAS_LOCK; + break; + default: + ret = -EINVAL; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +/* borrowed from lgdt330x.c */ +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* no signal */ + return 0; + + mse = intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); +} + +static int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ + + switch (state->current_modulation) { + case VSB_8: +#ifdef USE_PTMSE + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ + noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) | + (read_reg(state, LGDT3305_PT_MSE_2) << 8) | + (read_reg(state, LGDT3305_PT_MSE_3) & 0xff); + c = 73957994; /* log10(25*32^2)*2^24 */ +#else + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -16.12 to +44.08 */ + noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) | + (read_reg(state, LGDT3305_EQ_MSE_2) << 8) | + (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff); + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) | + (read_reg(state, LGDT3305_CR_MSE_2) & 0xff); + + c = (state->current_modulation == QAM_64) ? + 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + return -EINVAL; + } + state->snr = calculate_snr(noise, c); + /* report SNR in dB * 10 */ + *snr = (state->snr / ((1 << 24) / 10)); + lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt3305_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + struct lgdt3305_state *state = fe->demodulator_priv; + u16 snr; + int ret; + + *strength = 0; + + ret = fe->ops.read_snr(fe, &snr); + if (lg_fail(ret)) + goto fail; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + + *ucblocks = + (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) | + (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff); + + return 0; +} + +static int lgdt3305_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 500; + lg_dbg("\n"); + return 0; +} + +static void lgdt3305_release(struct dvb_frontend *fe) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + lg_dbg("\n"); + kfree(state); +} + +static const struct dvb_frontend_ops lgdt3304_ops; +static const struct dvb_frontend_ops lgdt3305_ops; + +struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, + struct i2c_adapter *i2c_adap) +{ + struct lgdt3305_state *state = NULL; + int ret; + u8 val; + + lg_dbg("(%d-%04x)\n", + i2c_adap ? i2c_adapter_id(i2c_adap) : 0, + config ? config->i2c_addr : 0); + + state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL); + if (state == NULL) + goto fail; + + state->cfg = config; + state->i2c_adap = i2c_adap; + + switch (config->demod_chip) { + case LGDT3304: + memcpy(&state->frontend.ops, &lgdt3304_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LGDT3305: + memcpy(&state->frontend.ops, &lgdt3305_ops, + sizeof(struct dvb_frontend_ops)); + break; + default: + goto fail; + } + state->frontend.demodulator_priv = state; + + /* verify that we're talking to a lg dt3304/5 */ + ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); + if ((lg_fail(ret)) | (val == 0)) + goto fail; + ret = lgdt3305_write_reg(state, 0x0808, 0x80); + if (lg_fail(ret)) + goto fail; + ret = lgdt3305_read_reg(state, 0x0808, &val); + if ((lg_fail(ret)) | (val != 0x80)) + goto fail; + ret = lgdt3305_write_reg(state, 0x0808, 0x00); + if (lg_fail(ret)) + goto fail; + + state->current_frequency = -1; + state->current_modulation = -1; + + return &state->frontend; +fail: + lg_warn("unable to detect %s hardware\n", + config->demod_chip ? "LGDT3304" : "LGDT3305"); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(lgdt3305_attach); + +static const struct dvb_frontend_ops lgdt3304_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "LG Electronics LGDT3304 VSB/QAM Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, + .init = lgdt3305_init, + .sleep = lgdt3305_sleep, + .set_frontend = lgdt3304_set_parameters, + .get_frontend = lgdt3305_get_frontend, + .get_tune_settings = lgdt3305_get_tune_settings, + .read_status = lgdt3305_read_status, + .read_ber = lgdt3305_read_ber, + .read_signal_strength = lgdt3305_read_signal_strength, + .read_snr = lgdt3305_read_snr, + .read_ucblocks = lgdt3305_read_ucblocks, + .release = lgdt3305_release, +}; + +static const struct dvb_frontend_ops lgdt3305_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "LG Electronics LGDT3305 VSB/QAM Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, + .init = lgdt3305_init, + .sleep = lgdt3305_sleep, + .set_frontend = lgdt3305_set_parameters, + .get_frontend = lgdt3305_get_frontend, + .get_tune_settings = lgdt3305_get_tune_settings, + .read_status = lgdt3305_read_status, + .read_ber = lgdt3305_read_ber, + .read_signal_strength = lgdt3305_read_signal_strength, + .read_snr = lgdt3305_read_snr, + .read_ucblocks = lgdt3305_read_ucblocks, + .release = lgdt3305_release, +}; + +MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); diff --git a/drivers/media/dvb-frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h new file mode 100644 index 0000000000..3718cf8235 --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt3305.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM + * + * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org> + */ + +#ifndef _LGDT3305_H_ +#define _LGDT3305_H_ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + + +enum lgdt3305_mpeg_mode { + LGDT3305_MPEG_PARALLEL = 0, + LGDT3305_MPEG_SERIAL = 1, +}; + +enum lgdt3305_tp_clock_edge { + LGDT3305_TPCLK_RISING_EDGE = 0, + LGDT3305_TPCLK_FALLING_EDGE = 1, +}; + +enum lgdt3305_tp_clock_mode { + LGDT3305_TPCLK_GATED = 0, + LGDT3305_TPCLK_FIXED = 1, +}; + +enum lgdt3305_tp_valid_polarity { + LGDT3305_TP_VALID_LOW = 0, + LGDT3305_TP_VALID_HIGH = 1, +}; + +enum lgdt_demod_chip_type { + LGDT3305 = 0, + LGDT3304 = 1, +}; + +struct lgdt3305_config { + u8 i2c_addr; + + /* user defined IF frequency in KHz */ + u16 qam_if_khz; + u16 vsb_if_khz; + + /* AGC Power reference - defaults are used if left unset */ + u16 usref_8vsb; /* default: 0x32c4 */ + u16 usref_qam64; /* default: 0x5400 */ + u16 usref_qam256; /* default: 0x2a80 */ + + /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ + unsigned int deny_i2c_rptr:1; + + /* spectral inversion - 0:disabled 1:enabled */ + unsigned int spectral_inversion:1; + + /* use RF AGC loop - 0:disabled 1:enabled */ + unsigned int rf_agc_loop:1; + + enum lgdt3305_mpeg_mode mpeg_mode; + enum lgdt3305_tp_clock_edge tpclk_edge; + enum lgdt3305_tp_clock_mode tpclk_mode; + enum lgdt3305_tp_valid_polarity tpvalid_polarity; + enum lgdt_demod_chip_type demod_chip; +}; + +#if IS_REACHABLE(CONFIG_DVB_LGDT3305) +extern +struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, + struct i2c_adapter *i2c_adap); +#else +static inline +struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, + struct i2c_adapter *i2c_adap) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGDT3305 */ + +#endif /* _LGDT3305_H_ */ diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c new file mode 100644 index 0000000000..2638875924 --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -0,0 +1,2262 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for LGDT3306A - 8VSB/QAM-B + * + * Copyright (C) 2013 Fred Richter <frichter@hauppauge.com> + * - driver structure based on lgdt3305.[ch] by Michael Krufky + * - code based on LG3306_V0.35 API by LG Electronics Inc. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <asm/div64.h> +#include <linux/kernel.h> +#include <linux/dvb/frontend.h> +#include <linux/int_log.h> +#include "lgdt3306a.h" +#include <linux/i2c-mux.h> + + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); + +/* + * Older drivers treated QAM64 and QAM256 the same; that is the HW always + * used "Auto" mode during detection. Setting "forced_manual"=1 allows + * the user to treat these modes as separate. For backwards compatibility, + * it's off by default. QAM_AUTO can now be specified to achive that + * effect even if "forced_manual"=1 + */ +static int forced_manual; +module_param(forced_manual, int, 0644); +MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified"); + +#define DBG_INFO 1 +#define DBG_REG 2 +#define DBG_DUMP 4 /* FGR - comment out to remove dump code */ + +#define lg_debug(fmt, arg...) \ + printk(KERN_DEBUG pr_fmt(fmt), ## arg) + +#define dbg_info(fmt, arg...) \ + do { \ + if (debug & DBG_INFO) \ + lg_debug(fmt, ## arg); \ + } while (0) + +#define dbg_reg(fmt, arg...) \ + do { \ + if (debug & DBG_REG) \ + lg_debug(fmt, ## arg); \ + } while (0) + +#define lg_chkerr(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + pr_err("error %d on line %d\n", ret, __LINE__); \ + __ret; \ +}) + +struct lgdt3306a_state { + struct i2c_adapter *i2c_adap; + const struct lgdt3306a_config *cfg; + + struct dvb_frontend frontend; + + enum fe_modulation current_modulation; + u32 current_frequency; + u32 snr; + + struct i2c_mux_core *muxc; +}; + +/* + * LG3306A Register Usage + * (LG does not really name the registers, so this code does not either) + * + * 0000 -> 00FF Common control and status + * 1000 -> 10FF Synchronizer control and status + * 1F00 -> 1FFF Smart Antenna control and status + * 2100 -> 21FF VSB Equalizer control and status + * 2800 -> 28FF QAM Equalizer control and status + * 3000 -> 30FF FEC control and status + */ + +enum lgdt3306a_lock_status { + LG3306_UNLOCK = 0x00, + LG3306_LOCK = 0x01, + LG3306_UNKNOWN_LOCK = 0xff +}; + +enum lgdt3306a_neverlock_status { + LG3306_NL_INIT = 0x00, + LG3306_NL_PROCESS = 0x01, + LG3306_NL_LOCK = 0x02, + LG3306_NL_FAIL = 0x03, + LG3306_NL_UNKNOWN = 0xff +}; + +enum lgdt3306a_modulation { + LG3306_VSB = 0x00, + LG3306_QAM64 = 0x01, + LG3306_QAM256 = 0x02, + LG3306_UNKNOWN_MODE = 0xff +}; + +enum lgdt3306a_lock_check { + LG3306_SYNC_LOCK, + LG3306_FEC_LOCK, + LG3306_TR_LOCK, + LG3306_AGC_LOCK, +}; + + +#ifdef DBG_DUMP +static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state); +static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state); +#endif + + +static int lgdt3306a_write_reg(struct lgdt3306a_state *state, u16 reg, u8 val) +{ + int ret; + u8 buf[] = { reg >> 8, reg & 0xff, val }; + struct i2c_msg msg = { + .addr = state->cfg->i2c_addr, .flags = 0, + .buf = buf, .len = 3, + }; + + dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); + + ret = i2c_transfer(state->i2c_adap, &msg, 1); + + if (ret != 1) { + pr_err("error (addr %02x %02x <- %02x, err = %i)\n", + msg.buf[0], msg.buf[1], msg.buf[2], ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +static int lgdt3306a_read_reg(struct lgdt3306a_state *state, u16 reg, u8 *val) +{ + int ret; + u8 reg_buf[] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[] = { + { .addr = state->cfg->i2c_addr, + .flags = 0, .buf = reg_buf, .len = 2 }, + { .addr = state->cfg->i2c_addr, + .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + ret = i2c_transfer(state->i2c_adap, msg, 2); + + if (ret != 2) { + pr_err("error (addr %02x reg %04x error (ret == %i)\n", + state->cfg->i2c_addr, reg, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, *val); + + return 0; +} + +#define read_reg(state, reg) \ +({ \ + u8 __val; \ + int ret = lgdt3306a_read_reg(state, reg, &__val); \ + if (lg_chkerr(ret)) \ + __val = 0; \ + __val; \ +}) + +static int lgdt3306a_set_reg_bit(struct lgdt3306a_state *state, + u16 reg, int bit, int onoff) +{ + u8 val; + int ret; + + dbg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); + + ret = lgdt3306a_read_reg(state, reg, &val); + if (lg_chkerr(ret)) + goto fail; + + val &= ~(1 << bit); + val |= (onoff & 1) << bit; + + ret = lgdt3306a_write_reg(state, reg, val); + lg_chkerr(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3306a_soft_reset(struct lgdt3306a_state *state) +{ + int ret; + + dbg_info("\n"); + + ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0); + if (lg_chkerr(ret)) + goto fail; + + msleep(20); + ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1); + lg_chkerr(ret); + +fail: + return ret; +} + +static int lgdt3306a_mpeg_mode(struct lgdt3306a_state *state, + enum lgdt3306a_mpeg_mode mode) +{ + u8 val; + int ret; + + dbg_info("(%d)\n", mode); + /* transport packet format - TPSENB=0x80 */ + ret = lgdt3306a_set_reg_bit(state, 0x0071, 7, + mode == LGDT3306A_MPEG_PARALLEL ? 1 : 0); + if (lg_chkerr(ret)) + goto fail; + + /* + * start of packet signal duration + * TPSSOPBITEN=0x40; 0=byte duration, 1=bit duration + */ + ret = lgdt3306a_set_reg_bit(state, 0x0071, 6, 0); + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_read_reg(state, 0x0070, &val); + if (lg_chkerr(ret)) + goto fail; + + val |= 0x10; /* TPCLKSUPB=0x10 */ + + if (mode == LGDT3306A_MPEG_PARALLEL) + val &= ~0x10; + + ret = lgdt3306a_write_reg(state, 0x0070, val); + lg_chkerr(ret); + +fail: + return ret; +} + +static int lgdt3306a_mpeg_mode_polarity(struct lgdt3306a_state *state, + enum lgdt3306a_tp_clock_edge edge, + enum lgdt3306a_tp_valid_polarity valid) +{ + u8 val; + int ret; + + dbg_info("edge=%d, valid=%d\n", edge, valid); + + ret = lgdt3306a_read_reg(state, 0x0070, &val); + if (lg_chkerr(ret)) + goto fail; + + val &= ~0x06; /* TPCLKPOL=0x04, TPVALPOL=0x02 */ + + if (edge == LGDT3306A_TPCLK_RISING_EDGE) + val |= 0x04; + if (valid == LGDT3306A_TP_VALID_HIGH) + val |= 0x02; + + ret = lgdt3306a_write_reg(state, 0x0070, val); + lg_chkerr(ret); + +fail: + return ret; +} + +static int lgdt3306a_mpeg_tristate(struct lgdt3306a_state *state, + int mode) +{ + u8 val; + int ret; + + dbg_info("(%d)\n", mode); + + if (mode) { + ret = lgdt3306a_read_reg(state, 0x0070, &val); + if (lg_chkerr(ret)) + goto fail; + /* + * Tristate bus; TPOUTEN=0x80, TPCLKOUTEN=0x20, + * TPDATAOUTEN=0x08 + */ + val &= ~0xa8; + ret = lgdt3306a_write_reg(state, 0x0070, val); + if (lg_chkerr(ret)) + goto fail; + + /* AGCIFOUTENB=0x40; 1=Disable IFAGC pin */ + ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 1); + if (lg_chkerr(ret)) + goto fail; + + } else { + /* enable IFAGC pin */ + ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 0); + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_read_reg(state, 0x0070, &val); + if (lg_chkerr(ret)) + goto fail; + + val |= 0xa8; /* enable bus */ + ret = lgdt3306a_write_reg(state, 0x0070, val); + if (lg_chkerr(ret)) + goto fail; + } + +fail: + return ret; +} + +static int lgdt3306a_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + dbg_info("acquire=%d\n", acquire); + + return lgdt3306a_mpeg_tristate(state, acquire ? 0 : 1); + +} + +static int lgdt3306a_power(struct lgdt3306a_state *state, + int mode) +{ + int ret; + + dbg_info("(%d)\n", mode); + + if (mode == 0) { + /* into reset */ + ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0); + if (lg_chkerr(ret)) + goto fail; + + /* power down */ + ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 0); + if (lg_chkerr(ret)) + goto fail; + + } else { + /* out of reset */ + ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1); + if (lg_chkerr(ret)) + goto fail; + + /* power up */ + ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 1); + if (lg_chkerr(ret)) + goto fail; + } + +#ifdef DBG_DUMP + lgdt3306a_DumpAllRegs(state); +#endif +fail: + return ret; +} + + +static int lgdt3306a_set_vsb(struct lgdt3306a_state *state) +{ + u8 val; + int ret; + + dbg_info("\n"); + + /* 0. Spectrum inversion detection manual; spectrum inverted */ + ret = lgdt3306a_read_reg(state, 0x0002, &val); + val &= 0xf7; /* SPECINVAUTO Off */ + val |= 0x04; /* SPECINV On */ + ret = lgdt3306a_write_reg(state, 0x0002, val); + if (lg_chkerr(ret)) + goto fail; + + /* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */ + ret = lgdt3306a_write_reg(state, 0x0008, 0x80); + if (lg_chkerr(ret)) + goto fail; + + /* 2. Bandwidth mode for VSB(6MHz) */ + ret = lgdt3306a_read_reg(state, 0x0009, &val); + val &= 0xe3; + val |= 0x0c; /* STDOPDETTMODE[2:0]=3 */ + ret = lgdt3306a_write_reg(state, 0x0009, val); + if (lg_chkerr(ret)) + goto fail; + + /* 3. QAM mode detection mode(None) */ + ret = lgdt3306a_read_reg(state, 0x0009, &val); + val &= 0xfc; /* STDOPDETCMODE[1:0]=0 */ + ret = lgdt3306a_write_reg(state, 0x0009, val); + if (lg_chkerr(ret)) + goto fail; + + /* 4. ADC sampling frequency rate(2x sampling) */ + ret = lgdt3306a_read_reg(state, 0x000d, &val); + val &= 0xbf; /* SAMPLING4XFEN=0 */ + ret = lgdt3306a_write_reg(state, 0x000d, val); + if (lg_chkerr(ret)) + goto fail; + +#if 0 + /* FGR - disable any AICC filtering, testing only */ + + ret = lgdt3306a_write_reg(state, 0x0024, 0x00); + if (lg_chkerr(ret)) + goto fail; + + /* AICCFIXFREQ0 NT N-1(Video rejection) */ + ret = lgdt3306a_write_reg(state, 0x002e, 0x00); + ret = lgdt3306a_write_reg(state, 0x002f, 0x00); + ret = lgdt3306a_write_reg(state, 0x0030, 0x00); + + /* AICCFIXFREQ1 NT N-1(Audio rejection) */ + ret = lgdt3306a_write_reg(state, 0x002b, 0x00); + ret = lgdt3306a_write_reg(state, 0x002c, 0x00); + ret = lgdt3306a_write_reg(state, 0x002d, 0x00); + + /* AICCFIXFREQ2 NT Co-Channel(Video rejection) */ + ret = lgdt3306a_write_reg(state, 0x0028, 0x00); + ret = lgdt3306a_write_reg(state, 0x0029, 0x00); + ret = lgdt3306a_write_reg(state, 0x002a, 0x00); + + /* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */ + ret = lgdt3306a_write_reg(state, 0x0025, 0x00); + ret = lgdt3306a_write_reg(state, 0x0026, 0x00); + ret = lgdt3306a_write_reg(state, 0x0027, 0x00); + +#else + /* FGR - this works well for HVR-1955,1975 */ + + /* 5. AICCOPMODE NT N-1 Adj. */ + ret = lgdt3306a_write_reg(state, 0x0024, 0x5A); + if (lg_chkerr(ret)) + goto fail; + + /* AICCFIXFREQ0 NT N-1(Video rejection) */ + ret = lgdt3306a_write_reg(state, 0x002e, 0x5A); + ret = lgdt3306a_write_reg(state, 0x002f, 0x00); + ret = lgdt3306a_write_reg(state, 0x0030, 0x00); + + /* AICCFIXFREQ1 NT N-1(Audio rejection) */ + ret = lgdt3306a_write_reg(state, 0x002b, 0x36); + ret = lgdt3306a_write_reg(state, 0x002c, 0x00); + ret = lgdt3306a_write_reg(state, 0x002d, 0x00); + + /* AICCFIXFREQ2 NT Co-Channel(Video rejection) */ + ret = lgdt3306a_write_reg(state, 0x0028, 0x2A); + ret = lgdt3306a_write_reg(state, 0x0029, 0x00); + ret = lgdt3306a_write_reg(state, 0x002a, 0x00); + + /* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */ + ret = lgdt3306a_write_reg(state, 0x0025, 0x06); + ret = lgdt3306a_write_reg(state, 0x0026, 0x00); + ret = lgdt3306a_write_reg(state, 0x0027, 0x00); +#endif + + ret = lgdt3306a_read_reg(state, 0x001e, &val); + val &= 0x0f; + val |= 0xa0; + ret = lgdt3306a_write_reg(state, 0x001e, val); + + ret = lgdt3306a_write_reg(state, 0x0022, 0x08); + + ret = lgdt3306a_write_reg(state, 0x0023, 0xFF); + + ret = lgdt3306a_read_reg(state, 0x211f, &val); + val &= 0xef; + ret = lgdt3306a_write_reg(state, 0x211f, val); + + ret = lgdt3306a_write_reg(state, 0x2173, 0x01); + + ret = lgdt3306a_read_reg(state, 0x1061, &val); + val &= 0xf8; + val |= 0x04; + ret = lgdt3306a_write_reg(state, 0x1061, val); + + ret = lgdt3306a_read_reg(state, 0x103d, &val); + val &= 0xcf; + ret = lgdt3306a_write_reg(state, 0x103d, val); + + ret = lgdt3306a_write_reg(state, 0x2122, 0x40); + + ret = lgdt3306a_read_reg(state, 0x2141, &val); + val &= 0x3f; + ret = lgdt3306a_write_reg(state, 0x2141, val); + + ret = lgdt3306a_read_reg(state, 0x2135, &val); + val &= 0x0f; + val |= 0x70; + ret = lgdt3306a_write_reg(state, 0x2135, val); + + ret = lgdt3306a_read_reg(state, 0x0003, &val); + val &= 0xf7; + ret = lgdt3306a_write_reg(state, 0x0003, val); + + ret = lgdt3306a_read_reg(state, 0x001c, &val); + val &= 0x7f; + ret = lgdt3306a_write_reg(state, 0x001c, val); + + /* 6. EQ step size */ + ret = lgdt3306a_read_reg(state, 0x2179, &val); + val &= 0xf8; + ret = lgdt3306a_write_reg(state, 0x2179, val); + + ret = lgdt3306a_read_reg(state, 0x217a, &val); + val &= 0xf8; + ret = lgdt3306a_write_reg(state, 0x217a, val); + + /* 7. Reset */ + ret = lgdt3306a_soft_reset(state); + if (lg_chkerr(ret)) + goto fail; + + dbg_info("complete\n"); +fail: + return ret; +} + +static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation) +{ + u8 val; + int ret; + + dbg_info("modulation=%d\n", modulation); + + /* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */ + ret = lgdt3306a_write_reg(state, 0x0008, 0x08); + if (lg_chkerr(ret)) + goto fail; + + /* 1a. Spectrum inversion detection to Auto */ + ret = lgdt3306a_read_reg(state, 0x0002, &val); + val &= 0xfb; /* SPECINV Off */ + val |= 0x08; /* SPECINVAUTO On */ + ret = lgdt3306a_write_reg(state, 0x0002, val); + if (lg_chkerr(ret)) + goto fail; + + /* 2. Bandwidth mode for QAM */ + ret = lgdt3306a_read_reg(state, 0x0009, &val); + val &= 0xe3; /* STDOPDETTMODE[2:0]=0 VSB Off */ + ret = lgdt3306a_write_reg(state, 0x0009, val); + if (lg_chkerr(ret)) + goto fail; + + /* 3. : 64QAM/256QAM detection(manual, auto) */ + ret = lgdt3306a_read_reg(state, 0x0009, &val); + val &= 0xfc; + /* Check for forced Manual modulation modes; otherwise always "auto" */ + if(forced_manual && (modulation != QAM_AUTO)){ + val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */ + } else { + val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */ + } + ret = lgdt3306a_write_reg(state, 0x0009, val); + if (lg_chkerr(ret)) + goto fail; + + /* 3a. : 64QAM/256QAM selection for manual */ + ret = lgdt3306a_read_reg(state, 0x101a, &val); + val &= 0xf8; + if (modulation == QAM_64) + val |= 0x02; /* QMDQMODE[2:0]=2=QAM64 */ + else + val |= 0x04; /* QMDQMODE[2:0]=4=QAM256 */ + + ret = lgdt3306a_write_reg(state, 0x101a, val); + if (lg_chkerr(ret)) + goto fail; + + /* 4. ADC sampling frequency rate(4x sampling) */ + ret = lgdt3306a_read_reg(state, 0x000d, &val); + val &= 0xbf; + val |= 0x40; /* SAMPLING4XFEN=1 */ + ret = lgdt3306a_write_reg(state, 0x000d, val); + if (lg_chkerr(ret)) + goto fail; + + /* 5. No AICC operation in QAM mode */ + ret = lgdt3306a_read_reg(state, 0x0024, &val); + val &= 0x00; + ret = lgdt3306a_write_reg(state, 0x0024, val); + if (lg_chkerr(ret)) + goto fail; + + /* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */ + ret = lgdt3306a_read_reg(state, 0x000a, &val); + val &= 0xfd; + val |= 0x02; + ret = lgdt3306a_write_reg(state, 0x000a, val); + if (lg_chkerr(ret)) + goto fail; + + /* 5.2 V0.36 Control of "no signal" detector function */ + ret = lgdt3306a_read_reg(state, 0x2849, &val); + val &= 0xdf; + ret = lgdt3306a_write_reg(state, 0x2849, val); + if (lg_chkerr(ret)) + goto fail; + + /* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */ + ret = lgdt3306a_read_reg(state, 0x302b, &val); + val &= 0x7f; /* SELFSYNCFINDEN_CQS=0; disable auto reset */ + ret = lgdt3306a_write_reg(state, 0x302b, val); + if (lg_chkerr(ret)) + goto fail; + + /* 6. Reset */ + ret = lgdt3306a_soft_reset(state); + if (lg_chkerr(ret)) + goto fail; + + dbg_info("complete\n"); +fail: + return ret; +} + +static int lgdt3306a_set_modulation(struct lgdt3306a_state *state, + struct dtv_frontend_properties *p) +{ + int ret; + + dbg_info("\n"); + + switch (p->modulation) { + case VSB_8: + ret = lgdt3306a_set_vsb(state); + break; + case QAM_64: + case QAM_256: + case QAM_AUTO: + ret = lgdt3306a_set_qam(state, p->modulation); + break; + default: + return -EINVAL; + } + if (lg_chkerr(ret)) + goto fail; + + state->current_modulation = p->modulation; + +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3306a_agc_setup(struct lgdt3306a_state *state, + struct dtv_frontend_properties *p) +{ + /* TODO: anything we want to do here??? */ + dbg_info("\n"); + + switch (p->modulation) { + case VSB_8: + break; + case QAM_64: + case QAM_256: + case QAM_AUTO: + break; + default: + return -EINVAL; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3306a_set_inversion(struct lgdt3306a_state *state, + int inversion) +{ + int ret; + + dbg_info("(%d)\n", inversion); + + ret = lgdt3306a_set_reg_bit(state, 0x0002, 2, inversion ? 1 : 0); + return ret; +} + +static int lgdt3306a_set_inversion_auto(struct lgdt3306a_state *state, + int enabled) +{ + int ret; + + dbg_info("(%d)\n", enabled); + + /* 0=Manual 1=Auto(QAM only) - SPECINVAUTO=0x04 */ + ret = lgdt3306a_set_reg_bit(state, 0x0002, 3, enabled); + return ret; +} + +static int lgdt3306a_set_if(struct lgdt3306a_state *state, + struct dtv_frontend_properties *p) +{ + int ret; + u16 if_freq_khz; + u8 nco1, nco2; + + switch (p->modulation) { + case VSB_8: + if_freq_khz = state->cfg->vsb_if_khz; + break; + case QAM_64: + case QAM_256: + case QAM_AUTO: + if_freq_khz = state->cfg->qam_if_khz; + break; + default: + return -EINVAL; + } + + switch (if_freq_khz) { + default: + pr_warn("IF=%d KHz is not supported, 3250 assumed\n", + if_freq_khz); + fallthrough; + case 3250: /* 3.25Mhz */ + nco1 = 0x34; + nco2 = 0x00; + break; + case 3500: /* 3.50Mhz */ + nco1 = 0x38; + nco2 = 0x00; + break; + case 4000: /* 4.00Mhz */ + nco1 = 0x40; + nco2 = 0x00; + break; + case 5000: /* 5.00Mhz */ + nco1 = 0x50; + nco2 = 0x00; + break; + case 5380: /* 5.38Mhz */ + nco1 = 0x56; + nco2 = 0x14; + break; + } + ret = lgdt3306a_write_reg(state, 0x0010, nco1); + if (ret) + return ret; + ret = lgdt3306a_write_reg(state, 0x0011, nco2); + if (ret) + return ret; + + dbg_info("if_freq=%d KHz->[%04x]\n", if_freq_khz, nco1<<8 | nco2); + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3306a_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + if (state->cfg->deny_i2c_rptr) { + dbg_info("deny_i2c_rptr=%d\n", state->cfg->deny_i2c_rptr); + return 0; + } + dbg_info("(%d)\n", enable); + + /* NI2CRPTEN=0x80 */ + return lgdt3306a_set_reg_bit(state, 0x0002, 7, enable ? 0 : 1); +} + +static int lgdt3306a_sleep(struct lgdt3306a_state *state) +{ + int ret; + + dbg_info("\n"); + state->current_frequency = -1; /* force re-tune, when we wake */ + + ret = lgdt3306a_mpeg_tristate(state, 1); /* disable data bus */ + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_power(state, 0); /* power down */ + lg_chkerr(ret); + +fail: + return 0; +} + +static int lgdt3306a_fe_sleep(struct dvb_frontend *fe) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + return lgdt3306a_sleep(state); +} + +static int lgdt3306a_init(struct dvb_frontend *fe) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 val; + int ret; + + dbg_info("\n"); + + /* 1. Normal operation mode */ + ret = lgdt3306a_set_reg_bit(state, 0x0001, 0, 1); /* SIMFASTENB=0x01 */ + if (lg_chkerr(ret)) + goto fail; + + /* 2. Spectrum inversion auto detection (Not valid for VSB) */ + ret = lgdt3306a_set_inversion_auto(state, 0); + if (lg_chkerr(ret)) + goto fail; + + /* 3. Spectrum inversion(According to the tuner configuration) */ + ret = lgdt3306a_set_inversion(state, 1); + if (lg_chkerr(ret)) + goto fail; + + /* 4. Peak-to-peak voltage of ADC input signal */ + + /* ADCSEL1V=0x80=1Vpp; 0x00=2Vpp */ + ret = lgdt3306a_set_reg_bit(state, 0x0004, 7, 1); + if (lg_chkerr(ret)) + goto fail; + + /* 5. ADC output data capture clock phase */ + + /* 0=same phase as ADC clock */ + ret = lgdt3306a_set_reg_bit(state, 0x0004, 2, 0); + if (lg_chkerr(ret)) + goto fail; + + /* 5a. ADC sampling clock source */ + + /* ADCCLKPLLSEL=0x08; 0=use ext clock, not PLL */ + ret = lgdt3306a_set_reg_bit(state, 0x0004, 3, 0); + if (lg_chkerr(ret)) + goto fail; + + /* 6. Automatic PLL set */ + + /* PLLSETAUTO=0x40; 0=off */ + ret = lgdt3306a_set_reg_bit(state, 0x0005, 6, 0); + if (lg_chkerr(ret)) + goto fail; + + if (state->cfg->xtalMHz == 24) { /* 24MHz */ + /* 7. Frequency for PLL output(0x2564 for 192MHz for 24MHz) */ + ret = lgdt3306a_read_reg(state, 0x0005, &val); + if (lg_chkerr(ret)) + goto fail; + val &= 0xc0; + val |= 0x25; + ret = lgdt3306a_write_reg(state, 0x0005, val); + if (lg_chkerr(ret)) + goto fail; + ret = lgdt3306a_write_reg(state, 0x0006, 0x64); + if (lg_chkerr(ret)) + goto fail; + + /* 8. ADC sampling frequency(0x180000 for 24MHz sampling) */ + ret = lgdt3306a_read_reg(state, 0x000d, &val); + if (lg_chkerr(ret)) + goto fail; + val &= 0xc0; + val |= 0x18; + ret = lgdt3306a_write_reg(state, 0x000d, val); + if (lg_chkerr(ret)) + goto fail; + + } else if (state->cfg->xtalMHz == 25) { /* 25MHz */ + /* 7. Frequency for PLL output */ + ret = lgdt3306a_read_reg(state, 0x0005, &val); + if (lg_chkerr(ret)) + goto fail; + val &= 0xc0; + val |= 0x25; + ret = lgdt3306a_write_reg(state, 0x0005, val); + if (lg_chkerr(ret)) + goto fail; + ret = lgdt3306a_write_reg(state, 0x0006, 0x64); + if (lg_chkerr(ret)) + goto fail; + + /* 8. ADC sampling frequency(0x190000 for 25MHz sampling) */ + ret = lgdt3306a_read_reg(state, 0x000d, &val); + if (lg_chkerr(ret)) + goto fail; + val &= 0xc0; + val |= 0x19; + ret = lgdt3306a_write_reg(state, 0x000d, val); + if (lg_chkerr(ret)) + goto fail; + } else { + pr_err("Bad xtalMHz=%d\n", state->cfg->xtalMHz); + } +#if 0 + ret = lgdt3306a_write_reg(state, 0x000e, 0x00); + ret = lgdt3306a_write_reg(state, 0x000f, 0x00); +#endif + + /* 9. Center frequency of input signal of ADC */ + ret = lgdt3306a_write_reg(state, 0x0010, 0x34); /* 3.25MHz */ + ret = lgdt3306a_write_reg(state, 0x0011, 0x00); + + /* 10. Fixed gain error value */ + ret = lgdt3306a_write_reg(state, 0x0014, 0); /* gain error=0 */ + + /* 10a. VSB TR BW gear shift initial step */ + ret = lgdt3306a_read_reg(state, 0x103c, &val); + val &= 0x0f; + val |= 0x20; /* SAMGSAUTOSTL_V[3:0] = 2 */ + ret = lgdt3306a_write_reg(state, 0x103c, val); + + /* 10b. Timing offset calibration in low temperature for VSB */ + ret = lgdt3306a_read_reg(state, 0x103d, &val); + val &= 0xfc; + val |= 0x03; + ret = lgdt3306a_write_reg(state, 0x103d, val); + + /* 10c. Timing offset calibration in low temperature for QAM */ + ret = lgdt3306a_read_reg(state, 0x1036, &val); + val &= 0xf0; + val |= 0x0c; + ret = lgdt3306a_write_reg(state, 0x1036, val); + + /* 11. Using the imaginary part of CIR in CIR loading */ + ret = lgdt3306a_read_reg(state, 0x211f, &val); + val &= 0xef; /* do not use imaginary of CIR */ + ret = lgdt3306a_write_reg(state, 0x211f, val); + + /* 12. Control of no signal detector function */ + ret = lgdt3306a_read_reg(state, 0x2849, &val); + val &= 0xef; /* NOUSENOSIGDET=0, enable no signal detector */ + ret = lgdt3306a_write_reg(state, 0x2849, val); + + /* FGR - put demod in some known mode */ + ret = lgdt3306a_set_vsb(state); + + /* 13. TP stream format */ + ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode); + + /* 14. disable output buses */ + ret = lgdt3306a_mpeg_tristate(state, 1); + + /* 15. Sleep (in reset) */ + ret = lgdt3306a_sleep(state); + lg_chkerr(ret); + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + +fail: + return ret; +} + +static int lgdt3306a_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct lgdt3306a_state *state = fe->demodulator_priv; + int ret; + + dbg_info("(%d, %d)\n", p->frequency, p->modulation); + + if (state->current_frequency == p->frequency && + state->current_modulation == p->modulation) { + dbg_info(" (already set, skipping ...)\n"); + return 0; + } + state->current_frequency = -1; + state->current_modulation = -1; + + ret = lgdt3306a_power(state, 1); /* power up */ + if (lg_chkerr(ret)) + goto fail; + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); +#if 0 + if (lg_chkerr(ret)) + goto fail; + state->current_frequency = p->frequency; +#endif + } + + ret = lgdt3306a_set_modulation(state, p); + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_agc_setup(state, p); + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_set_if(state, p); + if (lg_chkerr(ret)) + goto fail; + + /* spectral_inversion defaults already set for VSB and QAM */ + + ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode); + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_mpeg_mode_polarity(state, + state->cfg->tpclk_edge, + state->cfg->tpvalid_polarity); + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_mpeg_tristate(state, 0); /* enable data bus */ + if (lg_chkerr(ret)) + goto fail; + + ret = lgdt3306a_soft_reset(state); + if (lg_chkerr(ret)) + goto fail; + +#ifdef DBG_DUMP + lgdt3306a_DumpAllRegs(state); +#endif + state->current_frequency = p->frequency; +fail: + return ret; +} + +static int lgdt3306a_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + dbg_info("(%u, %d)\n", + state->current_frequency, state->current_modulation); + + p->modulation = state->current_modulation; + p->frequency = state->current_frequency; + return 0; +} + +static enum dvbfe_algo lgdt3306a_get_frontend_algo(struct dvb_frontend *fe) +{ +#if 1 + return DVBFE_ALGO_CUSTOM; +#else + return DVBFE_ALGO_HW; +#endif +} + +/* ------------------------------------------------------------------------ */ +static int lgdt3306a_monitor_vsb(struct lgdt3306a_state *state) +{ + u8 val; + int ret; + u8 snrRef, maxPowerMan, nCombDet; + u16 fbDlyCir; + + ret = lgdt3306a_read_reg(state, 0x21a1, &val); + if (ret) + return ret; + snrRef = val & 0x3f; + + ret = lgdt3306a_read_reg(state, 0x2185, &maxPowerMan); + if (ret) + return ret; + + ret = lgdt3306a_read_reg(state, 0x2191, &val); + if (ret) + return ret; + nCombDet = (val & 0x80) >> 7; + + ret = lgdt3306a_read_reg(state, 0x2180, &val); + if (ret) + return ret; + fbDlyCir = (val & 0x03) << 8; + + ret = lgdt3306a_read_reg(state, 0x2181, &val); + if (ret) + return ret; + fbDlyCir |= val; + + dbg_info("snrRef=%d maxPowerMan=0x%x nCombDet=%d fbDlyCir=0x%x\n", + snrRef, maxPowerMan, nCombDet, fbDlyCir); + + /* Carrier offset sub loop bandwidth */ + ret = lgdt3306a_read_reg(state, 0x1061, &val); + if (ret) + return ret; + val &= 0xf8; + if ((snrRef > 18) && (maxPowerMan > 0x68) + && (nCombDet == 0x01) + && ((fbDlyCir == 0x03FF) || (fbDlyCir < 0x6C))) { + /* SNR is over 18dB and no ghosting */ + val |= 0x00; /* final bandwidth = 0 */ + } else { + val |= 0x04; /* final bandwidth = 4 */ + } + ret = lgdt3306a_write_reg(state, 0x1061, val); + if (ret) + return ret; + + /* Adjust Notch Filter */ + ret = lgdt3306a_read_reg(state, 0x0024, &val); + if (ret) + return ret; + val &= 0x0f; + if (nCombDet == 0) { /* Turn on the Notch Filter */ + val |= 0x50; + } + ret = lgdt3306a_write_reg(state, 0x0024, val); + if (ret) + return ret; + + /* VSB Timing Recovery output normalization */ + ret = lgdt3306a_read_reg(state, 0x103d, &val); + if (ret) + return ret; + val &= 0xcf; + val |= 0x20; + ret = lgdt3306a_write_reg(state, 0x103d, val); + + return ret; +} + +static enum lgdt3306a_modulation +lgdt3306a_check_oper_mode(struct lgdt3306a_state *state) +{ + u8 val = 0; + int ret; + + ret = lgdt3306a_read_reg(state, 0x0081, &val); + if (ret) + goto err; + + if (val & 0x80) { + dbg_info("VSB\n"); + return LG3306_VSB; + } + if (val & 0x08) { + ret = lgdt3306a_read_reg(state, 0x00a6, &val); + if (ret) + goto err; + val = val >> 2; + if (val & 0x01) { + dbg_info("QAM256\n"); + return LG3306_QAM256; + } + dbg_info("QAM64\n"); + return LG3306_QAM64; + } +err: + pr_warn("UNKNOWN\n"); + return LG3306_UNKNOWN_MODE; +} + +static enum lgdt3306a_lock_status +lgdt3306a_check_lock_status(struct lgdt3306a_state *state, + enum lgdt3306a_lock_check whatLock) +{ + u8 val = 0; + int ret; + enum lgdt3306a_modulation modeOper; + enum lgdt3306a_lock_status lockStatus; + + modeOper = LG3306_UNKNOWN_MODE; + + switch (whatLock) { + case LG3306_SYNC_LOCK: + { + ret = lgdt3306a_read_reg(state, 0x00a6, &val); + if (ret) + return ret; + + if ((val & 0x80) == 0x80) + lockStatus = LG3306_LOCK; + else + lockStatus = LG3306_UNLOCK; + + dbg_info("SYNC_LOCK=%x\n", lockStatus); + break; + } + case LG3306_AGC_LOCK: + { + ret = lgdt3306a_read_reg(state, 0x0080, &val); + if (ret) + return ret; + + if ((val & 0x40) == 0x40) + lockStatus = LG3306_LOCK; + else + lockStatus = LG3306_UNLOCK; + + dbg_info("AGC_LOCK=%x\n", lockStatus); + break; + } + case LG3306_TR_LOCK: + { + modeOper = lgdt3306a_check_oper_mode(state); + if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) { + ret = lgdt3306a_read_reg(state, 0x1094, &val); + if (ret) + return ret; + + if ((val & 0x80) == 0x80) + lockStatus = LG3306_LOCK; + else + lockStatus = LG3306_UNLOCK; + } else + lockStatus = LG3306_UNKNOWN_LOCK; + + dbg_info("TR_LOCK=%x\n", lockStatus); + break; + } + case LG3306_FEC_LOCK: + { + modeOper = lgdt3306a_check_oper_mode(state); + if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) { + ret = lgdt3306a_read_reg(state, 0x0080, &val); + if (ret) + return ret; + + if ((val & 0x10) == 0x10) + lockStatus = LG3306_LOCK; + else + lockStatus = LG3306_UNLOCK; + } else + lockStatus = LG3306_UNKNOWN_LOCK; + + dbg_info("FEC_LOCK=%x\n", lockStatus); + break; + } + + default: + lockStatus = LG3306_UNKNOWN_LOCK; + pr_warn("UNKNOWN whatLock=%d\n", whatLock); + break; + } + + return lockStatus; +} + +static enum lgdt3306a_neverlock_status +lgdt3306a_check_neverlock_status(struct lgdt3306a_state *state) +{ + u8 val = 0; + int ret; + enum lgdt3306a_neverlock_status lockStatus; + + ret = lgdt3306a_read_reg(state, 0x0080, &val); + if (ret) + return ret; + lockStatus = (enum lgdt3306a_neverlock_status)(val & 0x03); + + dbg_info("NeverLock=%d", lockStatus); + + return lockStatus; +} + +static int lgdt3306a_pre_monitoring(struct lgdt3306a_state *state) +{ + u8 val = 0; + int ret; + u8 currChDiffACQ, snrRef, mainStrong, aiccrejStatus; + + /* Channel variation */ + ret = lgdt3306a_read_reg(state, 0x21bc, &currChDiffACQ); + if (ret) + return ret; + + /* SNR of Frame sync */ + ret = lgdt3306a_read_reg(state, 0x21a1, &val); + if (ret) + return ret; + snrRef = val & 0x3f; + + /* Strong Main CIR */ + ret = lgdt3306a_read_reg(state, 0x2199, &val); + if (ret) + return ret; + mainStrong = (val & 0x40) >> 6; + + ret = lgdt3306a_read_reg(state, 0x0090, &val); + if (ret) + return ret; + aiccrejStatus = (val & 0xf0) >> 4; + + dbg_info("snrRef=%d mainStrong=%d aiccrejStatus=%d currChDiffACQ=0x%x\n", + snrRef, mainStrong, aiccrejStatus, currChDiffACQ); + +#if 0 + /* Dynamic ghost exists */ + if ((mainStrong == 0) && (currChDiffACQ > 0x70)) +#endif + if (mainStrong == 0) { + ret = lgdt3306a_read_reg(state, 0x2135, &val); + if (ret) + return ret; + val &= 0x0f; + val |= 0xa0; + ret = lgdt3306a_write_reg(state, 0x2135, val); + if (ret) + return ret; + + ret = lgdt3306a_read_reg(state, 0x2141, &val); + if (ret) + return ret; + val &= 0x3f; + val |= 0x80; + ret = lgdt3306a_write_reg(state, 0x2141, val); + if (ret) + return ret; + + ret = lgdt3306a_write_reg(state, 0x2122, 0x70); + if (ret) + return ret; + } else { /* Weak ghost or static channel */ + ret = lgdt3306a_read_reg(state, 0x2135, &val); + if (ret) + return ret; + val &= 0x0f; + val |= 0x70; + ret = lgdt3306a_write_reg(state, 0x2135, val); + if (ret) + return ret; + + ret = lgdt3306a_read_reg(state, 0x2141, &val); + if (ret) + return ret; + val &= 0x3f; + val |= 0x40; + ret = lgdt3306a_write_reg(state, 0x2141, val); + if (ret) + return ret; + + ret = lgdt3306a_write_reg(state, 0x2122, 0x40); + if (ret) + return ret; + } + return 0; +} + +static enum lgdt3306a_lock_status +lgdt3306a_sync_lock_poll(struct lgdt3306a_state *state) +{ + enum lgdt3306a_lock_status syncLockStatus = LG3306_UNLOCK; + int i; + + for (i = 0; i < 2; i++) { + msleep(30); + + syncLockStatus = lgdt3306a_check_lock_status(state, + LG3306_SYNC_LOCK); + + if (syncLockStatus == LG3306_LOCK) { + dbg_info("locked(%d)\n", i); + return LG3306_LOCK; + } + } + dbg_info("not locked\n"); + return LG3306_UNLOCK; +} + +static enum lgdt3306a_lock_status +lgdt3306a_fec_lock_poll(struct lgdt3306a_state *state) +{ + enum lgdt3306a_lock_status FECLockStatus = LG3306_UNLOCK; + int i; + + for (i = 0; i < 2; i++) { + msleep(30); + + FECLockStatus = lgdt3306a_check_lock_status(state, + LG3306_FEC_LOCK); + + if (FECLockStatus == LG3306_LOCK) { + dbg_info("locked(%d)\n", i); + return FECLockStatus; + } + } + dbg_info("not locked\n"); + return FECLockStatus; +} + +static enum lgdt3306a_neverlock_status +lgdt3306a_neverlock_poll(struct lgdt3306a_state *state) +{ + enum lgdt3306a_neverlock_status NLLockStatus = LG3306_NL_FAIL; + int i; + + for (i = 0; i < 5; i++) { + msleep(30); + + NLLockStatus = lgdt3306a_check_neverlock_status(state); + + if (NLLockStatus == LG3306_NL_LOCK) { + dbg_info("NL_LOCK(%d)\n", i); + return NLLockStatus; + } + } + dbg_info("NLLockStatus=%d\n", NLLockStatus); + return NLLockStatus; +} + +static u8 lgdt3306a_get_packet_error(struct lgdt3306a_state *state) +{ + u8 val; + int ret; + + ret = lgdt3306a_read_reg(state, 0x00fa, &val); + if (ret) + return ret; + + return val; +} + +static const u32 valx_x10[] = { + 10, 11, 13, 15, 17, 20, 25, 33, 41, 50, 59, 73, 87, 100 +}; +static const u32 log10x_x1000[] = { + 0, 41, 114, 176, 230, 301, 398, 518, 613, 699, 771, 863, 939, 1000 +}; + +static u32 log10_x1000(u32 x) +{ + u32 diff_val, step_val, step_log10; + u32 log_val = 0; + u32 i; + + if (x <= 0) + return -1000000; /* signal error */ + + if (x == 10) + return 0; /* log(1)=0 */ + + if (x < 10) { + while (x < 10) { + x = x * 10; + log_val--; + } + } else { /* x > 10 */ + while (x >= 100) { + x = x / 10; + log_val++; + } + } + log_val *= 1000; + + if (x == 10) /* was our input an exact multiple of 10 */ + return log_val; /* don't need to interpolate */ + + /* find our place on the log curve */ + for (i = 1; i < ARRAY_SIZE(valx_x10); i++) { + if (valx_x10[i] >= x) + break; + } + if (i == ARRAY_SIZE(valx_x10)) + return log_val + log10x_x1000[i - 1]; + + diff_val = x - valx_x10[i-1]; + step_val = valx_x10[i] - valx_x10[i - 1]; + step_log10 = log10x_x1000[i] - log10x_x1000[i - 1]; + + /* do a linear interpolation to get in-between values */ + return log_val + log10x_x1000[i - 1] + + ((diff_val*step_log10) / step_val); +} + +static u32 lgdt3306a_calculate_snr_x100(struct lgdt3306a_state *state) +{ + u32 mse; /* Mean-Square Error */ + u32 pwr; /* Constelation power */ + u32 snr_x100; + + mse = (read_reg(state, 0x00ec) << 8) | + (read_reg(state, 0x00ed)); + pwr = (read_reg(state, 0x00e8) << 8) | + (read_reg(state, 0x00e9)); + + if (mse == 0) /* no signal */ + return 0; + + snr_x100 = log10_x1000((pwr * 10000) / mse) - 3000; + dbg_info("mse=%u, pwr=%u, snr_x100=%d\n", mse, pwr, snr_x100); + + return snr_x100; +} + +static enum lgdt3306a_lock_status +lgdt3306a_vsb_lock_poll(struct lgdt3306a_state *state) +{ + int ret; + u8 cnt = 0; + u8 packet_error; + u32 snr; + + for (cnt = 0; cnt < 10; cnt++) { + if (lgdt3306a_sync_lock_poll(state) == LG3306_UNLOCK) { + dbg_info("no sync lock!\n"); + return LG3306_UNLOCK; + } + + msleep(20); + ret = lgdt3306a_pre_monitoring(state); + if (ret) + break; + + packet_error = lgdt3306a_get_packet_error(state); + snr = lgdt3306a_calculate_snr_x100(state); + dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr); + + if ((snr >= 1500) && (packet_error < 0xff)) + return LG3306_LOCK; + } + + dbg_info("not locked!\n"); + return LG3306_UNLOCK; +} + +static enum lgdt3306a_lock_status +lgdt3306a_qam_lock_poll(struct lgdt3306a_state *state) +{ + u8 cnt; + u8 packet_error; + u32 snr; + + for (cnt = 0; cnt < 10; cnt++) { + if (lgdt3306a_fec_lock_poll(state) == LG3306_UNLOCK) { + dbg_info("no fec lock!\n"); + return LG3306_UNLOCK; + } + + msleep(20); + + packet_error = lgdt3306a_get_packet_error(state); + snr = lgdt3306a_calculate_snr_x100(state); + dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr); + + if ((snr >= 1500) && (packet_error < 0xff)) + return LG3306_LOCK; + } + + dbg_info("not locked!\n"); + return LG3306_UNLOCK; +} + +static int lgdt3306a_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u16 strength = 0; + int ret = 0; + + if (fe->ops.tuner_ops.get_rf_strength) { + ret = fe->ops.tuner_ops.get_rf_strength(fe, &strength); + if (ret == 0) + dbg_info("strength=%d\n", strength); + else + dbg_info("fe->ops.tuner_ops.get_rf_strength() failed\n"); + } + + *status = 0; + if (lgdt3306a_neverlock_poll(state) == LG3306_NL_LOCK) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + + switch (state->current_modulation) { + case QAM_256: + case QAM_64: + case QAM_AUTO: + if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) { + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + + *status |= FE_HAS_LOCK; + } + break; + case VSB_8: + if (lgdt3306a_vsb_lock_poll(state) == LG3306_LOCK) { + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + + *status |= FE_HAS_LOCK; + + ret = lgdt3306a_monitor_vsb(state); + } + break; + default: + ret = -EINVAL; + } + + if (*status & FE_HAS_SYNC) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = lgdt3306a_calculate_snr_x100(state) * 10; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + } + return ret; +} + + +static int lgdt3306a_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + state->snr = lgdt3306a_calculate_snr_x100(state); + /* report SNR in dB * 10 */ + *snr = state->snr/10; + + return 0; +} + +static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + /* + * Calculate some sort of "strength" from SNR + */ + struct lgdt3306a_state *state = fe->demodulator_priv; + u8 val; + u16 snr; /* snr_x10 */ + int ret; + u32 ref_snr; /* snr*100 */ + u32 str; + + *strength = 0; + + switch (state->current_modulation) { + case VSB_8: + ref_snr = 1600; /* 16dB */ + break; + case QAM_64: + case QAM_256: + case QAM_AUTO: + /* need to know actual modulation to set proper SNR baseline */ + ret = lgdt3306a_read_reg(state, 0x00a6, &val); + if (lg_chkerr(ret)) + goto fail; + + if(val & 0x04) + ref_snr = 2800; /* QAM-256 28dB */ + else + ref_snr = 2200; /* QAM-64 22dB */ + break; + default: + return -EINVAL; + } + + ret = fe->ops.read_snr(fe, &snr); + if (lg_chkerr(ret)) + goto fail; + + if (state->snr <= (ref_snr - 100)) + str = 0; + else if (state->snr <= ref_snr) + str = (0xffff * 65) / 100; /* 65% */ + else { + str = state->snr - ref_snr; + str /= 50; + str += 78; /* 78%-100% */ + if (str > 100) + str = 100; + str = (0xffff * str) / 100; + } + *strength = (u16)str; + dbg_info("strength=%u\n", *strength); + +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lgdt3306a_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + u32 tmp; + + *ber = 0; +#if 1 + /* FGR - FIXME - I don't know what value is expected by dvb_core + * what is the scale of the value?? */ + tmp = read_reg(state, 0x00fc); /* NBERVALUE[24-31] */ + tmp = (tmp << 8) | read_reg(state, 0x00fd); /* NBERVALUE[16-23] */ + tmp = (tmp << 8) | read_reg(state, 0x00fe); /* NBERVALUE[8-15] */ + tmp = (tmp << 8) | read_reg(state, 0x00ff); /* NBERVALUE[0-7] */ + *ber = tmp; + dbg_info("ber=%u\n", tmp); +#endif + return 0; +} + +static int lgdt3306a_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + *ucblocks = 0; +#if 1 + /* FGR - FIXME - I don't know what value is expected by dvb_core + * what happens when value wraps? */ + *ucblocks = read_reg(state, 0x00f4); /* TPIFTPERRCNT[0-7] */ + dbg_info("ucblocks=%u\n", *ucblocks); +#endif + + return 0; +} + +static int lgdt3306a_tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, unsigned int *delay, + enum fe_status *status) +{ + int ret = 0; + struct lgdt3306a_state *state = fe->demodulator_priv; + + dbg_info("re_tune=%u\n", re_tune); + + if (re_tune) { + state->current_frequency = -1; /* force re-tune */ + ret = lgdt3306a_set_parameters(fe); + if (ret != 0) + return ret; + } + *delay = 125; + ret = lgdt3306a_read_status(fe, status); + + return ret; +} + +static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 100; + dbg_info("\n"); + return 0; +} + +static enum dvbfe_search lgdt3306a_search(struct dvb_frontend *fe) +{ + enum fe_status status = 0; + int ret; + + /* set frontend */ + ret = lgdt3306a_set_parameters(fe); + if (ret) + goto error; + + ret = lgdt3306a_read_status(fe, &status); + if (ret) + goto error; + + /* check if we have a valid signal */ + if (status & FE_HAS_LOCK) + return DVBFE_ALGO_SEARCH_SUCCESS; + else + return DVBFE_ALGO_SEARCH_AGAIN; + +error: + dbg_info("failed (%d)\n", ret); + return DVBFE_ALGO_SEARCH_ERROR; +} + +static void lgdt3306a_release(struct dvb_frontend *fe) +{ + struct lgdt3306a_state *state = fe->demodulator_priv; + + dbg_info("\n"); + kfree(state); +} + +static const struct dvb_frontend_ops lgdt3306a_ops; + +struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, + struct i2c_adapter *i2c_adap) +{ + struct lgdt3306a_state *state = NULL; + int ret; + u8 val; + + dbg_info("(%d-%04x)\n", + i2c_adap ? i2c_adapter_id(i2c_adap) : 0, + config ? config->i2c_addr : 0); + + state = kzalloc(sizeof(struct lgdt3306a_state), GFP_KERNEL); + if (state == NULL) + goto fail; + + state->cfg = config; + state->i2c_adap = i2c_adap; + + memcpy(&state->frontend.ops, &lgdt3306a_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* verify that we're talking to a lg3306a */ + /* FGR - NOTE - there is no obvious ChipId to check; we check + * some "known" bits after reset, but it's still just a guess */ + ret = lgdt3306a_read_reg(state, 0x0000, &val); + if (lg_chkerr(ret)) + goto fail; + if ((val & 0x74) != 0x74) { + pr_warn("expected 0x74, got 0x%x\n", (val & 0x74)); +#if 0 + /* FIXME - re-enable when we know this is right */ + goto fail; +#endif + } + ret = lgdt3306a_read_reg(state, 0x0001, &val); + if (lg_chkerr(ret)) + goto fail; + if ((val & 0xf6) != 0xc6) { + pr_warn("expected 0xc6, got 0x%x\n", (val & 0xf6)); +#if 0 + /* FIXME - re-enable when we know this is right */ + goto fail; +#endif + } + ret = lgdt3306a_read_reg(state, 0x0002, &val); + if (lg_chkerr(ret)) + goto fail; + if ((val & 0x73) != 0x03) { + pr_warn("expected 0x03, got 0x%x\n", (val & 0x73)); +#if 0 + /* FIXME - re-enable when we know this is right */ + goto fail; +#endif + } + + state->current_frequency = -1; + state->current_modulation = -1; + + lgdt3306a_sleep(state); + + return &state->frontend; + +fail: + pr_warn("unable to detect LGDT3306A hardware\n"); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(lgdt3306a_attach); + +#ifdef DBG_DUMP + +static const short regtab[] = { + 0x0000, /* SOFTRSTB 1'b1 1'b1 1'b1 ADCPDB 1'b1 PLLPDB GBBPDB 11111111 */ + 0x0001, /* 1'b1 1'b1 1'b0 1'b0 AUTORPTRS */ + 0x0002, /* NI2CRPTEN 1'b0 1'b0 1'b0 SPECINVAUT */ + 0x0003, /* AGCRFOUT */ + 0x0004, /* ADCSEL1V ADCCNT ADCCNF ADCCNS ADCCLKPLL */ + 0x0005, /* PLLINDIVSE */ + 0x0006, /* PLLCTRL[7:0] 11100001 */ + 0x0007, /* SYSINITWAITTIME[7:0] (msec) 00001000 */ + 0x0008, /* STDOPMODE[7:0] 10000000 */ + 0x0009, /* 1'b0 1'b0 1'b0 STDOPDETTMODE[2:0] STDOPDETCMODE[1:0] 00011110 */ + 0x000a, /* DAFTEN 1'b1 x x SCSYSLOCK */ + 0x000b, /* SCSYSLOCKCHKTIME[7:0] (10msec) 01100100 */ + 0x000d, /* x SAMPLING4 */ + 0x000e, /* SAMFREQ[15:8] 00000000 */ + 0x000f, /* SAMFREQ[7:0] 00000000 */ + 0x0010, /* IFFREQ[15:8] 01100000 */ + 0x0011, /* IFFREQ[7:0] 00000000 */ + 0x0012, /* AGCEN AGCREFMO */ + 0x0013, /* AGCRFFIXB AGCIFFIXB AGCLOCKDETRNGSEL[1:0] 1'b1 1'b0 1'b0 1'b0 11101000 */ + 0x0014, /* AGCFIXVALUE[7:0] 01111111 */ + 0x0015, /* AGCREF[15:8] 00001010 */ + 0x0016, /* AGCREF[7:0] 11100100 */ + 0x0017, /* AGCDELAY[7:0] 00100000 */ + 0x0018, /* AGCRFBW[3:0] AGCIFBW[3:0] 10001000 */ + 0x0019, /* AGCUDOUTMODE[1:0] AGCUDCTRLLEN[1:0] AGCUDCTRL */ + 0x001c, /* 1'b1 PFEN MFEN AICCVSYNC */ + 0x001d, /* 1'b0 1'b1 1'b0 1'b1 AICCVSYNC */ + 0x001e, /* AICCALPHA[3:0] 1'b1 1'b0 1'b1 1'b0 01111010 */ + 0x001f, /* AICCDETTH[19:16] AICCOFFTH[19:16] 00000000 */ + 0x0020, /* AICCDETTH[15:8] 01111100 */ + 0x0021, /* AICCDETTH[7:0] 00000000 */ + 0x0022, /* AICCOFFTH[15:8] 00000101 */ + 0x0023, /* AICCOFFTH[7:0] 11100000 */ + 0x0024, /* AICCOPMODE3[1:0] AICCOPMODE2[1:0] AICCOPMODE1[1:0] AICCOPMODE0[1:0] 00000000 */ + 0x0025, /* AICCFIXFREQ3[23:16] 00000000 */ + 0x0026, /* AICCFIXFREQ3[15:8] 00000000 */ + 0x0027, /* AICCFIXFREQ3[7:0] 00000000 */ + 0x0028, /* AICCFIXFREQ2[23:16] 00000000 */ + 0x0029, /* AICCFIXFREQ2[15:8] 00000000 */ + 0x002a, /* AICCFIXFREQ2[7:0] 00000000 */ + 0x002b, /* AICCFIXFREQ1[23:16] 00000000 */ + 0x002c, /* AICCFIXFREQ1[15:8] 00000000 */ + 0x002d, /* AICCFIXFREQ1[7:0] 00000000 */ + 0x002e, /* AICCFIXFREQ0[23:16] 00000000 */ + 0x002f, /* AICCFIXFREQ0[15:8] 00000000 */ + 0x0030, /* AICCFIXFREQ0[7:0] 00000000 */ + 0x0031, /* 1'b0 1'b1 1'b0 1'b0 x DAGC1STER */ + 0x0032, /* DAGC1STEN DAGC1STER */ + 0x0033, /* DAGC1STREF[15:8] 00001010 */ + 0x0034, /* DAGC1STREF[7:0] 11100100 */ + 0x0035, /* DAGC2NDE */ + 0x0036, /* DAGC2NDREF[15:8] 00001010 */ + 0x0037, /* DAGC2NDREF[7:0] 10000000 */ + 0x0038, /* DAGC2NDLOCKDETRNGSEL[1:0] */ + 0x003d, /* 1'b1 SAMGEARS */ + 0x0040, /* SAMLFGMA */ + 0x0041, /* SAMLFBWM */ + 0x0044, /* 1'b1 CRGEARSHE */ + 0x0045, /* CRLFGMAN */ + 0x0046, /* CFLFBWMA */ + 0x0047, /* CRLFGMAN */ + 0x0048, /* x x x x CRLFGSTEP_VS[3:0] xxxx1001 */ + 0x0049, /* CRLFBWMA */ + 0x004a, /* CRLFBWMA */ + 0x0050, /* 1'b0 1'b1 1'b1 1'b0 MSECALCDA */ + 0x0070, /* TPOUTEN TPIFEN TPCLKOUTE */ + 0x0071, /* TPSENB TPSSOPBITE */ + 0x0073, /* TP47HINS x x CHBERINT PERMODE[1:0] PERINT[1:0] 1xx11100 */ + 0x0075, /* x x x x x IQSWAPCTRL[2:0] xxxxx000 */ + 0x0076, /* NBERCON NBERST NBERPOL NBERWSYN */ + 0x0077, /* x NBERLOSTTH[2:0] NBERACQTH[3:0] x0000000 */ + 0x0078, /* NBERPOLY[31:24] 00000000 */ + 0x0079, /* NBERPOLY[23:16] 00000000 */ + 0x007a, /* NBERPOLY[15:8] 00000000 */ + 0x007b, /* NBERPOLY[7:0] 00000000 */ + 0x007c, /* NBERPED[31:24] 00000000 */ + 0x007d, /* NBERPED[23:16] 00000000 */ + 0x007e, /* NBERPED[15:8] 00000000 */ + 0x007f, /* NBERPED[7:0] 00000000 */ + 0x0080, /* x AGCLOCK DAGCLOCK SYSLOCK x x NEVERLOCK[1:0] */ + 0x0085, /* SPECINVST */ + 0x0088, /* SYSLOCKTIME[15:8] */ + 0x0089, /* SYSLOCKTIME[7:0] */ + 0x008c, /* FECLOCKTIME[15:8] */ + 0x008d, /* FECLOCKTIME[7:0] */ + 0x008e, /* AGCACCOUT[15:8] */ + 0x008f, /* AGCACCOUT[7:0] */ + 0x0090, /* AICCREJSTATUS[3:0] AICCREJBUSY[3:0] */ + 0x0091, /* AICCVSYNC */ + 0x009c, /* CARRFREQOFFSET[15:8] */ + 0x009d, /* CARRFREQOFFSET[7:0] */ + 0x00a1, /* SAMFREQOFFSET[23:16] */ + 0x00a2, /* SAMFREQOFFSET[15:8] */ + 0x00a3, /* SAMFREQOFFSET[7:0] */ + 0x00a6, /* SYNCLOCK SYNCLOCKH */ +#if 0 /* covered elsewhere */ + 0x00e8, /* CONSTPWR[15:8] */ + 0x00e9, /* CONSTPWR[7:0] */ + 0x00ea, /* BMSE[15:8] */ + 0x00eb, /* BMSE[7:0] */ + 0x00ec, /* MSE[15:8] */ + 0x00ed, /* MSE[7:0] */ + 0x00ee, /* CONSTI[7:0] */ + 0x00ef, /* CONSTQ[7:0] */ +#endif + 0x00f4, /* TPIFTPERRCNT[7:0] */ + 0x00f5, /* TPCORREC */ + 0x00f6, /* VBBER[15:8] */ + 0x00f7, /* VBBER[7:0] */ + 0x00f8, /* VABER[15:8] */ + 0x00f9, /* VABER[7:0] */ + 0x00fa, /* TPERRCNT[7:0] */ + 0x00fb, /* NBERLOCK x x x x x x x */ + 0x00fc, /* NBERVALUE[31:24] */ + 0x00fd, /* NBERVALUE[23:16] */ + 0x00fe, /* NBERVALUE[15:8] */ + 0x00ff, /* NBERVALUE[7:0] */ + 0x1000, /* 1'b0 WODAGCOU */ + 0x1005, /* x x 1'b1 1'b1 x SRD_Q_QM */ + 0x1009, /* SRDWAITTIME[7:0] (10msec) 00100011 */ + 0x100a, /* SRDWAITTIME_CQS[7:0] (msec) 01100100 */ + 0x101a, /* x 1'b1 1'b0 1'b0 x QMDQAMMODE[2:0] x100x010 */ + 0x1036, /* 1'b0 1'b1 1'b0 1'b0 SAMGSEND_CQS[3:0] 01001110 */ + 0x103c, /* SAMGSAUTOSTL_V[3:0] SAMGSAUTOEDL_V[3:0] 01000110 */ + 0x103d, /* 1'b1 1'b1 SAMCNORMBP_V[1:0] 1'b0 1'b0 SAMMODESEL_V[1:0] 11100001 */ + 0x103f, /* SAMZTEDSE */ + 0x105d, /* EQSTATUSE */ + 0x105f, /* x PMAPG2_V[2:0] x DMAPG2_V[2:0] x001x011 */ + 0x1060, /* 1'b1 EQSTATUSE */ + 0x1061, /* CRMAPBWSTL_V[3:0] CRMAPBWEDL_V[3:0] 00000100 */ + 0x1065, /* 1'b0 x CRMODE_V[1:0] 1'b1 x 1'b1 x 0x111x1x */ + 0x1066, /* 1'b0 1'b0 1'b1 1'b0 1'b1 PNBOOSTSE */ + 0x1068, /* CREPHNGAIN2_V[3:0] CREPHNPBW_V[3:0] 10010001 */ + 0x106e, /* x x x x x CREPHNEN_ */ + 0x106f, /* CREPHNTH_V[7:0] 00010101 */ + 0x1072, /* CRSWEEPN */ + 0x1073, /* CRPGAIN_V[3:0] x x 1'b1 1'b1 1001xx11 */ + 0x1074, /* CRPBW_V[3:0] x x 1'b1 1'b1 0001xx11 */ + 0x1080, /* DAFTSTATUS[1:0] x x x x x x */ + 0x1081, /* SRDSTATUS[1:0] x x x x x SRDLOCK */ + 0x10a9, /* EQSTATUS_CQS[1:0] x x x x x x */ + 0x10b7, /* EQSTATUS_V[1:0] x x x x x x */ +#if 0 /* SMART_ANT */ + 0x1f00, /* MODEDETE */ + 0x1f01, /* x x x x x x x SFNRST xxxxxxx0 */ + 0x1f03, /* NUMOFANT[7:0] 10000000 */ + 0x1f04, /* x SELMASK[6:0] x0000000 */ + 0x1f05, /* x SETMASK[6:0] x0000000 */ + 0x1f06, /* x TXDATA[6:0] x0000000 */ + 0x1f07, /* x CHNUMBER[6:0] x0000000 */ + 0x1f09, /* AGCTIME[23:16] 10011000 */ + 0x1f0a, /* AGCTIME[15:8] 10010110 */ + 0x1f0b, /* AGCTIME[7:0] 10000000 */ + 0x1f0c, /* ANTTIME[31:24] 00000000 */ + 0x1f0d, /* ANTTIME[23:16] 00000011 */ + 0x1f0e, /* ANTTIME[15:8] 10010000 */ + 0x1f0f, /* ANTTIME[7:0] 10010000 */ + 0x1f11, /* SYNCTIME[23:16] 10011000 */ + 0x1f12, /* SYNCTIME[15:8] 10010110 */ + 0x1f13, /* SYNCTIME[7:0] 10000000 */ + 0x1f14, /* SNRTIME[31:24] 00000001 */ + 0x1f15, /* SNRTIME[23:16] 01111101 */ + 0x1f16, /* SNRTIME[15:8] 01111000 */ + 0x1f17, /* SNRTIME[7:0] 01000000 */ + 0x1f19, /* FECTIME[23:16] 00000000 */ + 0x1f1a, /* FECTIME[15:8] 01110010 */ + 0x1f1b, /* FECTIME[7:0] 01110000 */ + 0x1f1d, /* FECTHD[7:0] 00000011 */ + 0x1f1f, /* SNRTHD[23:16] 00001000 */ + 0x1f20, /* SNRTHD[15:8] 01111111 */ + 0x1f21, /* SNRTHD[7:0] 10000101 */ + 0x1f80, /* IRQFLG x x SFSDRFLG MODEBFLG SAVEFLG SCANFLG TRACKFLG */ + 0x1f81, /* x SYNCCON SNRCON FECCON x STDBUSY SYNCRST AGCFZCO */ + 0x1f82, /* x x x SCANOPCD[4:0] */ + 0x1f83, /* x x x x MAINOPCD[3:0] */ + 0x1f84, /* x x RXDATA[13:8] */ + 0x1f85, /* RXDATA[7:0] */ + 0x1f86, /* x x SDTDATA[13:8] */ + 0x1f87, /* SDTDATA[7:0] */ + 0x1f89, /* ANTSNR[23:16] */ + 0x1f8a, /* ANTSNR[15:8] */ + 0x1f8b, /* ANTSNR[7:0] */ + 0x1f8c, /* x x x x ANTFEC[13:8] */ + 0x1f8d, /* ANTFEC[7:0] */ + 0x1f8e, /* MAXCNT[7:0] */ + 0x1f8f, /* SCANCNT[7:0] */ + 0x1f91, /* MAXPW[23:16] */ + 0x1f92, /* MAXPW[15:8] */ + 0x1f93, /* MAXPW[7:0] */ + 0x1f95, /* CURPWMSE[23:16] */ + 0x1f96, /* CURPWMSE[15:8] */ + 0x1f97, /* CURPWMSE[7:0] */ +#endif /* SMART_ANT */ + 0x211f, /* 1'b1 1'b1 1'b1 CIRQEN x x 1'b0 1'b0 1111xx00 */ + 0x212a, /* EQAUTOST */ + 0x2122, /* CHFAST[7:0] 01100000 */ + 0x212b, /* FFFSTEP_V[3:0] x FBFSTEP_V[2:0] 0001x001 */ + 0x212c, /* PHDEROTBWSEL[3:0] 1'b1 1'b1 1'b1 1'b0 10001110 */ + 0x212d, /* 1'b1 1'b1 1'b1 1'b1 x x TPIFLOCKS */ + 0x2135, /* DYNTRACKFDEQ[3:0] x 1'b0 1'b0 1'b0 1010x000 */ + 0x2141, /* TRMODE[1:0] 1'b1 1'b1 1'b0 1'b1 1'b1 1'b1 01110111 */ + 0x2162, /* AICCCTRLE */ + 0x2173, /* PHNCNFCNT[7:0] 00000100 */ + 0x2179, /* 1'b0 1'b0 1'b0 1'b1 x BADSINGLEDYNTRACKFBF[2:0] 0001x001 */ + 0x217a, /* 1'b0 1'b0 1'b0 1'b1 x BADSLOWSINGLEDYNTRACKFBF[2:0] 0001x001 */ + 0x217e, /* CNFCNTTPIF[7:0] 00001000 */ + 0x217f, /* TPERRCNTTPIF[7:0] 00000001 */ + 0x2180, /* x x x x x x FBDLYCIR[9:8] */ + 0x2181, /* FBDLYCIR[7:0] */ + 0x2185, /* MAXPWRMAIN[7:0] */ + 0x2191, /* NCOMBDET x x x x x x x */ + 0x2199, /* x MAINSTRON */ + 0x219a, /* FFFEQSTEPOUT_V[3:0] FBFSTEPOUT_V[2:0] */ + 0x21a1, /* x x SNRREF[5:0] */ + 0x2845, /* 1'b0 1'b1 x x FFFSTEP_CQS[1:0] FFFCENTERTAP[1:0] 01xx1110 */ + 0x2846, /* 1'b0 x 1'b0 1'b1 FBFSTEP_CQS[1:0] 1'b1 1'b0 0x011110 */ + 0x2847, /* ENNOSIGDE */ + 0x2849, /* 1'b1 1'b1 NOUSENOSI */ + 0x284a, /* EQINITWAITTIME[7:0] 01100100 */ + 0x3000, /* 1'b1 1'b1 1'b1 x x x 1'b0 RPTRSTM */ + 0x3001, /* RPTRSTWAITTIME[7:0] (100msec) 00110010 */ + 0x3031, /* FRAMELOC */ + 0x3032, /* 1'b1 1'b0 1'b0 1'b0 x x FRAMELOCKMODE_CQS[1:0] 1000xx11 */ + 0x30a9, /* VDLOCK_Q FRAMELOCK */ + 0x30aa, /* MPEGLOCK */ +}; + +#define numDumpRegs (ARRAY_SIZE(regtab)) +static u8 regval1[numDumpRegs] = {0, }; +static u8 regval2[numDumpRegs] = {0, }; + +static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state) +{ + memset(regval2, 0xff, sizeof(regval2)); + lgdt3306a_DumpRegs(state); +} + +static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state) +{ + int i; + int sav_debug = debug; + + if ((debug & DBG_DUMP) == 0) + return; + debug &= ~DBG_REG; /* suppress DBG_REG during reg dump */ + + lg_debug("\n"); + + for (i = 0; i < numDumpRegs; i++) { + lgdt3306a_read_reg(state, regtab[i], ®val1[i]); + if (regval1[i] != regval2[i]) { + lg_debug(" %04X = %02X\n", regtab[i], regval1[i]); + regval2[i] = regval1[i]; + } + } + debug = sav_debug; +} +#endif /* DBG_DUMP */ + + + +static const struct dvb_frontend_ops lgdt3306a_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "LG Electronics LGDT3306A VSB/QAM Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl, + .init = lgdt3306a_init, + .sleep = lgdt3306a_fe_sleep, + /* if this is set, it overrides the default swzigzag */ + .tune = lgdt3306a_tune, + .set_frontend = lgdt3306a_set_parameters, + .get_frontend = lgdt3306a_get_frontend, + .get_frontend_algo = lgdt3306a_get_frontend_algo, + .get_tune_settings = lgdt3306a_get_tune_settings, + .read_status = lgdt3306a_read_status, + .read_ber = lgdt3306a_read_ber, + .read_signal_strength = lgdt3306a_read_signal_strength, + .read_snr = lgdt3306a_read_snr, + .read_ucblocks = lgdt3306a_read_ucblocks, + .release = lgdt3306a_release, + .ts_bus_ctrl = lgdt3306a_ts_bus_ctrl, + .search = lgdt3306a_search, +}; + +static int lgdt3306a_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + struct lgdt3306a_state *state = i2c_get_clientdata(client); + + return lgdt3306a_i2c_gate_ctrl(&state->frontend, 1); +} + +static int lgdt3306a_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + struct lgdt3306a_state *state = i2c_get_clientdata(client); + + return lgdt3306a_i2c_gate_ctrl(&state->frontend, 0); +} + +static int lgdt3306a_probe(struct i2c_client *client) +{ + struct lgdt3306a_config *config; + struct lgdt3306a_state *state; + struct dvb_frontend *fe; + int ret; + + config = kmemdup(client->dev.platform_data, + sizeof(struct lgdt3306a_config), GFP_KERNEL); + if (config == NULL) { + ret = -ENOMEM; + goto fail; + } + + config->i2c_addr = client->addr; + fe = lgdt3306a_attach(config, client->adapter); + if (fe == NULL) { + ret = -ENODEV; + goto err_fe; + } + + i2c_set_clientdata(client, fe->demodulator_priv); + state = fe->demodulator_priv; + state->frontend.ops.release = NULL; + + /* create mux i2c adapter for tuner */ + state->muxc = i2c_mux_alloc(client->adapter, &client->dev, + 1, 0, I2C_MUX_LOCKED, + lgdt3306a_select, lgdt3306a_deselect); + if (!state->muxc) { + ret = -ENOMEM; + goto err_kfree; + } + state->muxc->priv = client; + ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0); + if (ret) + goto err_kfree; + + /* create dvb_frontend */ + fe->ops.i2c_gate_ctrl = NULL; + *config->i2c_adapter = state->muxc->adapter[0]; + *config->fe = fe; + + dev_info(&client->dev, "LG Electronics LGDT3306A successfully identified\n"); + + return 0; + +err_kfree: + kfree(state); +err_fe: + kfree(config); +fail: + dev_warn(&client->dev, "probe failed = %d\n", ret); + return ret; +} + +static void lgdt3306a_remove(struct i2c_client *client) +{ + struct lgdt3306a_state *state = i2c_get_clientdata(client); + + i2c_mux_del_adapters(state->muxc); + + state->frontend.ops.release = NULL; + state->frontend.demodulator_priv = NULL; + + kfree(state->cfg); + kfree(state); +} + +static const struct i2c_device_id lgdt3306a_id_table[] = { + {"lgdt3306a", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table); + +static struct i2c_driver lgdt3306a_driver = { + .driver = { + .name = "lgdt3306a", + .suppress_bind_attrs = true, + }, + .probe = lgdt3306a_probe, + .remove = lgdt3306a_remove, + .id_table = lgdt3306a_id_table, +}; + +module_i2c_driver(lgdt3306a_driver); + +MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver"); +MODULE_AUTHOR("Fred Richter <frichter@hauppauge.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); diff --git a/drivers/media/dvb-frontends/lgdt3306a.h b/drivers/media/dvb-frontends/lgdt3306a.h new file mode 100644 index 0000000000..407e74b5d1 --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt3306a.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for LGDT3306A - 8VSB/QAM-B + * + * Copyright (C) 2013,2014 Fred Richter <frichter@hauppauge.com> + * based on lgdt3305.[ch] by Michael Krufky + */ + +#ifndef _LGDT3306A_H_ +#define _LGDT3306A_H_ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + + +enum lgdt3306a_mpeg_mode { + LGDT3306A_MPEG_PARALLEL = 0, + LGDT3306A_MPEG_SERIAL = 1, +}; + +enum lgdt3306a_tp_clock_edge { + LGDT3306A_TPCLK_RISING_EDGE = 0, + LGDT3306A_TPCLK_FALLING_EDGE = 1, +}; + +enum lgdt3306a_tp_valid_polarity { + LGDT3306A_TP_VALID_LOW = 0, + LGDT3306A_TP_VALID_HIGH = 1, +}; + +struct lgdt3306a_config { + u8 i2c_addr; + + /* user defined IF frequency in KHz */ + u16 qam_if_khz; + u16 vsb_if_khz; + + /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ + unsigned int deny_i2c_rptr:1; + + /* spectral inversion - 0:disabled 1:enabled */ + unsigned int spectral_inversion:1; + + enum lgdt3306a_mpeg_mode mpeg_mode; + enum lgdt3306a_tp_clock_edge tpclk_edge; + enum lgdt3306a_tp_valid_polarity tpvalid_polarity; + + /* demod clock freq in MHz; 24 or 25 supported */ + int xtalMHz; + + /* returned by driver if using i2c bus multiplexing */ + struct dvb_frontend **fe; + struct i2c_adapter **i2c_adapter; +}; + +#if IS_REACHABLE(CONFIG_DVB_LGDT3306A) +struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, + struct i2c_adapter *i2c_adap); +#else +static inline +struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, + struct i2c_adapter *i2c_adap) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGDT3306A */ + +#endif /* _LGDT3306A_H_ */ diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c new file mode 100644 index 0000000000..081d6ad3ce --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for LGDT3302 and LGDT3303 - VSB/QAM + * + * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net> + */ + +/* + * NOTES ABOUT THIS DRIVER + * + * This Linux driver supports: + * DViCO FusionHDTV 3 Gold-Q + * DViCO FusionHDTV 3 Gold-T + * DViCO FusionHDTV 5 Gold + * DViCO FusionHDTV 5 Lite + * DViCO FusionHDTV 5 USB Gold + * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) + * pcHDTV HD5500 + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <asm/byteorder.h> + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "lgdt330x_priv.h" +#include "lgdt330x.h" + +/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */ +/* #define USE_EQMSE */ + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off lgdt330x frontend debugging (default:off)."); + +#define dprintk(state, fmt, arg...) do { \ + if (debug) \ + dev_printk(KERN_DEBUG, &state->client->dev, fmt, ##arg);\ +} while (0) + +struct lgdt330x_state { + struct i2c_client *client; + + /* Configuration settings */ + struct lgdt330x_config config; + + struct dvb_frontend frontend; + + /* Demodulator private data */ + enum fe_modulation current_modulation; + u32 snr; /* Result of last SNR calculation */ + u16 ucblocks; + unsigned long last_stats_time; + + /* Tuner private data */ + u32 current_frequency; +}; + +static int i2c_write_demod_bytes(struct lgdt330x_state *state, + const u8 *buf, /* data bytes to send */ + int len /* number of bytes to send */) +{ + int i; + int err; + + for (i = 0; i < len - 1; i += 2) { + err = i2c_master_send(state->client, buf, 2); + if (err != 2) { + dev_warn(&state->client->dev, + "%s: error (addr %02x <- %02x, err = %i)\n", + __func__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + buf += 2; + } + return 0; +} + +/* + * This routine writes the register (reg) to the demod bus + * then reads the data returned for (len) bytes. + */ +static int i2c_read_demod_bytes(struct lgdt330x_state *state, + enum I2C_REG reg, u8 *buf, int len) +{ + u8 wr[] = { reg }; + struct i2c_msg msg[] = { + { + .addr = state->client->addr, + .flags = 0, + .buf = wr, + .len = 1 + }, { + .addr = state->client->addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len + }, + }; + int ret; + + ret = i2c_transfer(state->client->adapter, msg, 2); + if (ret != 2) { + dev_warn(&state->client->dev, + "%s: addr 0x%02x select 0x%02x error (ret == %i)\n", + __func__, state->client->addr, reg, ret); + if (ret >= 0) + ret = -EIO; + } else { + ret = 0; + } + return ret; +} + +/* Software reset */ +static int lgdt3302_sw_reset(struct lgdt330x_state *state) +{ + u8 ret; + u8 reset[] = { + IRQ_MASK, + /* + * bit 6 is active low software reset + * bits 5-0 are 1 to mask interrupts + */ + 0x00 + }; + + ret = i2c_write_demod_bytes(state, + reset, sizeof(reset)); + if (ret == 0) { + /* force reset high (inactive) and unmask interrupts */ + reset[1] = 0x7f; + ret = i2c_write_demod_bytes(state, + reset, sizeof(reset)); + } + return ret; +} + +static int lgdt3303_sw_reset(struct lgdt330x_state *state) +{ + u8 ret; + u8 reset[] = { + 0x02, + 0x00 /* bit 0 is active low software reset */ + }; + + ret = i2c_write_demod_bytes(state, + reset, sizeof(reset)); + if (ret == 0) { + /* force reset high (inactive) */ + reset[1] = 0x01; + ret = i2c_write_demod_bytes(state, + reset, sizeof(reset)); + } + return ret; +} + +static int lgdt330x_sw_reset(struct lgdt330x_state *state) +{ + switch (state->config.demod_chip) { + case LGDT3302: + return lgdt3302_sw_reset(state); + case LGDT3303: + return lgdt3303_sw_reset(state); + default: + return -ENODEV; + } +} + +static int lgdt330x_init(struct dvb_frontend *fe) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + char *chip_name; + int err; + /* + * Array of byte pairs <address, value> + * to initialize each different chip + */ + static const u8 lgdt3302_init_data[] = { + /* Use 50MHz param values from spec sheet since xtal is 50 */ + /* + * Change the value of NCOCTFV[25:0] of carrier + * recovery center frequency register + */ + VSB_CARRIER_FREQ0, 0x00, + VSB_CARRIER_FREQ1, 0x87, + VSB_CARRIER_FREQ2, 0x8e, + VSB_CARRIER_FREQ3, 0x01, + /* + * Change the TPCLK pin polarity + * data is valid on falling clock + */ + DEMUX_CONTROL, 0xfb, + /* + * Change the value of IFBW[11:0] of + * AGC IF/RF loop filter bandwidth register + */ + AGC_RF_BANDWIDTH0, 0x40, + AGC_RF_BANDWIDTH1, 0x93, + AGC_RF_BANDWIDTH2, 0x00, + /* + * Change the value of bit 6, 'nINAGCBY' and + * 'NSSEL[1:0] of ACG function control register 2 + */ + AGC_FUNC_CTRL2, 0xc6, + /* + * Change the value of bit 6 'RFFIX' + * of AGC function control register 3 + */ + AGC_FUNC_CTRL3, 0x40, + /* + * Set the value of 'INLVTHD' register 0x2a/0x2c + * to 0x7fe + */ + AGC_DELAY0, 0x07, + AGC_DELAY2, 0xfe, + /* + * Change the value of IAGCBW[15:8] + * of inner AGC loop filter bandwidth + */ + AGC_LOOP_BANDWIDTH0, 0x08, + AGC_LOOP_BANDWIDTH1, 0x9a + }; + static const u8 lgdt3303_init_data[] = { + 0x4c, 0x14 + }; + static const u8 flip_1_lgdt3303_init_data[] = { + 0x4c, 0x14, + 0x87, 0xf3 + }; + static const u8 flip_2_lgdt3303_init_data[] = { + 0x4c, 0x14, + 0x87, 0xda + }; + + /* + * Hardware reset is done using gpio[0] of cx23880x chip. + * I'd like to do it here, but don't know how to find chip address. + * cx88-cards.c arranges for the reset bit to be inactive (high). + * Maybe there needs to be a callable function in cx88-core or + * the caller of this function needs to do it. + */ + + switch (state->config.demod_chip) { + case LGDT3302: + chip_name = "LGDT3302"; + err = i2c_write_demod_bytes(state, lgdt3302_init_data, + sizeof(lgdt3302_init_data)); + break; + case LGDT3303: + chip_name = "LGDT3303"; + switch (state->config.clock_polarity_flip) { + case 2: + err = i2c_write_demod_bytes(state, + flip_2_lgdt3303_init_data, + sizeof(flip_2_lgdt3303_init_data)); + break; + case 1: + err = i2c_write_demod_bytes(state, + flip_1_lgdt3303_init_data, + sizeof(flip_1_lgdt3303_init_data)); + break; + case 0: + default: + err = i2c_write_demod_bytes(state, lgdt3303_init_data, + sizeof(lgdt3303_init_data)); + } + break; + default: + chip_name = "undefined"; + dev_warn(&state->client->dev, + "Only LGDT3302 and LGDT3303 are supported chips.\n"); + err = -ENODEV; + } + dprintk(state, "Initialized the %s chip\n", chip_name); + if (err < 0) + return err; + + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.len = 1; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + state->last_stats_time = 0; + + return lgdt330x_sw_reset(state); +} + +static int lgdt330x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + + *ucblocks = state->ucblocks; + + return 0; +} + +static int lgdt330x_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct lgdt330x_state *state = fe->demodulator_priv; + /* + * Array of byte pairs <address, value> + * to initialize 8VSB for lgdt3303 chip 50 MHz IF + */ + static const u8 lgdt3303_8vsb_44_data[] = { + 0x04, 0x00, + 0x0d, 0x40, + 0x0e, 0x87, + 0x0f, 0x8e, + 0x10, 0x01, + 0x47, 0x8b + }; + /* + * Array of byte pairs <address, value> + * to initialize QAM for lgdt3303 chip + */ + static const u8 lgdt3303_qam_data[] = { + 0x04, 0x00, + 0x0d, 0x00, + 0x0e, 0x00, + 0x0f, 0x00, + 0x10, 0x00, + 0x51, 0x63, + 0x47, 0x66, + 0x48, 0x66, + 0x4d, 0x1a, + 0x49, 0x08, + 0x4a, 0x9b + }; + u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 }; + + int err = 0; + /* Change only if we are actually changing the modulation */ + if (state->current_modulation != p->modulation) { + switch (p->modulation) { + case VSB_8: + dprintk(state, "VSB_8 MODE\n"); + + /* Select VSB mode */ + top_ctrl_cfg[1] = 0x03; + + /* Select ANT connector if supported by card */ + if (state->config.pll_rf_set) + state->config.pll_rf_set(fe, 1); + + if (state->config.demod_chip == LGDT3303) { + err = i2c_write_demod_bytes(state, + lgdt3303_8vsb_44_data, + sizeof(lgdt3303_8vsb_44_data)); + } + break; + + case QAM_64: + dprintk(state, "QAM_64 MODE\n"); + + /* Select QAM_64 mode */ + top_ctrl_cfg[1] = 0x00; + + /* Select CABLE connector if supported by card */ + if (state->config.pll_rf_set) + state->config.pll_rf_set(fe, 0); + + if (state->config.demod_chip == LGDT3303) { + err = i2c_write_demod_bytes(state, + lgdt3303_qam_data, + sizeof(lgdt3303_qam_data)); + } + break; + + case QAM_256: + dprintk(state, "QAM_256 MODE\n"); + + /* Select QAM_256 mode */ + top_ctrl_cfg[1] = 0x01; + + /* Select CABLE connector if supported by card */ + if (state->config.pll_rf_set) + state->config.pll_rf_set(fe, 0); + + if (state->config.demod_chip == LGDT3303) { + err = i2c_write_demod_bytes(state, + lgdt3303_qam_data, + sizeof(lgdt3303_qam_data)); + } + break; + default: + dev_warn(&state->client->dev, + "%s: Modulation type(%d) UNSUPPORTED\n", + __func__, p->modulation); + return -1; + } + if (err < 0) + dev_warn(&state->client->dev, + "%s: error blasting bytes to lgdt3303 for modulation type(%d)\n", + __func__, p->modulation); + + /* + * select serial or parallel MPEG hardware interface + * Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303 + * Parallel: 0x00 + */ + top_ctrl_cfg[1] |= state->config.serial_mpeg; + + /* Select the requested mode */ + i2c_write_demod_bytes(state, top_ctrl_cfg, + sizeof(top_ctrl_cfg)); + if (state->config.set_ts_params) + state->config.set_ts_params(fe, 0); + state->current_modulation = p->modulation; + } + + /* Tune to the specified frequency */ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Keep track of the new frequency */ + /* + * FIXME this is the wrong way to do this... + * The tuner is shared with the video4linux analog API + */ + state->current_frequency = p->frequency; + + lgdt330x_sw_reset(state); + return 0; +} + +static int lgdt330x_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + return 0; +} + +/* + * Calculate SNR estimation (scaled by 2^24) + * + * 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM + * equations from LGDT3303 datasheet. VSB is the same between the '02 + * and '03, so maybe QAM is too? Perhaps someone with a newer datasheet + * that has QAM information could verify? + * + * For 8-VSB: (two ways, take your pick) + * LGDT3302: + * SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) + * LGDT3303: + * SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) + * LGDT3302 & LGDT3303: + * SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) + * For 64-QAM: + * SNR = 10 * log10( 688128 / MSEQAM) + * For 256-QAM: + * SNR = 10 * log10( 696320 / MSEQAM) + * + * We re-write the snr equation as: + * SNR * 2^24 = 10*(c - intlog10(MSE)) + * Where for 256-QAM, c = log10(696320) * 2^24, and so on. + */ +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* No signal */ + return 0; + + mse = intlog10(mse); + if (mse > c) { + /* + * Negative SNR, which is possible, but realisticly the + * demod will lose lock before the signal gets this bad. + * The API only allows for unsigned values, so just return 0 + */ + return 0; + } + return 10 * (c - mse); +} + +static int lgdt3302_read_snr(struct dvb_frontend *fe) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ + + switch (state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -15.61 to +41.58 */ + noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; + c = 69765745; /* log10(25*24^2)*2^24 */ +#else + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ + noise = ((buf[0] & 7 << 3) << 13) | (buf[3] << 8) | buf[4]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); + noise = ((buf[0] & 3) << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + dev_err(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + + state->snr = 0; + + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ + } + + state->snr = calculate_snr(noise, c); + + dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt3303_read_snr(struct dvb_frontend *fe) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ + + switch (state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -16.12 to +44.08 */ + noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#else + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ + noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); + noise = (buf[0] << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + dev_err(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + state->snr = 0; + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ + } + + state->snr = calculate_snr(noise, c); + + dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt330x_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ + + return 0; +} + +static int lgdt330x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + /* Calculate Strength from SNR up to 35dB */ + /* + * Even though the SNR can go higher than 35dB, there is some comfort + * factor in having a range of strong signals that can show at 100% + */ + struct lgdt330x_state *state = fe->demodulator_priv; + u16 snr; + int ret; + + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; + + return 0; +} + + +static int lgdt3302_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u8 buf[3]; + int err; + + *status = 0; /* Reset status result */ + + /* AGC status register */ + i2c_read_demod_bytes(state, AGC_STATUS, buf, 1); + dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]); + if ((buf[0] & 0x0c) == 0x8) { + /* + * Test signal does not exist flag + * as well as the AGC lock flag. + */ + *status |= FE_HAS_SIGNAL; + } + + /* + * You must set the Mask bits to 1 in the IRQ_MASK in order + * to see that status bit in the IRQ_STATUS register. + * This is done in SwReset(); + */ + + /* signal status */ + i2c_read_demod_bytes(state, TOP_CONTROL, buf, sizeof(buf)); + dprintk(state, + "TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", + buf[0], buf[1], buf[2]); + + /* sync status */ + if ((buf[2] & 0x03) == 0x01) + *status |= FE_HAS_SYNC; + + /* FEC error status */ + if ((buf[2] & 0x0c) == 0x08) + *status |= FE_HAS_LOCK | FE_HAS_VITERBI; + + /* Carrier Recovery Lock Status Register */ + i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); + dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]); + switch (state->current_modulation) { + case QAM_256: + case QAM_64: + /* Need to understand why there are 3 lock levels here */ + if ((buf[0] & 0x07) == 0x07) + *status |= FE_HAS_CARRIER; + break; + case VSB_8: + if ((buf[0] & 0x80) == 0x80) + *status |= FE_HAS_CARRIER; + break; + default: + dev_warn(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + } + + if (!(*status & FE_HAS_LOCK)) { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (state->last_stats_time && + time_is_after_jiffies(state->last_stats_time)) + return 0; + + state->last_stats_time = jiffies + msecs_to_jiffies(1000); + + err = lgdt3302_read_snr(fe); + if (!err) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24; + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1, + buf, sizeof(buf)); + if (!err) { + state->ucblocks = (buf[0] << 8) | buf[1]; + + dprintk(state, "UCB = 0x%02x\n", state->ucblocks); + + p->block_error.stat[0].uvalue += state->ucblocks; + /* FIXME: what's the basis for block count */ + p->block_count.stat[0].uvalue += 10000; + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_count.stat[0].scale = FE_SCALE_COUNTER; + } else { + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +} + +static int lgdt3303_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u8 buf[3]; + int err; + + *status = 0; /* Reset status result */ + + /* lgdt3303 AGC status register */ + err = i2c_read_demod_bytes(state, 0x58, buf, 1); + if (err < 0) + return err; + + dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]); + if ((buf[0] & 0x21) == 0x01) { + /* + * Test input signal does not exist flag + * as well as the AGC lock flag. + */ + *status |= FE_HAS_SIGNAL; + } + + /* Carrier Recovery Lock Status Register */ + i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); + dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]); + switch (state->current_modulation) { + case QAM_256: + case QAM_64: + /* Need to understand why there are 3 lock levels here */ + if ((buf[0] & 0x07) == 0x07) + *status |= FE_HAS_CARRIER; + else + break; + i2c_read_demod_bytes(state, 0x8a, buf, 1); + dprintk(state, "QAM LOCK = 0x%02x\n", buf[0]); + + if ((buf[0] & 0x04) == 0x04) + *status |= FE_HAS_SYNC; + if ((buf[0] & 0x01) == 0x01) + *status |= FE_HAS_LOCK; + if ((buf[0] & 0x08) == 0x08) + *status |= FE_HAS_VITERBI; + break; + case VSB_8: + if ((buf[0] & 0x80) == 0x80) + *status |= FE_HAS_CARRIER; + else + break; + i2c_read_demod_bytes(state, 0x38, buf, 1); + dprintk(state, "8-VSB LOCK = 0x%02x\n", buf[0]); + + if ((buf[0] & 0x02) == 0x00) + *status |= FE_HAS_SYNC; + if ((buf[0] & 0x01) == 0x01) + *status |= FE_HAS_VITERBI | FE_HAS_LOCK; + break; + default: + dev_warn(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + } + + if (!(*status & FE_HAS_LOCK)) { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (state->last_stats_time && + time_is_after_jiffies(state->last_stats_time)) + return 0; + + state->last_stats_time = jiffies + msecs_to_jiffies(1000); + + err = lgdt3303_read_snr(fe); + if (!err) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24; + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1, + buf, sizeof(buf)); + if (!err) { + state->ucblocks = (buf[0] << 8) | buf[1]; + + dprintk(state, "UCB = 0x%02x\n", state->ucblocks); + + p->block_error.stat[0].uvalue += state->ucblocks; + /* FIXME: what's the basis for block count */ + p->block_count.stat[0].uvalue += 10000; + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_count.stat[0].scale = FE_SCALE_COUNTER; + } else { + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +} + +static int +lgdt330x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fe_tune_settings) +{ + /* I have no idea about this - it may not be needed */ + fe_tune_settings->min_delay_ms = 500; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + return 0; +} + +static void lgdt330x_release(struct dvb_frontend *fe) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + + dev_dbg(&client->dev, "\n"); + + i2c_unregister_device(client); +} + +static struct dvb_frontend *lgdt330x_get_dvb_frontend(struct i2c_client *client) +{ + struct lgdt330x_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &state->frontend; +} + +static const struct dvb_frontend_ops lgdt3302_ops; +static const struct dvb_frontend_ops lgdt3303_ops; + +static int lgdt330x_probe(struct i2c_client *client) +{ + struct lgdt330x_state *state = NULL; + u8 buf[1]; + + /* Allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto error; + + /* Setup the state */ + memcpy(&state->config, client->dev.platform_data, + sizeof(state->config)); + i2c_set_clientdata(client, state); + state->client = client; + + /* Create dvb_frontend */ + switch (state->config.demod_chip) { + case LGDT3302: + memcpy(&state->frontend.ops, &lgdt3302_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LGDT3303: + memcpy(&state->frontend.ops, &lgdt3303_ops, + sizeof(struct dvb_frontend_ops)); + break; + default: + goto error; + } + state->frontend.demodulator_priv = state; + + /* Setup get frontend callback */ + state->config.get_dvb_frontend = lgdt330x_get_dvb_frontend; + + /* Verify communication with demod chip */ + if (i2c_read_demod_bytes(state, 2, buf, 1)) + goto error; + + state->current_frequency = -1; + state->current_modulation = -1; + + dev_info(&state->client->dev, + "Demod loaded for LGDT330%s chip\n", + state->config.demod_chip == LGDT3302 ? "2" : "3"); + + return 0; + +error: + kfree(state); + if (debug) + dev_printk(KERN_DEBUG, &client->dev, "Error loading lgdt330x driver\n"); + return -ENODEV; +} +struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *_config, + u8 demod_address, + struct i2c_adapter *i2c) +{ + struct i2c_client *client; + struct i2c_board_info board_info = {}; + struct lgdt330x_config config = *_config; + + strscpy(board_info.type, "lgdt330x", sizeof(board_info.type)); + board_info.addr = demod_address; + board_info.platform_data = &config; + client = i2c_new_client_device(i2c, &board_info); + if (!i2c_client_has_driver(client)) + return NULL; + + return lgdt330x_get_dvb_frontend(client); +} +EXPORT_SYMBOL_GPL(lgdt330x_attach); + +static const struct dvb_frontend_ops lgdt3302_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "LG Electronics LGDT3302 VSB/QAM Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 5056941, /* QAM 64 */ + .symbol_rate_max = 10762000, /* VSB 8 */ + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .init = lgdt330x_init, + .set_frontend = lgdt330x_set_parameters, + .get_frontend = lgdt330x_get_frontend, + .get_tune_settings = lgdt330x_get_tune_settings, + .read_status = lgdt3302_read_status, + .read_signal_strength = lgdt330x_read_signal_strength, + .read_snr = lgdt330x_read_snr, + .read_ucblocks = lgdt330x_read_ucblocks, + .release = lgdt330x_release, +}; + +static const struct dvb_frontend_ops lgdt3303_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "LG Electronics LGDT3303 VSB/QAM Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 5056941, /* QAM 64 */ + .symbol_rate_max = 10762000, /* VSB 8 */ + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .init = lgdt330x_init, + .set_frontend = lgdt330x_set_parameters, + .get_frontend = lgdt330x_get_frontend, + .get_tune_settings = lgdt330x_get_tune_settings, + .read_status = lgdt3303_read_status, + .read_signal_strength = lgdt330x_read_signal_strength, + .read_snr = lgdt330x_read_snr, + .read_ucblocks = lgdt330x_read_ucblocks, + .release = lgdt330x_release, +}; + +static void lgdt330x_remove(struct i2c_client *client) +{ + struct lgdt330x_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(state); +} + +static const struct i2c_device_id lgdt330x_id_table[] = { + {"lgdt330x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table); + +static struct i2c_driver lgdt330x_driver = { + .driver = { + .name = "lgdt330x", + .suppress_bind_attrs = true, + }, + .probe = lgdt330x_probe, + .remove = lgdt330x_remove, + .id_table = lgdt330x_id_table, +}; + +module_i2c_driver(lgdt330x_driver); + + +MODULE_DESCRIPTION("LGDT330X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); +MODULE_AUTHOR("Wilson Michaels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h new file mode 100644 index 0000000000..99834aa608 --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt330x.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for LGDT3302 and LGDT3303 - VSB/QAM + * + * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net> + */ + +#ifndef LGDT330X_H +#define LGDT330X_H + +#include <linux/dvb/frontend.h> + +typedef enum lg_chip_t { + UNDEFINED, + LGDT3302, + LGDT3303 +}lg_chip_type; + +/** + * struct lgdt330x_config - contains lgdt330x configuration + * + * @demod_chip: LG demodulator chip LGDT3302 or LGDT3303 + * @serial_mpeg: MPEG hardware interface - 0:parallel 1:serial + * @pll_rf_set: Callback function to set PLL interface + * @set_ts_params: Callback function to set device param for start_dma + * @clock_polarity_flip: + * Flip the polarity of the mpeg data transfer clock using alternate + * init data. + * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled + * @get_dvb_frontend: + * returns the frontend associated with this I2C client. + * Filled by the driver. + */ +struct lgdt330x_config +{ + lg_chip_type demod_chip; + int serial_mpeg; + int (*pll_rf_set) (struct dvb_frontend* fe, int index); + int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); + int clock_polarity_flip; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); +}; + +#if IS_REACHABLE(CONFIG_DVB_LGDT330X) +struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config, + u8 demod_address, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config, + u8 demod_address, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_LGDT330X + +#endif /* LGDT330X_H */ diff --git a/drivers/media/dvb-frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h new file mode 100644 index 0000000000..eb3758dd71 --- /dev/null +++ b/drivers/media/dvb-frontends/lgdt330x_priv.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for LGDT3302 and LGDT3303 - VSB/QAM + * + * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net> + */ + +#ifndef _LGDT330X_PRIV_ +#define _LGDT330X_PRIV_ + +/* i2c control register addresses */ +enum I2C_REG { + TOP_CONTROL= 0x00, + IRQ_MASK= 0x01, + IRQ_STATUS= 0x02, + VSB_CARRIER_FREQ0= 0x16, + VSB_CARRIER_FREQ1= 0x17, + VSB_CARRIER_FREQ2= 0x18, + VSB_CARRIER_FREQ3= 0x19, + CARRIER_MSEQAM1= 0x1a, + CARRIER_MSEQAM2= 0x1b, + CARRIER_LOCK= 0x1c, + TIMING_RECOVERY= 0x1d, + AGC_DELAY0= 0x2a, + AGC_DELAY1= 0x2b, + AGC_DELAY2= 0x2c, + AGC_RF_BANDWIDTH0= 0x2d, + AGC_RF_BANDWIDTH1= 0x2e, + AGC_RF_BANDWIDTH2= 0x2f, + AGC_LOOP_BANDWIDTH0= 0x30, + AGC_LOOP_BANDWIDTH1= 0x31, + AGC_FUNC_CTRL1= 0x32, + AGC_FUNC_CTRL2= 0x33, + AGC_FUNC_CTRL3= 0x34, + AGC_RFIF_ACC0= 0x39, + AGC_RFIF_ACC1= 0x3a, + AGC_RFIF_ACC2= 0x3b, + AGC_STATUS= 0x3f, + SYNC_STATUS_VSB= 0x43, + DEMUX_CONTROL= 0x66, + LGDT3302_EQPH_ERR0= 0x47, + LGDT3302_EQ_ERR1= 0x48, + LGDT3302_EQ_ERR2= 0x49, + LGDT3302_PH_ERR1= 0x4a, + LGDT3302_PH_ERR2= 0x4b, + LGDT3302_PACKET_ERR_COUNTER1= 0x6a, + LGDT3302_PACKET_ERR_COUNTER2= 0x6b, + LGDT3303_EQPH_ERR0= 0x6e, + LGDT3303_EQ_ERR1= 0x6f, + LGDT3303_EQ_ERR2= 0x70, + LGDT3303_PH_ERR1= 0x71, + LGDT3303_PH_ERR2= 0x72, + LGDT3303_PACKET_ERR_COUNTER1= 0x8b, + LGDT3303_PACKET_ERR_COUNTER2= 0x8c, +}; + +#endif /* _LGDT330X_PRIV_ */ diff --git a/drivers/media/dvb-frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c new file mode 100644 index 0000000000..872abb70d1 --- /dev/null +++ b/drivers/media/dvb-frontends/lgs8gl5.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver + + Copyright (C) 2008 Sirius International (Hong Kong) Limited + Timothy Lee <timothy.lee@siriushk.com> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <media/dvb_frontend.h> +#include "lgs8gl5.h" + + +#define REG_RESET 0x02 +#define REG_RESET_OFF 0x01 +#define REG_03 0x03 +#define REG_04 0x04 +#define REG_07 0x07 +#define REG_09 0x09 +#define REG_0A 0x0a +#define REG_0B 0x0b +#define REG_0C 0x0c +#define REG_37 0x37 +#define REG_STRENGTH 0x4b +#define REG_STRENGTH_MASK 0x7f +#define REG_STRENGTH_CARRIER 0x80 +#define REG_INVERSION 0x7c +#define REG_INVERSION_ON 0x80 +#define REG_7D 0x7d +#define REG_7E 0x7e +#define REG_A2 0xa2 +#define REG_STATUS 0xa4 +#define REG_STATUS_SYNC 0x04 +#define REG_STATUS_LOCK 0x01 + + +struct lgs8gl5_state { + struct i2c_adapter *i2c; + const struct lgs8gl5_config *config; + struct dvb_frontend frontend; +}; + + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "lgs8gl5: " args); \ + } while (0) + + +/* Writes into demod's register */ +static int +lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = {reg, data}; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n", + __func__, reg, data, ret); + return (ret != 1) ? -1 : 0; +} + + +/* Reads from demod's register */ +static int +lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg) +{ + int ret; + u8 b0[] = {reg}; + u8 b1[] = {0}; + struct i2c_msg msg[2] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, + { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) + return -EIO; + + return b1[0]; +} + + +static int +lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +{ + lgs8gl5_read_reg(state, reg); + lgs8gl5_write_reg(state, reg, data); + return 0; +} + + +/* Writes into alternate device's register */ +/* TODO: Find out what that device is for! */ +static int +lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +{ + int ret; + u8 b0[] = {reg}; + u8 b1[] = {0}; + u8 b2[] = {reg, data}; + struct i2c_msg msg[3] = { + { + .addr = state->config->demod_address + 2, + .flags = 0, + .buf = b0, + .len = 1 + }, + { + .addr = state->config->demod_address + 2, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + }, + { + .addr = state->config->demod_address + 2, + .flags = 0, + .buf = b2, + .len = 2 + }, + }; + + ret = i2c_transfer(state->i2c, msg, 3); + return (ret != 3) ? -1 : 0; +} + + +static void +lgs8gl5_soft_reset(struct lgs8gl5_state *state) +{ + u8 val; + + dprintk("%s\n", __func__); + + val = lgs8gl5_read_reg(state, REG_RESET); + lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF); + lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF); + msleep(5); +} + + +/* Starts demodulation */ +static void +lgs8gl5_start_demod(struct lgs8gl5_state *state) +{ + u8 val; + int n; + + dprintk("%s\n", __func__); + + lgs8gl5_update_alt_reg(state, 0xc2, 0x28); + lgs8gl5_soft_reset(state); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_write_reg(state, REG_09, 0x0e); + lgs8gl5_write_reg(state, REG_0A, 0xe5); + lgs8gl5_write_reg(state, REG_0B, 0x35); + lgs8gl5_write_reg(state, REG_0C, 0x30); + + lgs8gl5_update_reg(state, REG_03, 0x00); + lgs8gl5_update_reg(state, REG_7E, 0x01); + lgs8gl5_update_alt_reg(state, 0xc5, 0x00); + lgs8gl5_update_reg(state, REG_04, 0x02); + lgs8gl5_update_reg(state, REG_37, 0x01); + lgs8gl5_soft_reset(state); + + /* Wait for carrier */ + for (n = 0; n < 10; n++) { + val = lgs8gl5_read_reg(state, REG_STRENGTH); + dprintk("Wait for carrier[%d] 0x%02X\n", n, val); + if (val & REG_STRENGTH_CARRIER) + break; + msleep(4); + } + if (!(val & REG_STRENGTH_CARRIER)) + return; + + /* Wait for lock */ + for (n = 0; n < 20; n++) { + val = lgs8gl5_read_reg(state, REG_STATUS); + dprintk("Wait for lock[%d] 0x%02X\n", n, val); + if (val & REG_STATUS_LOCK) + break; + msleep(12); + } + if (!(val & REG_STATUS_LOCK)) + return; + + lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2)); + lgs8gl5_soft_reset(state); +} + + +static int +lgs8gl5_init(struct dvb_frontend *fe) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + lgs8gl5_update_alt_reg(state, 0xc2, 0x28); + lgs8gl5_soft_reset(state); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_update_reg(state, REG_07, 0x10); + lgs8gl5_write_reg(state, REG_09, 0x0e); + lgs8gl5_write_reg(state, REG_0A, 0xe5); + lgs8gl5_write_reg(state, REG_0B, 0x35); + lgs8gl5_write_reg(state, REG_0C, 0x30); + + return 0; +} + + +static int +lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); + u8 flags = lgs8gl5_read_reg(state, REG_STATUS); + + *status = 0; + + if ((level & REG_STRENGTH_MASK) > 0) + *status |= FE_HAS_SIGNAL; + if (level & REG_STRENGTH_CARRIER) + *status |= FE_HAS_CARRIER; + if (flags & REG_STATUS_SYNC) + *status |= FE_HAS_SYNC; + if (flags & REG_STATUS_LOCK) + *status |= FE_HAS_LOCK; + + return 0; +} + + +static int +lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + + +static int +lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); + *signal_strength = (level & REG_STRENGTH_MASK) << 8; + + return 0; +} + + +static int +lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); + *snr = (level & REG_STRENGTH_MASK) << 8; + + return 0; +} + + +static int +lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + + return 0; +} + + +static int +lgs8gl5_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct lgs8gl5_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (p->bandwidth_hz != 8000000) + return -EINVAL; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* lgs8gl5_set_inversion(state, p->inversion); */ + + lgs8gl5_start_demod(state); + + return 0; +} + + +static int +lgs8gl5_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + + u8 inv = lgs8gl5_read_reg(state, REG_INVERSION); + + p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF; + + p->code_rate_HP = FEC_1_2; + p->code_rate_LP = FEC_7_8; + p->guard_interval = GUARD_INTERVAL_1_32; + p->transmission_mode = TRANSMISSION_MODE_2K; + p->modulation = QAM_64; + p->hierarchy = HIERARCHY_NONE; + p->bandwidth_hz = 8000000; + + return 0; +} + + +static int +lgs8gl5_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 240; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + + +static void +lgs8gl5_release(struct dvb_frontend *fe) +{ + struct lgs8gl5_state *state = fe->demodulator_priv; + kfree(state); +} + + +static const struct dvb_frontend_ops lgs8gl5_ops; + + +struct dvb_frontend* +lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) +{ + struct lgs8gl5_state *state = NULL; + + dprintk("%s\n", __func__); + + /* Allocate memory for the internal state */ + state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* Setup the state */ + state->config = config; + state->i2c = i2c; + + /* Check if the demod is there */ + if (lgs8gl5_read_reg(state, REG_RESET) < 0) + goto error; + + /* Create dvb_frontend */ + memcpy(&state->frontend.ops, &lgs8gl5_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(lgs8gl5_attach); + + +static const struct dvb_frontend_ops lgs8gl5_ops = { + .delsys = { SYS_DTMB }, + .info = { + .name = "Legend Silicon LGS-8GL5 DMB-TH", + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, + .caps = FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_BANDWIDTH_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER + }, + + .release = lgs8gl5_release, + + .init = lgs8gl5_init, + + .set_frontend = lgs8gl5_set_frontend, + .get_frontend = lgs8gl5_get_frontend, + .get_tune_settings = lgs8gl5_get_tune_settings, + + .read_status = lgs8gl5_read_status, + .read_ber = lgs8gl5_read_ber, + .read_signal_strength = lgs8gl5_read_signal_strength, + .read_snr = lgs8gl5_read_snr, + .read_ucblocks = lgs8gl5_read_ucblocks, +}; + + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver"); +MODULE_AUTHOR("Timothy Lee"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lgs8gl5.h b/drivers/media/dvb-frontends/lgs8gl5.h new file mode 100644 index 0000000000..1ea9c4b523 --- /dev/null +++ b/drivers/media/dvb-frontends/lgs8gl5.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver + + Copyright (C) 2008 Sirius International (Hong Kong) Limited + Timothy Lee <timothy.lee@siriushk.com> + + +*/ + +#ifndef LGS8GL5_H +#define LGS8GL5_H + +#include <linux/dvb/frontend.h> + +struct lgs8gl5_config { + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_LGS8GL5) +extern struct dvb_frontend *lgs8gl5_attach( + const struct lgs8gl5_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lgs8gl5_attach( + const struct lgs8gl5_config *config, struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGS8GL5 */ + +#endif /* LGS8GL5_H */ diff --git a/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c new file mode 100644 index 0000000000..ffaf60e16e --- /dev/null +++ b/drivers/media/dvb-frontends/lgs8gxx.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 + * experimental support LGS8G42, LGS8G52 + * + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2008 Sirius International (Hong Kong) Limited + * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) + */ + +#include <asm/div64.h> +#include <linux/firmware.h> + +#include <media/dvb_frontend.h> + +#include "lgs8gxx.h" +#include "lgs8gxx_priv.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "lgs8gxx: " args); \ + } while (0) + +static int debug; +static int fake_signal_str = 1; + +#define LGS8GXX_FIRMWARE "lgs8g75.fw" + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +module_param(fake_signal_str, int, 0644); +MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." +"Signal strength calculation is slow.(default:on)."); + +/* LGS8GXX internal helper functions */ + +static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->demod_address; + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) + msg.addr += 0x02; + + if (debug >= 2) + dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .flags = 0, .buf = b0, .len = 1 }, + { .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + dev_addr = priv->config->demod_address; + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) + dev_addr += 0x02; + msg[1].addr = msg[0].addr = dev_addr; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); + return -1; + } + + *p_data = b1[0]; + if (debug >= 2) + dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); + return 0; +} + +static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) +{ + lgs8gxx_write_reg(priv, 0x02, 0x00); + msleep(1); + lgs8gxx_write_reg(priv, 0x02, 0x01); + msleep(100); + + return 0; +} + +static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, + u8 val, u8 delay, u8 tries) +{ + u8 t; + int i; + + for (i = 0; i < tries; i++) { + lgs8gxx_read_reg(priv, reg, &t); + + if ((t & mask) == val) + return 0; + msleep(delay); + } + + return 1; +} + +static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) +{ + const struct lgs8gxx_config *config = priv->config; + u8 if_conf; + + if_conf = 0x10; /* AGC output on, RF_AGC output off; */ + + if_conf |= + ((config->ext_adc) ? 0x80 : 0x00) | + ((config->if_neg_center) ? 0x04 : 0x00) | + ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ + ((config->adc_signed) ? 0x02 : 0x00) | + ((config->if_neg_edge) ? 0x01 : 0x00); + + if (config->ext_adc && + (config->prod == LGS8GXX_PROD_LGS8G52)) { + lgs8gxx_write_reg(priv, 0xBA, 0x40); + } + + lgs8gxx_write_reg(priv, 0x07, if_conf); + + return 0; +} + +static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) +{ + u64 val; + u32 v32; + u32 if_clk; + + if_clk = priv->config->if_clk_freq; + + val = freq; + if (freq != 0) { + val <<= 32; + if (if_clk != 0) + do_div(val, if_clk); + v32 = val & 0xFFFFFFFF; + dprintk("Set IF Freq to %dkHz\n", freq); + } else { + v32 = 0; + dprintk("Set IF Freq to baseband\n"); + } + dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); + } else { + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + } + + return 0; +} + +static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) +{ + u64 val; + u32 v32 = 0; + u8 reg_addr, t; + int i; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + reg_addr = 0x23; + else + reg_addr = 0x48; + + for (i = 0; i < 4; i++) { + lgs8gxx_read_reg(priv, reg_addr, &t); + v32 <<= 8; + v32 |= t; + reg_addr--; + } + + val = v32; + val *= priv->config->if_clk_freq; + val >>= 32; + dprintk("AFC = %u kHz\n", (u32)val); + return 0; +} + +static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) +{ + u8 t; + u8 prod = priv->config->prod; + + if (prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC6, 0x01); + + if (prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x04); + lgs8gxx_write_reg(priv, 0x0C, t | 0x80); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } else if (prod == LGS8GXX_PROD_LGS8913 || + prod == LGS8GXX_PROD_LGS8GL5 || + prod == LGS8GXX_PROD_LGS8G42 || + prod == LGS8GXX_PROD_LGS8G52 || + prod == LGS8GXX_PROD_LGS8G54) { + lgs8gxx_read_reg(priv, 0x7E, &t); + lgs8gxx_write_reg(priv, 0x7E, t | 0x01); + + /* clear FEC self reset */ + lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + } + + if (prod == LGS8GXX_PROD_LGS8913) { + /* FEC auto detect */ + lgs8gxx_write_reg(priv, 0xC1, 0x03); + + lgs8gxx_read_reg(priv, 0x7C, &t); + t = (t & 0x8C) | 0x03; + lgs8gxx_write_reg(priv, 0x7C, t); + + /* BER test mode */ + lgs8gxx_read_reg(priv, 0xC3, &t); + t = (t & 0xEF) | 0x10; + lgs8gxx_write_reg(priv, 0xC3, t); + } + + if (priv->config->prod == LGS8GXX_PROD_LGS8G52) + lgs8gxx_write_reg(priv, 0xD9, 0x40); + + return 0; +} + +static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) +{ + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t2; + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x80); + lgs8gxx_write_reg(priv, 0x0C, t); + + lgs8gxx_read_reg(priv, 0x0C, &t); + lgs8gxx_read_reg(priv, 0x19, &t2); + + if (((t&0x03) == 0x01) && (t2&0x01)) { + lgs8gxx_write_reg(priv, 0x6E, 0x05); + lgs8gxx_write_reg(priv, 0x39, 0x02); + lgs8gxx_write_reg(priv, 0x39, 0x03); + lgs8gxx_write_reg(priv, 0x3D, 0x05); + lgs8gxx_write_reg(priv, 0x3E, 0x28); + lgs8gxx_write_reg(priv, 0x53, 0x80); + } else { + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } + + lgs8gxx_soft_reset(priv); + return 0; + } + + /* turn off auto-detect; manual settings */ + lgs8gxx_write_reg(priv, 0x7E, 0); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC1, 0); + + lgs8gxx_read_reg(priv, 0xC5, &t); + t = (t & 0xE0) | 0x06; + lgs8gxx_write_reg(priv, 0xC5, t); + + lgs8gxx_soft_reset(priv); + + return 0; +} + +static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) +{ + int ret = 0; + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + ret = lgs8gxx_read_reg(priv, 0x13, &t); + else + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return ret; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + *locked = ((t & 0x80) == 0x80) ? 1 : 0; + else + *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + return 0; +} + +/* Wait for Code Acquisition Lock */ +static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) +{ + int ret = 0; + u8 reg, mask, val; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x13; + mask = 0x80; + val = 0x80; + } else { + reg = 0x4B; + mask = 0xC0; + val = 0xC0; + } + + ret = wait_reg_mask(priv, reg, mask, val, 50, 40); + *locked = (ret == 0) ? 1 : 0; + + return 0; +} + +static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, + u8 *finished) +{ + int ret = 0; + u8 reg, mask, val; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x1f; + mask = 0xC0; + val = 0x80; + } else { + reg = 0xA4; + mask = 0x03; + val = 0x01; + } + + ret = wait_reg_mask(priv, reg, mask, val, 10, 20); + *finished = (ret == 0) ? 1 : 0; + + return 0; +} + +static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, + u8 *locked) +{ + int err = 0; + u8 ad_fini = 0; + u8 t1, t2; + + if (gi == GI_945) + dprintk("try GI 945\n"); + else if (gi == GI_595) + dprintk("try GI 595\n"); + else if (gi == GI_420) + dprintk("try GI 420\n"); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t1); + lgs8gxx_read_reg(priv, 0x18, &t2); + t1 &= ~(GI_MASK); + t1 |= gi; + t2 &= 0xFE; + t2 |= cpn ? 0x01 : 0x00; + lgs8gxx_write_reg(priv, 0x0C, t1); + lgs8gxx_write_reg(priv, 0x18, t2); + } else { + lgs8gxx_write_reg(priv, 0x04, gi); + } + lgs8gxx_soft_reset(priv); + err = lgs8gxx_wait_ca_lock(priv, locked); + if (err || !(*locked)) + return err; + err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); + if (err != 0) + return err; + if (ad_fini) { + dprintk("auto detect finished\n"); + } else + *locked = 0; + + return 0; +} + +static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, + u8 *detected_param, u8 *gi) +{ + int i, j; + int err = 0; + u8 locked = 0, tmp_gi; + + dprintk("%s\n", __func__); + + lgs8gxx_set_mode_auto(priv); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x67, 0xAA); + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + } else { + /* Guard Interval */ + lgs8gxx_write_reg(priv, 0x03, 00); + } + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + tmp_gi = GI_945; + err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); + if (err) + goto out; + if (locked) + goto locked; + } + for (j = 0; j < 2; j++) { + tmp_gi = GI_420; + err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); + if (err) + goto out; + if (locked) + goto locked; + } + tmp_gi = GI_595; + err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); + if (err) + goto out; + if (locked) + goto locked; + } + +locked: + if ((err == 0) && (locked == 1)) { + u8 t; + + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0xA2, &t); + *detected_param = t; + } else { + lgs8gxx_read_reg(priv, 0x1F, &t); + *detected_param = t & 0x3F; + } + + if (tmp_gi == GI_945) + dprintk("GI 945 locked\n"); + else if (tmp_gi == GI_595) + dprintk("GI 595 locked\n"); + else if (tmp_gi == GI_420) + dprintk("GI 420 locked\n"); + *gi = tmp_gi; + } + if (!locked) + err = -1; + +out: + return err; +} + +static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) +{ + s8 err; + u8 gi = 0x2; + u8 detected_param = 0; + + err = lgs8gxx_auto_detect(priv, &detected_param, &gi); + + if (err != 0) { + dprintk("lgs8gxx_auto_detect failed\n"); + } else + dprintk("detected param = 0x%02X\n", detected_param); + + /* Apply detected parameters */ + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + u8 inter_leave_len = detected_param & TIM_MASK ; + /* Fix 8913 time interleaver detection bug */ + inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; + detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; + detected_param |= inter_leave_len; + } + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t; + lgs8gxx_read_reg(priv, 0x19, &t); + t &= 0x81; + t |= detected_param << 1; + lgs8gxx_write_reg(priv, 0x19, t); + } else { + lgs8gxx_write_reg(priv, 0x7D, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC0, detected_param); + } + /* lgs8gxx_soft_reset(priv); */ + + /* Enter manual mode */ + lgs8gxx_set_mode_manual(priv); + + switch (gi) { + case GI_945: + priv->curr_gi = 945; break; + case GI_595: + priv->curr_gi = 595; break; + case GI_420: + priv->curr_gi = 420; break; + default: + priv->curr_gi = 945; break; + } +} + +static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, + u8 serial, u8 clk_pol, u8 clk_gated) +{ + int ret = 0; + u8 t, reg_addr; + + reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; + ret = lgs8gxx_read_reg(priv, reg_addr, &t); + if (ret != 0) + return ret; + + t &= 0xF8; + t |= serial ? TS_SERIAL : TS_PARALLEL; + t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; + t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; + + ret = lgs8gxx_write_reg(priv, reg_addr, t); + if (ret != 0) + return ret; + + return 0; +} + +/* A/D input peak-to-peak voltage range */ +static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, + u8 sel) +{ + u8 r26 = 0x73, r27 = 0x90; + + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) + return 0; + + r26 |= (sel & 0x01) << 7; + r27 |= (sel & 0x02) >> 1; + lgs8gxx_write_reg(priv, 0x26, r26); + lgs8gxx_write_reg(priv, 0x27, r27); + + return 0; +} + +/* LGS8913 demod frontend functions */ + +static int lgs8913_init(struct lgs8gxx_state *priv) +{ + u8 t; + + /* LGS8913 specific */ + lgs8gxx_write_reg(priv, 0xc1, 0x3); + + lgs8gxx_read_reg(priv, 0x7c, &t); + lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3); + + /* LGS8913 specific */ + lgs8gxx_read_reg(priv, 0xc3, &t); + lgs8gxx_write_reg(priv, 0xc3, t&0x10); + + + return 0; +} + +static int lgs8g75_init_data(struct lgs8gxx_state *priv) +{ + const struct firmware *fw; + int rc; + int i; + + rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev); + if (rc) + return rc; + + lgs8gxx_write_reg(priv, 0xC6, 0x40); + + lgs8gxx_write_reg(priv, 0x3D, 0x04); + lgs8gxx_write_reg(priv, 0x39, 0x00); + + lgs8gxx_write_reg(priv, 0x3A, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3B, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + + for (i = 0; i < fw->size; i++) { + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); + lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); + lgs8gxx_write_reg(priv, 0x3C, fw->data[i]); + } + + lgs8gxx_write_reg(priv, 0x38, 0x00); + + release_firmware(fw); + return 0; +} + +static int lgs8gxx_init(struct dvb_frontend *fe) +{ + struct lgs8gxx_state *priv = + (struct lgs8gxx_state *)fe->demodulator_priv; + const struct lgs8gxx_config *config = priv->config; + u8 data = 0; + s8 err; + dprintk("%s\n", __func__); + + lgs8gxx_read_reg(priv, 0, &data); + dprintk("reg 0 = 0x%02X\n", data); + + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_set_adc_vpp(priv, config->adc_vpp); + + /* Setup MPEG output format */ + err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, + config->ts_clk_pol, + config->ts_clk_gated); + if (err != 0) + return -EIO; + + if (config->prod == LGS8GXX_PROD_LGS8913) + lgs8913_init(priv); + lgs8gxx_set_if_freq(priv, priv->config->if_freq); + lgs8gxx_set_ad_mode(priv); + + return 0; +} + +static void lgs8gxx_release(struct dvb_frontend *fe) +{ + struct lgs8gxx_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + + kfree(state); +} + + +static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return lgs8gxx_write_reg(priv, buf[0], buf[1]); +} + +static int lgs8gxx_set_fe(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; + struct lgs8gxx_state *priv = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + /* set frequency */ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* start auto lock */ + lgs8gxx_auto_lock(priv); + + msleep(10); + + /* TODO: get real readings from device */ + + /* bandwidth */ + fe_params->bandwidth_hz = 8000000; + + fe_params->code_rate_HP = FEC_AUTO; + fe_params->code_rate_LP = FEC_AUTO; + + fe_params->modulation = QAM_AUTO; + + /* transmission mode */ + fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; + + /* guard interval */ + fe_params->guard_interval = GUARD_INTERVAL_AUTO; + + /* hierarchy */ + fe_params->hierarchy = HIERARCHY_NONE; + + return 0; +} + +static +int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + /* FIXME: copy from tda1004x.c */ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int lgs8gxx_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + s8 ret; + u8 t, locked = 0; + + dprintk("%s\n", __func__); + *fe_status = 0; + + lgs8gxx_get_afc_phase(priv); + lgs8gxx_is_locked(priv, &locked); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + if (locked) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; + } + + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return -EIO; + + dprintk("Reg 0x4B: 0x%02X\n", t); + + *fe_status = 0; + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + if ((t & 0x40) == 0x40) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + if ((t & 0x80) == 0x80) + *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } else { + if ((t & 0x80) == 0x80) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } + + /* success */ + dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); + return 0; +} + +static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal) +{ + u16 v; + u8 agc_lvl[2], cat; + + dprintk("%s()\n", __func__); + lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]); + lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]); + + v = agc_lvl[0]; + v <<= 8; + v |= agc_lvl[1]; + + dprintk("agc_lvl: 0x%04X\n", v); + + if (v < 0x100) + cat = 0; + else if (v < 0x190) + cat = 5; + else if (v < 0x2A8) + cat = 4; + else if (v < 0x381) + cat = 3; + else if (v < 0x400) + cat = 2; + else if (v == 0x400) + cat = 1; + else + cat = 0; + + *signal = cat * 65535 / 5; + + return 0; +} + +static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +{ + u8 t; s8 ret; + s16 max_strength = 0; + u8 str; + u16 i, gi = priv->curr_gi; + + dprintk("%s\n", __func__); + + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return -EIO; + + if (fake_signal_str) { + if ((t & 0xC0) == 0xC0) { + dprintk("Fake signal strength\n"); + *signal = 0x7FFF; + } else + *signal = 0; + return 0; + } + + dprintk("gi = %d\n", gi); + for (i = 0; i < gi; i++) { + + if ((i & 0xFF) == 0) + lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8)); + lgs8gxx_write_reg(priv, 0x83, i & 0xFF); + + lgs8gxx_read_reg(priv, 0x94, &str); + if (max_strength < str) + max_strength = str; + } + + *signal = max_strength; + dprintk("%s: signal=0x%02X\n", __func__, *signal); + + lgs8gxx_read_reg(priv, 0x95, &t); + dprintk("%s: AVG Noise=0x%02X\n", __func__, t); + + return 0; +} + +static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +{ + u8 t; + s16 v = 0; + + dprintk("%s\n", __func__); + + lgs8gxx_read_reg(priv, 0xB1, &t); + v |= t; + v <<= 8; + lgs8gxx_read_reg(priv, 0xB0, &t); + v |= t; + + *signal = v; + dprintk("%s: signal=0x%02X\n", __func__, *signal); + + return 0; +} + +static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + return lgs8913_read_signal_strength(priv, signal); + else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + return lgs8g75_read_signal_strength(priv, signal); + else + return lgs8gxx_read_signal_agc(priv, signal); +} + +static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + u8 t; + *snr = 0; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + lgs8gxx_read_reg(priv, 0x34, &t); + else + lgs8gxx_read_reg(priv, 0x95, &t); + dprintk("AVG Noise=0x%02X\n", t); + *snr = 256 - t; + *snr <<= 8; + dprintk("snr=0x%x\n", *snr); + + return 0; +} + +static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); + return 0; +} + +static void packet_counter_start(struct lgs8gxx_state *priv) +{ + u8 orig, t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &orig); + orig &= 0xE7; + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x18; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x01); + lgs8gxx_write_reg(priv, 0xC6, 0x41); + lgs8gxx_write_reg(priv, 0xC6, 0x01); + } +} + +static void packet_counter_stop(struct lgs8gxx_state *priv) +{ + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &t); + t &= 0xE7; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x81); + } +} + +static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + u8 reg_err, reg_total, t; + u32 total_cnt = 0, err_cnt = 0; + int i; + + dprintk("%s\n", __func__); + + packet_counter_start(priv); + msleep(200); + packet_counter_stop(priv); + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg_total = 0x28; reg_err = 0x2C; + } else { + reg_total = 0xD0; reg_err = 0xD4; + } + + for (i = 0; i < 4; i++) { + total_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_total+3-i, &t); + total_cnt |= t; + } + for (i = 0; i < 4; i++) { + err_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_err+3-i, &t); + err_cnt |= t; + } + dprintk("error=%d total=%d\n", err_cnt, total_cnt); + + if (total_cnt == 0) + *ber = 0; + else + *ber = err_cnt * 100 / total_cnt; + + dprintk("%s: ber=0x%x\n", __func__, *ber); + return 0; +} + +static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + if (priv->config->tuner_address == 0) + return 0; + if (enable) { + u8 v = 0x80 | priv->config->tuner_address; + return lgs8gxx_write_reg(priv, 0x01, v); + } + return lgs8gxx_write_reg(priv, 0x01, 0); +} + +static const struct dvb_frontend_ops lgs8gxx_ops = { + .delsys = { SYS_DTMB }, + .info = { + .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, + .caps = + FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = lgs8gxx_release, + + .init = lgs8gxx_init, + .write = lgs8gxx_write, + .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl, + + .set_frontend = lgs8gxx_set_fe, + .get_tune_settings = lgs8gxx_get_tune_settings, + + .read_status = lgs8gxx_read_status, + .read_ber = lgs8gxx_read_ber, + .read_signal_strength = lgs8gxx_read_signal_strength, + .read_snr = lgs8gxx_read_snr, + .read_ucblocks = lgs8gxx_read_ucblocks, +}; + +struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, + struct i2c_adapter *i2c) +{ + struct lgs8gxx_state *priv = NULL; + u8 data = 0; + + dprintk("%s()\n", __func__); + + if (config == NULL || i2c == NULL) + return NULL; + + priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL); + if (priv == NULL) + goto error_out; + + priv->config = config; + priv->i2c = i2c; + + /* check if the demod is there */ + if (lgs8gxx_read_reg(priv, 0, &data) != 0) { + dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n", + __func__, priv->config->demod_address); + goto error_out; + } + + lgs8gxx_read_reg(priv, 1, &data); + + memcpy(&priv->frontend.ops, &lgs8gxx_ops, + sizeof(struct dvb_frontend_ops)); + priv->frontend.demodulator_priv = priv; + + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_init_data(priv); + + return &priv->frontend; + +error_out: + dprintk("%s() error_out\n", __func__); + kfree(priv); + return NULL; + +} +EXPORT_SYMBOL_GPL(lgs8gxx_attach); + +MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); +MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(LGS8GXX_FIRMWARE); diff --git a/drivers/media/dvb-frontends/lgs8gxx.h b/drivers/media/dvb-frontends/lgs8gxx.h new file mode 100644 index 0000000000..5b2f13a8be --- /dev/null +++ b/drivers/media/dvb-frontends/lgs8gxx.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 + * experimental support LGS8G42, LGS8G52 + * + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2008 Sirius International (Hong Kong) Limited + * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) + */ + +#ifndef __LGS8GXX_H__ +#define __LGS8GXX_H__ + +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#define LGS8GXX_PROD_LGS8913 0 +#define LGS8GXX_PROD_LGS8GL5 1 +#define LGS8GXX_PROD_LGS8G42 3 +#define LGS8GXX_PROD_LGS8G52 4 +#define LGS8GXX_PROD_LGS8G54 5 +#define LGS8GXX_PROD_LGS8G75 6 + +struct lgs8gxx_config { + + /* product type */ + u8 prod; + + /* the demodulator's i2c address */ + u8 demod_address; + + /* parallel or serial transport stream */ + u8 serial_ts; + + /* transport stream polarity*/ + u8 ts_clk_pol; + + /* transport stream clock gated by ts_valid */ + u8 ts_clk_gated; + + /* A/D Clock frequency */ + u32 if_clk_freq; /* in kHz */ + + /* IF frequency */ + u32 if_freq; /* in kHz */ + + /*Use External ADC*/ + u8 ext_adc; + + /*External ADC output two's complement*/ + u8 adc_signed; + + /*Sample IF data at falling edge of IF_CLK*/ + u8 if_neg_edge; + + /*IF use Negative center frequency*/ + u8 if_neg_center; + + /*8G75 internal ADC input range selection*/ + /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/ + u8 adc_vpp; + + /* slave address and configuration of the tuner */ + u8 tuner_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_LGS8GXX) +extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, + struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGS8GXX */ + +#endif /* __LGS8GXX_H__ */ diff --git a/drivers/media/dvb-frontends/lgs8gxx_priv.h b/drivers/media/dvb-frontends/lgs8gxx_priv.h new file mode 100644 index 0000000000..4c295a7586 --- /dev/null +++ b/drivers/media/dvb-frontends/lgs8gxx_priv.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 + * experimental support LGS8G42, LGS8G52 + * + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2008 Sirius International (Hong Kong) Limited + * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) + */ + +#ifndef LGS8913_PRIV_H +#define LGS8913_PRIV_H + +struct lgs8gxx_state { + struct i2c_adapter *i2c; + /* configuration settings */ + const struct lgs8gxx_config *config; + struct dvb_frontend frontend; + u16 curr_gi; /* current guard interval */ +}; + +#define SC_MASK 0x1C /* Sub-Carrier Modulation Mask */ +#define SC_QAM64 0x10 /* 64QAM modulation */ +#define SC_QAM32 0x0C /* 32QAM modulation */ +#define SC_QAM16 0x08 /* 16QAM modulation */ +#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */ +#define SC_QAM4 0x00 /* 4QAM modulation */ + +#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ +#define LGS_FEC_0_4 0x00 /* FEC Rate 0.4 */ +#define LGS_FEC_0_6 0x01 /* FEC Rate 0.6 */ +#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ + +#define TIM_MASK 0x20 /* Time Interleave Length Mask */ +#define TIM_LONG 0x20 /* Time Interleave Length = 720 */ +#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */ + +#define CF_MASK 0x80 /* Control Frame Mask */ +#define CF_EN 0x80 /* Control Frame On */ + +#define GI_MASK 0x03 /* Guard Interval Mask */ +#define GI_420 0x00 /* 1/9 Guard Interval */ +#define GI_595 0x01 /* */ +#define GI_945 0x02 /* 1/4 Guard Interval */ + + +#define TS_PARALLEL 0x00 /* Parallel TS Output a.k.a. SPI */ +#define TS_SERIAL 0x01 /* Serial TS Output a.k.a. SSI */ +#define TS_CLK_NORMAL 0x00 /* MPEG Clock Normal */ +#define TS_CLK_INVERTED 0x02 /* MPEG Clock Inverted */ +#define TS_CLK_GATED 0x00 /* MPEG clock gated */ +#define TS_CLK_FREERUN 0x04 /* MPEG clock free running*/ + + +#endif diff --git a/drivers/media/dvb-frontends/lnbh24.h b/drivers/media/dvb-frontends/lnbh24.h new file mode 100644 index 0000000000..d8d0303c6e --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh24.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * lnbh24.h - driver for lnb supply and control ic lnbh24 + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef _LNBH24_H +#define _LNBH24_H + +/* system register bits */ +#define LNBH24_OLF 0x01 +#define LNBH24_OTF 0x02 +#define LNBH24_EN 0x04 +#define LNBH24_VSEL 0x08 +#define LNBH24_LLC 0x10 +#define LNBH24_TEN 0x20 +#define LNBH24_TTX 0x40 +#define LNBH24_PCL 0x80 + +#include <linux/dvb/frontend.h> + +#if IS_REACHABLE(CONFIG_DVB_LNBP21) +/* override_set and override_clear control which + system register bits (above) to always set & clear */ +extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear, u8 i2c_addr); +#else +static inline struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear, u8 i2c_addr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c new file mode 100644 index 0000000000..41bec05064 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh25.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lnbh25.c + * + * Driver for LNB supply and control IC LNBH25 + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "lnbh25.h" + +/** + * struct lnbh25_priv - LNBH25 driver private data + * @i2c: pointer to the I2C adapter structure + * @i2c_address: I2C address of LNBH25 SEC chip + * @config: Registers configuration: + * offset 0: 1st register address, always 0x02 (DATA1) + * offset 1: DATA1 register value + * offset 2: DATA2 register value + */ +struct lnbh25_priv { + struct i2c_adapter *i2c; + u8 i2c_address; + u8 config[3]; +}; + +#define LNBH25_STATUS_OFL 0x1 +#define LNBH25_STATUS_VMON 0x4 +#define LNBH25_VSEL_13 0x03 +#define LNBH25_VSEL_18 0x0a + +static int lnbh25_read_vmon(struct lnbh25_priv *priv) +{ + int i, ret; + u8 addr = 0x00; + u8 status[6]; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = &addr + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = sizeof(status), + .buf = status + } + }; + + for (i = 0; i < 2; i++) { + ret = i2c_transfer(priv->i2c, &msg[i], 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_dbg(&priv->i2c->dev, + "%s(): I2C transfer %d failed (%d)\n", + __func__, i, ret); + return ret; + } + } + dev_dbg(&priv->i2c->dev, "%s(): %*ph\n", + __func__, (int) sizeof(status), status); + if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) { + dev_err(&priv->i2c->dev, + "%s(): voltage in failure state, status reg 0x%x\n", + __func__, status[0]); + return -EIO; + } + return 0; +} + +static int lnbh25_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + int ret; + u8 data1_reg; + const char *vsel; + struct lnbh25_priv *priv = fe->sec_priv; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .len = sizeof(priv->config), + .buf = priv->config + }; + + switch (voltage) { + case SEC_VOLTAGE_OFF: + data1_reg = 0x00; + vsel = "Off"; + break; + case SEC_VOLTAGE_13: + data1_reg = LNBH25_VSEL_13; + vsel = "13V"; + break; + case SEC_VOLTAGE_18: + data1_reg = LNBH25_VSEL_18; + vsel = "18V"; + break; + default: + return -EINVAL; + } + priv->config[1] = data1_reg; + dev_dbg(&priv->i2c->dev, + "%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n", + __func__, vsel, priv->i2c_address, + priv->config[0], priv->config[1], priv->config[2]); + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n", + __func__, ret); + return ret; + } + if (voltage != SEC_VOLTAGE_OFF) { + msleep(120); + ret = lnbh25_read_vmon(priv); + } else { + msleep(20); + ret = 0; + } + return ret; +} + +static void lnbh25_release(struct dvb_frontend *fe) +{ + struct lnbh25_priv *priv = fe->sec_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF); + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c) +{ + struct lnbh25_priv *priv; + + dev_dbg(&i2c->dev, "%s()\n", __func__); + priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL); + if (!priv) + return NULL; + priv->i2c_address = (cfg->i2c_address >> 1); + priv->i2c = i2c; + priv->config[0] = 0x02; + priv->config[1] = 0x00; + priv->config[2] = cfg->data2_config; + fe->sec_priv = priv; + if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) { + dev_err(&i2c->dev, + "%s(): no LNBH25 found at I2C addr 0x%02x\n", + __func__, priv->i2c_address); + kfree(priv); + fe->sec_priv = NULL; + return NULL; + } + + fe->ops.release_sec = lnbh25_release; + fe->ops.set_voltage = lnbh25_set_voltage; + + dev_info(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n", + __func__, priv->i2c_address); + return fe; +} +EXPORT_SYMBOL_GPL(lnbh25_attach); + +MODULE_DESCRIPTION("ST LNBH25 driver"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lnbh25.h b/drivers/media/dvb-frontends/lnbh25.h new file mode 100644 index 0000000000..4f68f0c444 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh25.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * lnbh25.c + * + * Driver for LNB supply and control IC LNBH25 + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + */ + +#ifndef LNBH25_H +#define LNBH25_H + +#include <linux/i2c.h> +#include <linux/dvb/frontend.h> + +/* 22 kHz tone enabled. Tone output controlled by DSQIN pin */ +#define LNBH25_TEN 0x01 +/* Low power mode activated (used only with 22 kHz tone output disabled) */ +#define LNBH25_LPM 0x02 +/* DSQIN input pin is set to receive external 22 kHz TTL signal source */ +#define LNBH25_EXTM 0x04 + +struct lnbh25_config { + u8 i2c_address; + u8 data2_config; +}; + +#if IS_REACHABLE(CONFIG_DVB_LNBH25) +struct dvb_frontend *lnbh25_attach( + struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lnbh25_attach( + struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/lnbh29.c b/drivers/media/dvb-frontends/lnbh29.c new file mode 100644 index 0000000000..410bae099c --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh29.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for LNB supply and control IC STMicroelectronics LNBH29 +// +// Copyright (c) 2018 Socionext Inc. + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "lnbh29.h" + +/** + * struct lnbh29_priv - LNBH29 driver private data + * @i2c: Pointer to the I2C adapter structure + * @i2c_address: I2C address of LNBH29 chip + * @config: Registers configuration + * offset 0: 1st register address, always 0x01 (DATA) + * offset 1: DATA register value + */ +struct lnbh29_priv { + struct i2c_adapter *i2c; + u8 i2c_address; + u8 config[2]; +}; + +#define LNBH29_STATUS_OLF BIT(0) +#define LNBH29_STATUS_OTF BIT(1) +#define LNBH29_STATUS_VMON BIT(2) +#define LNBH29_STATUS_PNG BIT(3) +#define LNBH29_STATUS_PDO BIT(4) +#define LNBH29_VSEL_MASK GENMASK(2, 0) +#define LNBH29_VSEL_0 0x00 +/* Min: 13.188V, Typ: 13.667V, Max:14V */ +#define LNBH29_VSEL_13 0x03 +/* Min: 18.158V, Typ: 18.817V, Max:19.475V */ +#define LNBH29_VSEL_18 0x07 + +static int lnbh29_read_vmon(struct lnbh29_priv *priv) +{ + u8 addr = 0x00; + u8 status[2]; + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = &addr + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = sizeof(status), + .buf = status + } + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret >= 0 && ret != 2) + ret = -EIO; + if (ret < 0) { + dev_dbg(&priv->i2c->dev, "LNBH29 I2C transfer failed (%d)\n", + ret); + return ret; + } + + if (status[0] & (LNBH29_STATUS_OLF | LNBH29_STATUS_VMON)) { + dev_err(&priv->i2c->dev, + "LNBH29 voltage in failure state, status reg 0x%x\n", + status[0]); + return -EIO; + } + + return 0; +} + +static int lnbh29_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct lnbh29_priv *priv = fe->sec_priv; + u8 data_reg; + int ret; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .len = sizeof(priv->config), + .buf = priv->config + }; + + switch (voltage) { + case SEC_VOLTAGE_OFF: + data_reg = LNBH29_VSEL_0; + break; + case SEC_VOLTAGE_13: + data_reg = LNBH29_VSEL_13; + break; + case SEC_VOLTAGE_18: + data_reg = LNBH29_VSEL_18; + break; + default: + return -EINVAL; + } + priv->config[1] &= ~LNBH29_VSEL_MASK; + priv->config[1] |= data_reg; + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_err(&priv->i2c->dev, "LNBH29 I2C transfer error (%d)\n", + ret); + return ret; + } + + /* Soft-start time (Vout 0V to 18V) is Typ. 6ms. */ + usleep_range(6000, 20000); + + if (voltage == SEC_VOLTAGE_OFF) + return 0; + + return lnbh29_read_vmon(priv); +} + +static void lnbh29_release(struct dvb_frontend *fe) +{ + lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF); + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, + struct lnbh29_config *cfg, + struct i2c_adapter *i2c) +{ + struct lnbh29_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->i2c_address = (cfg->i2c_address >> 1); + priv->i2c = i2c; + priv->config[0] = 0x01; + priv->config[1] = cfg->data_config; + fe->sec_priv = priv; + + if (lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF)) { + dev_err(&i2c->dev, "no LNBH29 found at I2C addr 0x%02x\n", + priv->i2c_address); + kfree(priv); + fe->sec_priv = NULL; + return NULL; + } + + fe->ops.release_sec = lnbh29_release; + fe->ops.set_voltage = lnbh29_set_voltage; + + dev_info(&i2c->dev, "LNBH29 attached at I2C addr 0x%02x\n", + priv->i2c_address); + + return fe; +} +EXPORT_SYMBOL(lnbh29_attach); + +MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); +MODULE_DESCRIPTION("STMicroelectronics LNBH29 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/lnbh29.h b/drivers/media/dvb-frontends/lnbh29.h new file mode 100644 index 0000000000..6179921520 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh29.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for LNB supply and control IC STMicroelectronics LNBH29 + * + * Copyright (c) 2018 Socionext Inc. + */ + +#ifndef LNBH29_H +#define LNBH29_H + +#include <linux/i2c.h> +#include <linux/dvb/frontend.h> + +/* Using very low E.S.R. capacitors or ceramic caps */ +#define LNBH29_DATA_COMP BIT(3) + +struct lnbh29_config { + u8 i2c_address; + u8 data_config; +}; + +#if IS_REACHABLE(CONFIG_DVB_LNBH29) +struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, + struct lnbh29_config *cfg, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, + struct lnbh29_config *cfg, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c new file mode 100644 index 0000000000..32593b1f75 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbp21.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lnbp21.c - driver for lnb supply and control ic lnbp21 + * + * Copyright (C) 2006, 2009 Oliver Endriss <o.endriss@gmx.de> + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * + * the project's page is at https://linuxtv.org + */ +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "lnbp21.h" +#include "lnbh24.h" + +struct lnbp21 { + u8 config; + u8 override_or; + u8 override_and; + struct i2c_adapter *i2c; + u8 i2c_addr; +}; + +static int lnbp21_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; + struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, + .buf = &lnbp21->config, + .len = sizeof(lnbp21->config) }; + + lnbp21->config &= ~(LNBP21_VSEL | LNBP21_EN); + + switch(voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + lnbp21->config |= LNBP21_EN; + break; + case SEC_VOLTAGE_18: + lnbp21->config |= (LNBP21_EN | LNBP21_VSEL); + break; + default: + return -EINVAL; + } + + lnbp21->config |= lnbp21->override_or; + lnbp21->config &= lnbp21->override_and; + + return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; + struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, + .buf = &lnbp21->config, + .len = sizeof(lnbp21->config) }; + + if (arg) + lnbp21->config |= LNBP21_LLC; + else + lnbp21->config &= ~LNBP21_LLC; + + lnbp21->config |= lnbp21->override_or; + lnbp21->config &= lnbp21->override_and; + + return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int lnbp21_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; + struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, + .buf = &lnbp21->config, + .len = sizeof(lnbp21->config) }; + + switch (tone) { + case SEC_TONE_OFF: + lnbp21->config &= ~LNBP21_TEN; + break; + case SEC_TONE_ON: + lnbp21->config |= LNBP21_TEN; + break; + default: + return -EINVAL; + } + + lnbp21->config |= lnbp21->override_or; + lnbp21->config &= lnbp21->override_and; + + return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void lnbp21_release(struct dvb_frontend *fe) +{ + /* LNBP power off */ + lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free data */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear, u8 i2c_addr, u8 config) +{ + struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); + if (!lnbp21) + return NULL; + + /* default configuration */ + lnbp21->config = config; + lnbp21->i2c = i2c; + lnbp21->i2c_addr = i2c_addr; + fe->sec_priv = lnbp21; + + /* bits which should be forced to '1' */ + lnbp21->override_or = override_set; + + /* bits which should be forced to '0' */ + lnbp21->override_and = ~override_clear; + + /* detect if it is present or not */ + if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) { + kfree(lnbp21); + return NULL; + } + + /* install release callback */ + fe->ops.release_sec = lnbp21_release; + + /* override frontend ops */ + fe->ops.set_voltage = lnbp21_set_voltage; + fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; + if (!(override_clear & LNBH24_TEN)) /*22kHz logic controlled by demod*/ + fe->ops.set_tone = lnbp21_set_tone; + printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr); + + return fe; +} + +struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear, u8 i2c_addr) +{ + return lnbx2x_attach(fe, i2c, override_set, override_clear, + i2c_addr, LNBH24_TTX); +} +EXPORT_SYMBOL_GPL(lnbh24_attach); + +struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear) +{ + return lnbx2x_attach(fe, i2c, override_set, override_clear, + 0x08, LNBP21_ISEL); +} +EXPORT_SYMBOL_GPL(lnbp21_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24"); +MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h new file mode 100644 index 0000000000..f17cf2b731 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbp21.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * lnbp21.h - driver for lnb supply and control ic lnbp21 + * + * Copyright (C) 2006 Oliver Endriss + * + * the project's page is at https://linuxtv.org + */ + +#ifndef _LNBP21_H +#define _LNBP21_H + +/* system register bits */ +/* [RO] 0=OK; 1=over current limit flag */ +#define LNBP21_OLF 0x01 +/* [RO] 0=OK; 1=over temperature flag (150 C) */ +#define LNBP21_OTF 0x02 +/* [RW] 0=disable LNB power, enable loopthrough + 1=enable LNB power, disable loopthrough */ +#define LNBP21_EN 0x04 +/* [RW] 0=low voltage (13/14V, vert pol) + 1=high voltage (18/19V,horiz pol) */ +#define LNBP21_VSEL 0x08 +/* [RW] increase LNB voltage by 1V: + 0=13/18V; 1=14/19V */ +#define LNBP21_LLC 0x10 +/* [RW] 0=tone controlled by DSQIN pin + 1=tone enable, disable DSQIN */ +#define LNBP21_TEN 0x20 +/* [RW] current limit select: + 0:Iout=500-650mA Isc=300mA + 1:Iout=400-550mA Isc=200mA */ +#define LNBP21_ISEL 0x40 +/* [RW] short-circuit protect: + 0=pulsed (dynamic) curr limiting + 1=static curr limiting */ +#define LNBP21_PCL 0x80 + +#include <linux/dvb/frontend.h> + +#if IS_REACHABLE(CONFIG_DVB_LNBP21) +/* override_set and override_clear control which + system register bits (above) to always set & clear */ +extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear); +#else +static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 override_set, + u8 override_clear) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c new file mode 100644 index 0000000000..cb4ea5d3fa --- /dev/null +++ b/drivers/media/dvb-frontends/lnbp22.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lnbp22.h - driver for lnb supply and control ic lnbp22 + * + * Copyright (C) 2006 Dominik Kuhlen + * Based on lnbp21 driver + * + * the project's page is at https://linuxtv.org + */ +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "lnbp22.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + + +#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg) + +struct lnbp22 { + u8 config[4]; + struct i2c_adapter *i2c; +}; + +static int lnbp22_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv; + struct i2c_msg msg = { + .addr = 0x08, + .flags = 0, + .buf = (char *)&lnbp22->config, + .len = sizeof(lnbp22->config), + }; + + dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage, + SEC_VOLTAGE_18, SEC_VOLTAGE_13); + + lnbp22->config[3] = 0x60; /* Power down */ + switch (voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + lnbp22->config[3] |= LNBP22_EN; + break; + case SEC_VOLTAGE_18: + lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL); + break; + default: + return -EINVAL; + } + + dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]); + return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv; + struct i2c_msg msg = { + .addr = 0x08, + .flags = 0, + .buf = (char *)&lnbp22->config, + .len = sizeof(lnbp22->config), + }; + + dprintk(1, "%s: %d\n", __func__, (int)arg); + if (arg) + lnbp22->config[3] |= LNBP22_LLC; + else + lnbp22->config[3] &= ~LNBP22_LLC; + + return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void lnbp22_release(struct dvb_frontend *fe) +{ + dprintk(1, "%s\n", __func__); + /* LNBP power off */ + lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free data */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c) +{ + struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL); + if (!lnbp22) + return NULL; + + /* default configuration */ + lnbp22->config[0] = 0x00; /* ? */ + lnbp22->config[1] = 0x28; /* ? */ + lnbp22->config[2] = 0x48; /* ? */ + lnbp22->config[3] = 0x60; /* Power down */ + lnbp22->i2c = i2c; + fe->sec_priv = lnbp22; + + /* detect if it is present or not */ + if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) { + dprintk(0, "%s LNBP22 not found\n", __func__); + kfree(lnbp22); + fe->sec_priv = NULL; + return NULL; + } + + /* install release callback */ + fe->ops.release_sec = lnbp22_release; + + /* override frontend ops */ + fe->ops.set_voltage = lnbp22_set_voltage; + fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage; + + return fe; +} +EXPORT_SYMBOL_GPL(lnbp22_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22"); +MODULE_AUTHOR("Dominik Kuhlen"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h new file mode 100644 index 0000000000..35810d9d09 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbp22.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * lnbp22.h - driver for lnb supply and control ic lnbp22 + * + * Copyright (C) 2006 Dominik Kuhlen + * Based on lnbp21.h + * + * the project's page is at https://linuxtv.org + */ + +#ifndef _LNBP22_H +#define _LNBP22_H + +/* Enable */ +#define LNBP22_EN 0x10 +/* Voltage selection */ +#define LNBP22_VSEL 0x02 +/* Plus 1 Volt Bit */ +#define LNBP22_LLC 0x01 + +#include <linux/dvb/frontend.h> + +#if IS_REACHABLE(CONFIG_DVB_LNBP22) +/* + * override_set and override_clear control which system register bits (above) + * to always set & clear + */ +extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LNBP22 */ + +#endif /* _LNBP22_H */ diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c new file mode 100644 index 0000000000..affaa36d31 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -0,0 +1,1959 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Montage Technology M88DS3103/M88RS6000 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + */ + +#include "m88ds3103_priv.h" + +static const struct dvb_frontend_ops m88ds3103_ops; + +/* write single register with mask */ +static int m88ds3103_update_bits(struct m88ds3103_dev *dev, + u8 reg, u8 mask, u8 val) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return regmap_bulk_write(dev->regmap, reg, &val, 1); +} + +/* write reg val table using reg addr auto increment */ +static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev, + const struct m88ds3103_reg_val *tab, int tab_len) +{ + struct i2c_client *client = dev->client; + int ret, i, j; + u8 buf[83]; + + dev_dbg(&client->dev, "tab_len=%d\n", tab_len); + + if (tab_len > 86) { + ret = -EINVAL; + goto err; + } + + for (i = 0, j = 0; i < tab_len; i++, j++) { + buf[j] = tab[i].val; + + if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1 || + !((j + 1) % (dev->cfg->i2c_wr_max - 1))) { + ret = regmap_bulk_write(dev->regmap, tab[i].reg - j, buf, j + 1); + if (ret) + goto err; + + j = -1; + } + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +/* + * m88ds3103b demod has an internal device related to clocking. First the i2c + * gate must be opened, for one transaction, then writes will be allowed. + */ +static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data) +{ + struct i2c_client *client = dev->client; + u8 buf[] = {reg, data}; + u8 val; + int ret; + struct i2c_msg msg = { + .addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2 + }; + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + + val = 0x11; + ret = regmap_write(dev->regmap, 0x03, val); + if (ret) + dev_dbg(&client->dev, "fail=%d\n", ret); + + ret = i2c_transfer(dev->dt_client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n", + dev->dt_addr, ret, reg, data); + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + return -EREMOTEIO; + } + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n", + dev->dt_addr, reg, data); + + return 0; +} + +/* + * m88ds3103b demod has an internal device related to clocking. First the i2c + * gate must be opened, for two transactions, then reads will be allowed. + */ +static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg) +{ + struct i2c_client *client = dev->client; + int ret; + u8 val; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = dev->dt_addr, + .flags = 0, + .buf = b0, + .len = 1 + }, + { + .addr = dev->dt_addr, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + + val = 0x12; + ret = regmap_write(dev->regmap, 0x03, val); + if (ret) + dev_dbg(&client->dev, "fail=%d\n", ret); + + ret = i2c_transfer(dev->dt_client->adapter, msg, 2); + if (ret != 2) { + dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n", + dev->dt_addr, ret, reg); + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + return -EREMOTEIO; + } + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n", + dev->dt_addr, reg, b1[0]); + + return b1[0]; +} + +/* + * Get the demodulator AGC PWM voltage setting supplied to the tuner. + */ +int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + unsigned tmp; + int ret; + + ret = regmap_read(dev->regmap, 0x3f, &tmp); + if (ret == 0) + *_agc_pwm = tmp; + return ret; +} +EXPORT_SYMBOL(m88ds3103_get_agc_pwm); + +static int m88ds3103_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, itmp; + unsigned int utmp; + u8 buf[3]; + + *status = 0; + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBS: + ret = regmap_read(dev->regmap, 0xd1, &utmp); + if (ret) + goto err; + + if ((utmp & 0x07) == 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + break; + case SYS_DVBS2: + ret = regmap_read(dev->regmap, 0x0d, &utmp); + if (ret) + goto err; + + if ((utmp & 0x8f) == 0x8f) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + dev->fe_status = *status; + dev_dbg(&client->dev, "lock=%02x status=%02x\n", utmp, *status); + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned int cnr, noise, signal, noise_tot, signal_tot; + + cnr = 0; + /* more iterations for more accurate estimation */ + #define M88DS3103_SNR_ITERATIONS 3 + + switch (c->delivery_system) { + case SYS_DVBS: + itmp = 0; + + for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { + ret = regmap_read(dev->regmap, 0xff, &utmp); + if (ret) + goto err; + + itmp += utmp; + } + + /* use of single register limits max value to 15 dB */ + /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ + itmp = DIV_ROUND_CLOSEST(itmp, 8 * M88DS3103_SNR_ITERATIONS); + if (itmp) + cnr = div_u64((u64) 10000 * intlog2(itmp), intlog2(10)); + break; + case SYS_DVBS2: + noise_tot = 0; + signal_tot = 0; + + for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { + ret = regmap_bulk_read(dev->regmap, 0x8c, buf, 3); + if (ret) + goto err; + + noise = buf[1] << 6; /* [13:6] */ + noise |= buf[0] & 0x3f; /* [5:0] */ + noise >>= 2; + signal = buf[2] * buf[2]; + signal >>= 1; + + noise_tot += noise; + signal_tot += signal; + } + + noise = noise_tot / M88DS3103_SNR_ITERATIONS; + signal = signal_tot / M88DS3103_SNR_ITERATIONS; + + /* SNR(X) dB = 10 * log10(X) dB */ + if (signal > noise) { + itmp = signal / noise; + cnr = div_u64((u64) 10000 * intlog10(itmp), (1 << 24)); + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + if (cnr) { + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + unsigned int utmp, post_bit_error, post_bit_count; + + switch (c->delivery_system) { + case SYS_DVBS: + ret = regmap_write(dev->regmap, 0xf9, 0x04); + if (ret) + goto err; + + ret = regmap_read(dev->regmap, 0xf8, &utmp); + if (ret) + goto err; + + /* measurement ready? */ + if (!(utmp & 0x10)) { + ret = regmap_bulk_read(dev->regmap, 0xf6, buf, 2); + if (ret) + goto err; + + post_bit_error = buf[1] << 8 | buf[0] << 0; + post_bit_count = 0x800000; + dev->post_bit_error += post_bit_error; + dev->post_bit_count += post_bit_count; + dev->dvbv3_ber = post_bit_error; + + /* restart measurement */ + utmp |= 0x10; + ret = regmap_write(dev->regmap, 0xf8, utmp); + if (ret) + goto err; + } + break; + case SYS_DVBS2: + ret = regmap_bulk_read(dev->regmap, 0xd5, buf, 3); + if (ret) + goto err; + + utmp = buf[2] << 16 | buf[1] << 8 | buf[0] << 0; + + /* enough data? */ + if (utmp > 4000) { + ret = regmap_bulk_read(dev->regmap, 0xf7, buf, 2); + if (ret) + goto err; + + post_bit_error = buf[1] << 8 | buf[0] << 0; + post_bit_count = 32 * utmp; /* TODO: FEC */ + dev->post_bit_error += post_bit_error; + dev->post_bit_count += post_bit_count; + dev->dvbv3_ber = post_bit_error; + + /* restart measurement */ + ret = regmap_write(dev->regmap, 0xd1, 0x01); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xf9, 0x01); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xf9, 0x00); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xd1, 0x00); + if (ret) + goto err; + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev) +{ + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + u32 adc_Freq_MHz[3] = {96, 93, 99}; + u8 reg16_list[3] = {96, 92, 100}, reg16, reg15; + u32 offset_MHz[3]; + u32 max_offset = 0; + u32 old_setting = dev->mclk; + u32 tuner_freq_MHz = c->frequency / 1000; + u8 i; + char big_symbol = 0; + + big_symbol = (c->symbol_rate > 45010000) ? 1 : 0; + + if (big_symbol) { + reg16 = 115; + } else { + reg16 = 96; + + /* TODO: IS THIS NECESSARY ? */ + for (i = 0; i < 3; i++) { + offset_MHz[i] = tuner_freq_MHz % adc_Freq_MHz[i]; + + if (offset_MHz[i] > (adc_Freq_MHz[i] / 2)) + offset_MHz[i] = adc_Freq_MHz[i] - offset_MHz[i]; + + if (offset_MHz[i] > max_offset) { + max_offset = offset_MHz[i]; + reg16 = reg16_list[i]; + dev->mclk = adc_Freq_MHz[i] * 1000 * 1000; + + if (big_symbol) + dev->mclk /= 2; + + dev_dbg(&client->dev, "modifying mclk %u -> %u\n", + old_setting, dev->mclk); + } + } + } + + if (dev->mclk == 93000000) + regmap_write(dev->regmap, 0xA0, 0x42); + else if (dev->mclk == 96000000) + regmap_write(dev->regmap, 0xA0, 0x44); + else if (dev->mclk == 99000000) + regmap_write(dev->regmap, 0xA0, 0x46); + else if (dev->mclk == 110250000) + regmap_write(dev->regmap, 0xA0, 0x4E); + else + regmap_write(dev->regmap, 0xA0, 0x44); + + reg15 = m88ds3103b_dt_read(dev, 0x15); + + m88ds3103b_dt_write(dev, 0x05, 0x40); + m88ds3103b_dt_write(dev, 0x11, 0x08); + + if (big_symbol) + reg15 |= 0x02; + else + reg15 &= ~0x02; + + m88ds3103b_dt_write(dev, 0x15, reg15); + m88ds3103b_dt_write(dev, 0x16, reg16); + + usleep_range(5000, 5500); + + m88ds3103b_dt_write(dev, 0x05, 0x00); + m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A)); + + usleep_range(5000, 5500); + + return 0; +} + +static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz) +{ + u8 reg15, reg16, reg1D, reg1E, reg1F, tmp; + u8 sm, f0 = 0, f1 = 0, f2 = 0, f3 = 0; + u16 pll_div_fb, N; + u32 div; + + reg15 = m88ds3103b_dt_read(dev, 0x15); + reg16 = m88ds3103b_dt_read(dev, 0x16); + reg1D = m88ds3103b_dt_read(dev, 0x1D); + + if (dev->cfg->ts_mode != M88DS3103_TS_SERIAL) { + if (reg16 == 92) + tmp = 93; + else if (reg16 == 100) + tmp = 99; + else + tmp = 96; + + mclk_khz *= tmp; + mclk_khz /= 96; + } + + pll_div_fb = (reg15 & 0x01) << 8; + pll_div_fb += reg16; + pll_div_fb += 32; + + div = 9000 * pll_div_fb * 4; + div /= mclk_khz; + + if (dev->cfg->ts_mode == M88DS3103_TS_SERIAL) { + if (div <= 32) { + N = 2; + + f0 = 0; + f1 = div / N; + f2 = div - f1; + f3 = 0; + } else if (div <= 34) { + N = 3; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = div - f0 - f1; + f3 = 0; + } else if (div <= 64) { + N = 4; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = (div - f0 - f1) / (N - 2); + f3 = div - f0 - f1 - f2; + } else { + N = 4; + + f0 = 16; + f1 = 16; + f2 = 16; + f3 = 16; + } + + if (f0 == 16) + f0 = 0; + else if ((f0 < 8) && (f0 != 0)) + f0 = 8; + + if (f1 == 16) + f1 = 0; + else if ((f1 < 8) && (f1 != 0)) + f1 = 8; + + if (f2 == 16) + f2 = 0; + else if ((f2 < 8) && (f2 != 0)) + f2 = 8; + + if (f3 == 16) + f3 = 0; + else if ((f3 < 8) && (f3 != 0)) + f3 = 8; + } else { + if (div <= 32) { + N = 2; + + f0 = 0; + f1 = div / N; + f2 = div - f1; + f3 = 0; + } else if (div <= 48) { + N = 3; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = div - f0 - f1; + f3 = 0; + } else if (div <= 64) { + N = 4; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = (div - f0 - f1) / (N - 2); + f3 = div - f0 - f1 - f2; + } else { + N = 4; + + f0 = 16; + f1 = 16; + f2 = 16; + f3 = 16; + } + + if (f0 == 16) + f0 = 0; + else if ((f0 < 9) && (f0 != 0)) + f0 = 9; + + if (f1 == 16) + f1 = 0; + else if ((f1 < 9) && (f1 != 0)) + f1 = 9; + + if (f2 == 16) + f2 = 0; + else if ((f2 < 9) && (f2 != 0)) + f2 = 9; + + if (f3 == 16) + f3 = 0; + else if ((f3 < 9) && (f3 != 0)) + f3 = 9; + } + + sm = N - 1; + + /* Write to registers */ + //reg15 &= 0x01; + //reg15 |= (pll_div_fb >> 8) & 0x01; + + //reg16 = pll_div_fb & 0xFF; + + reg1D &= ~0x03; + reg1D |= sm; + reg1D |= 0x80; + + reg1E = ((f3 << 4) + f2) & 0xFF; + reg1F = ((f1 << 4) + f0) & 0xFF; + + m88ds3103b_dt_write(dev, 0x05, 0x40); + m88ds3103b_dt_write(dev, 0x11, 0x08); + m88ds3103b_dt_write(dev, 0x1D, reg1D); + m88ds3103b_dt_write(dev, 0x1E, reg1E); + m88ds3103b_dt_write(dev, 0x1F, reg1F); + + m88ds3103b_dt_write(dev, 0x17, 0xc1); + m88ds3103b_dt_write(dev, 0x17, 0x81); + + usleep_range(5000, 5500); + + m88ds3103b_dt_write(dev, 0x05, 0x00); + m88ds3103b_dt_write(dev, 0x11, 0x0A); + + usleep_range(5000, 5500); + + return 0; +} + +static int m88ds3103_set_frontend(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, len; + const struct m88ds3103_reg_val *init; + u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */ + u8 buf[3]; + u16 u16tmp; + u32 tuner_frequency_khz, target_mclk, u32tmp; + s32 s32tmp; + static const struct reg_sequence reset_buf[] = { + {0x07, 0x80}, {0x07, 0x00} + }; + + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + c->delivery_system, c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + /* reset */ + ret = regmap_multi_reg_write(dev->regmap, reset_buf, 2); + if (ret) + goto err; + + /* Disable demod clock path */ + if (dev->chip_id == M88RS6000_CHIP_ID) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = regmap_read(dev->regmap, 0xb2, &u32tmp); + if (ret) + goto err; + if (u32tmp == 0x01) { + ret = regmap_write(dev->regmap, 0x00, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err; + } + } + + ret = regmap_write(dev->regmap, 0x06, 0xe0); + if (ret) + goto err; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + if (fe->ops.tuner_ops.get_frequency) { + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz); + if (ret) + goto err; + } else { + /* + * Use nominal target frequency as tuner driver does not provide + * actual frequency used. Carrier offset calculation is not + * valid. + */ + tuner_frequency_khz = c->frequency; + } + + /* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */ + if (dev->chip_id == M88RS6000_CHIP_ID) { + if (c->symbol_rate > 45010000) + dev->mclk = 110250000; + else + dev->mclk = 96000000; + + if (c->delivery_system == SYS_DVBS) + target_mclk = 96000000; + else + target_mclk = 144000000; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_select_mclk(dev); + m88ds3103b_set_mclk(dev, target_mclk / 1000); + } + + /* Enable demod clock path */ + ret = regmap_write(dev->regmap, 0x06, 0x00); + if (ret) + goto err; + usleep_range(10000, 20000); + } else { + /* set M88DS3103 mclk and ts mclk. */ + dev->mclk = 96000000; + + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: + target_mclk = dev->cfg->ts_clk; + break; + case M88DS3103_TS_PARALLEL: + case M88DS3103_TS_CI: + if (c->delivery_system == SYS_DVBS) + target_mclk = 96000000; + else { + if (c->symbol_rate < 18000000) + target_mclk = 96000000; + else if (c->symbol_rate < 28000000) + target_mclk = 144000000; + else + target_mclk = 192000000; + } + break; + default: + dev_dbg(&client->dev, "invalid ts_mode\n"); + ret = -EINVAL; + goto err; + } + + switch (target_mclk) { + case 96000000: + u8tmp1 = 0x02; /* 0b10 */ + u8tmp2 = 0x01; /* 0b01 */ + break; + case 144000000: + u8tmp1 = 0x00; /* 0b00 */ + u8tmp2 = 0x01; /* 0b01 */ + break; + case 192000000: + u8tmp1 = 0x03; /* 0b11 */ + u8tmp2 = 0x00; /* 0b00 */ + break; + } + ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6); + if (ret) + goto err; + } + + ret = regmap_write(dev->regmap, 0xb2, 0x01); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x00, 0x01); + if (ret) + goto err; + + switch (c->delivery_system) { + case SYS_DVBS: + if (dev->chip_id == M88RS6000_CHIP_ID) { + len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); + init = m88rs6000_dvbs_init_reg_vals; + } else { + len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); + init = m88ds3103_dvbs_init_reg_vals; + } + break; + case SYS_DVBS2: + if (dev->chip_id == M88RS6000_CHIP_ID) { + len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); + init = m88rs6000_dvbs2_init_reg_vals; + } else { + len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); + init = m88ds3103_dvbs2_init_reg_vals; + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + /* program init table */ + if (c->delivery_system != dev->delivery_system) { + ret = m88ds3103_wr_reg_val_tab(dev, init, len); + if (ret) + goto err; + } + + if (dev->chip_id == M88RS6000_CHIP_ID) { + if (c->delivery_system == SYS_DVBS2 && + c->symbol_rate <= 5000000) { + ret = regmap_write(dev->regmap, 0xc0, 0x04); + if (ret) + goto err; + buf[0] = 0x09; + buf[1] = 0x22; + buf[2] = 0x88; + ret = regmap_bulk_write(dev->regmap, 0x8a, buf, 3); + if (ret) + goto err; + } + ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); + if (ret) + goto err; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + buf[0] = m88ds3103b_dt_read(dev, 0x15); + buf[1] = m88ds3103b_dt_read(dev, 0x16); + + if (c->symbol_rate > 45010000) { + buf[0] &= ~0x03; + buf[0] |= 0x02; + buf[0] |= ((147 - 32) >> 8) & 0x01; + buf[1] = (147 - 32) & 0xFF; + + dev->mclk = 110250 * 1000; + } else { + buf[0] &= ~0x03; + buf[0] |= ((128 - 32) >> 8) & 0x01; + buf[1] = (128 - 32) & 0xFF; + + dev->mclk = 96000 * 1000; + } + m88ds3103b_dt_write(dev, 0x15, buf[0]); + m88ds3103b_dt_write(dev, 0x16, buf[1]); + + regmap_read(dev->regmap, 0x30, &u32tmp); + u32tmp &= ~0x80; + regmap_write(dev->regmap, 0x30, u32tmp & 0xff); + } + + ret = regmap_write(dev->regmap, 0xf1, 0x01); + if (ret) + goto err; + + if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); + if (ret) + goto err; + } + } + + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + u8tmp1 = 0x00; + u8tmp = 0x06; + break; + case M88DS3103_TS_SERIAL_D7: + u8tmp1 = 0x20; + u8tmp = 0x06; + break; + case M88DS3103_TS_PARALLEL: + u8tmp = 0x02; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + u8tmp = 0x01; + u8tmp1 = 0x01; + } + break; + case M88DS3103_TS_CI: + u8tmp = 0x03; + break; + default: + dev_dbg(&client->dev, "invalid ts_mode\n"); + ret = -EINVAL; + goto err; + } + + if (dev->cfg->ts_clk_pol) + u8tmp |= 0x40; + + /* TS mode */ + ret = regmap_write(dev->regmap, 0xfd, u8tmp); + if (ret) + goto err; + + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: + ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); + if (ret) + goto err; + u16tmp = 0; + u8tmp1 = 0x3f; + u8tmp2 = 0x3f; + break; + case M88DS3103_TS_PARALLEL: + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x29, 0x01, u8tmp1); + if (ret) + goto err; + } + fallthrough; + default: + u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); + u8tmp1 = u16tmp / 2 - 1; + u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1; + } + + dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n", + target_mclk, dev->cfg->ts_clk, u16tmp); + + /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */ + /* u8tmp2[5:0] => ea[5:0] */ + u8tmp = (u8tmp1 >> 2) & 0x0f; + ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp); + if (ret) + goto err; + u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; + ret = regmap_write(dev->regmap, 0xea, u8tmp); + if (ret) + goto err; + + if (c->symbol_rate <= 3000000) + u8tmp = 0x20; + else if (c->symbol_rate <= 10000000) + u8tmp = 0x10; + else + u8tmp = 0x06; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + m88ds3103b_set_mclk(dev, target_mclk / 1000); + + ret = regmap_write(dev->regmap, 0xc3, 0x08); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xc8, u8tmp); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xc4, 0x08); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xc7, 0x00); + if (ret) + goto err; + + u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk); + buf[0] = (u16tmp >> 0) & 0xff; + buf[1] = (u16tmp >> 8) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x33, dev->cfg->agc); + if (ret) + goto err; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + /* enable/disable 192M LDPC clock */ + ret = m88ds3103_update_bits(dev, 0x29, 0x10, + (c->delivery_system == SYS_DVBS) ? 0x10 : 0x0); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08); + if (ret) + goto err; + } + + dev_dbg(&client->dev, "carrier offset=%d\n", + (tuner_frequency_khz - c->frequency)); + + /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */ + s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency); + s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000); + buf[0] = (s32tmp >> 0) & 0xff; + buf[1] = (s32tmp >> 8) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x00, 0x00); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err; + + dev->delivery_system = c->delivery_system; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_init(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, len, rem; + unsigned int utmp; + const struct firmware *firmware; + const char *name; + + dev_dbg(&client->dev, "\n"); + + /* set cold state by default */ + dev->warm = false; + + /* wake up device from sleep */ + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00); + if (ret) + goto err; + + /* firmware status */ + ret = regmap_read(dev->regmap, 0xb9, &utmp); + if (ret) + goto err; + + dev_dbg(&client->dev, "firmware=%02x\n", utmp); + + if (utmp) + goto warm; + + /* global reset, global diseqc reset, global fec reset */ + ret = regmap_write(dev->regmap, 0x07, 0xe0); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x07, 0x00); + if (ret) + goto err; + + /* cold state - try to download firmware */ + dev_info(&client->dev, "found a '%s' in cold state\n", + dev->fe.ops.info.name); + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + name = M88DS3103B_FIRMWARE; + else if (dev->chip_id == M88RS6000_CHIP_ID) + name = M88RS6000_FIRMWARE; + else + name = M88DS3103_FIRMWARE; + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&firmware, name, &client->dev); + if (ret) { + dev_err(&client->dev, "firmware file '%s' not found\n", name); + goto err; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); + + ret = regmap_write(dev->regmap, 0xb2, 0x01); + if (ret) + goto err_release_firmware; + + for (rem = firmware->size; rem > 0; rem -= (dev->cfg->i2c_wr_max - 1)) { + len = min(dev->cfg->i2c_wr_max - 1, rem); + ret = regmap_bulk_write(dev->regmap, 0xb0, + &firmware->data[firmware->size - rem], + len); + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; + } + } + + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err_release_firmware; + + release_firmware(firmware); + + ret = regmap_read(dev->regmap, 0xb9, &utmp); + if (ret) + goto err; + + if (!utmp) { + ret = -EINVAL; + dev_info(&client->dev, "firmware did not run\n"); + goto err; + } + + dev_info(&client->dev, "found a '%s' in warm state\n", + dev->fe.ops.info.name); + dev_info(&client->dev, "firmware version: %X.%X\n", + (utmp >> 4) & 0xf, (utmp >> 0 & 0xf)); + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_dt_write(dev, 0x21, 0x92); + m88ds3103b_dt_write(dev, 0x15, 0x6C); + m88ds3103b_dt_write(dev, 0x17, 0xC1); + m88ds3103b_dt_write(dev, 0x17, 0x81); + } +warm: + /* warm state */ + dev->warm = true; + + /* init stats here in order signal app which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_sleep(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp; + + dev_dbg(&client->dev, "\n"); + + dev->fe_status = 0; + dev->delivery_system = SYS_UNDEFINED; + + /* TS Hi-Z */ + if (dev->chip_id == M88RS6000_CHIP_ID) + utmp = 0x29; + else + utmp = 0x27; + ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00); + if (ret) + goto err; + + /* sleep */ + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 buf[3]; + + dev_dbg(&client->dev, "\n"); + + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + ret = 0; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBS: + ret = regmap_bulk_read(dev->regmap, 0xe0, &buf[0], 1); + if (ret) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0xe6, &buf[1], 1); + if (ret) + goto err; + + switch ((buf[0] >> 2) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + switch ((buf[1] >> 5) & 0x07) { + case 0: + c->fec_inner = FEC_7_8; + break; + case 1: + c->fec_inner = FEC_5_6; + break; + case 2: + c->fec_inner = FEC_3_4; + break; + case 3: + c->fec_inner = FEC_2_3; + break; + case 4: + c->fec_inner = FEC_1_2; + break; + default: + dev_dbg(&client->dev, "invalid fec_inner\n"); + } + + c->modulation = QPSK; + + break; + case SYS_DVBS2: + ret = regmap_bulk_read(dev->regmap, 0x7e, &buf[0], 1); + if (ret) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0x89, &buf[1], 1); + if (ret) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0xf2, &buf[2], 1); + if (ret) + goto err; + + switch ((buf[0] >> 0) & 0x0f) { + case 2: + c->fec_inner = FEC_2_5; + break; + case 3: + c->fec_inner = FEC_1_2; + break; + case 4: + c->fec_inner = FEC_3_5; + break; + case 5: + c->fec_inner = FEC_2_3; + break; + case 6: + c->fec_inner = FEC_3_4; + break; + case 7: + c->fec_inner = FEC_4_5; + break; + case 8: + c->fec_inner = FEC_5_6; + break; + case 9: + c->fec_inner = FEC_8_9; + break; + case 10: + c->fec_inner = FEC_9_10; + break; + default: + dev_dbg(&client->dev, "invalid fec_inner\n"); + } + + switch ((buf[0] >> 5) & 0x01) { + case 0: + c->pilot = PILOT_OFF; + break; + case 1: + c->pilot = PILOT_ON; + break; + } + + switch ((buf[0] >> 6) & 0x07) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = PSK_8; + break; + case 2: + c->modulation = APSK_16; + break; + case 3: + c->modulation = APSK_32; + break; + default: + dev_dbg(&client->dev, "invalid modulation\n"); + } + + switch ((buf[1] >> 7) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + switch ((buf[2] >> 0) & 0x03) { + case 0: + c->rolloff = ROLLOFF_35; + break; + case 1: + c->rolloff = ROLLOFF_25; + break; + case 2: + c->rolloff = ROLLOFF_20; + break; + default: + dev_dbg(&client->dev, "invalid rolloff\n"); + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + ret = regmap_bulk_read(dev->regmap, 0x6d, buf, 2); + if (ret) + goto err; + + c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + + return 0; +} + +static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + + *ber = dev->dvbv3_ber; + + return 0; +} + +static int m88ds3103_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode fe_sec_tone_mode) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp, tone, reg_a1_mask; + + dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode); + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + switch (fe_sec_tone_mode) { + case SEC_TONE_ON: + tone = 0; + reg_a1_mask = 0x47; + break; + case SEC_TONE_OFF: + tone = 1; + reg_a1_mask = 0x00; + break; + default: + dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n"); + ret = -EINVAL; + goto err; + } + + utmp = tone << 7 | dev->cfg->envelope_mode << 5; + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); + if (ret) + goto err; + + utmp = 1 << 2; + ret = m88ds3103_update_bits(dev, 0xa1, reg_a1_mask, utmp); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage fe_sec_voltage) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp; + bool voltage_sel, voltage_dis; + + dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage); + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + switch (fe_sec_voltage) { + case SEC_VOLTAGE_18: + voltage_sel = true; + voltage_dis = false; + break; + case SEC_VOLTAGE_13: + voltage_sel = false; + voltage_dis = false; + break; + case SEC_VOLTAGE_OFF: + voltage_sel = false; + voltage_dis = true; + break; + default: + dev_dbg(&client->dev, "invalid fe_sec_voltage\n"); + ret = -EINVAL; + goto err; + } + + /* output pin polarity */ + voltage_sel ^= dev->cfg->lnb_hv_pol; + voltage_dis ^= dev->cfg->lnb_en_pol; + + utmp = voltage_dis << 1 | voltage_sel << 0; + ret = m88ds3103_update_bits(dev, 0xa2, 0x03, utmp); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *diseqc_cmd) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp; + unsigned long timeout; + + dev_dbg(&client->dev, "msg=%*ph\n", + diseqc_cmd->msg_len, diseqc_cmd->msg); + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { + ret = -EINVAL; + goto err; + } + + utmp = dev->cfg->envelope_mode << 5; + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0xa3, diseqc_cmd->msg, + diseqc_cmd->msg_len); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xa1, + (diseqc_cmd->msg_len - 1) << 3 | 0x07); + if (ret) + goto err; + + /* wait DiSEqC TX ready */ + #define SEND_MASTER_CMD_TIMEOUT 120 + timeout = jiffies + msecs_to_jiffies(SEND_MASTER_CMD_TIMEOUT); + + /* DiSEqC message period is 13.5 ms per byte */ + utmp = diseqc_cmd->msg_len * 13500; + usleep_range(utmp - 4000, utmp); + + for (utmp = 1; !time_after(jiffies, timeout) && utmp;) { + ret = regmap_read(dev->regmap, 0xa1, &utmp); + if (ret) + goto err; + utmp = (utmp >> 6) & 0x1; + } + + if (utmp == 0) { + dev_dbg(&client->dev, "diseqc tx took %u ms\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - SEND_MASTER_CMD_TIMEOUT)); + } else { + dev_dbg(&client->dev, "diseqc tx timeout\n"); + + ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); + if (ret) + goto err; + } + + ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); + if (ret) + goto err; + + if (utmp == 1) { + ret = -ETIMEDOUT; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd fe_sec_mini_cmd) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp, burst; + unsigned long timeout; + + dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd); + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + utmp = dev->cfg->envelope_mode << 5; + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); + if (ret) + goto err; + + switch (fe_sec_mini_cmd) { + case SEC_MINI_A: + burst = 0x02; + break; + case SEC_MINI_B: + burst = 0x01; + break; + default: + dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n"); + ret = -EINVAL; + goto err; + } + + ret = regmap_write(dev->regmap, 0xa1, burst); + if (ret) + goto err; + + /* wait DiSEqC TX ready */ + #define SEND_BURST_TIMEOUT 40 + timeout = jiffies + msecs_to_jiffies(SEND_BURST_TIMEOUT); + + /* DiSEqC ToneBurst period is 12.5 ms */ + usleep_range(8500, 12500); + + for (utmp = 1; !time_after(jiffies, timeout) && utmp;) { + ret = regmap_read(dev->regmap, 0xa1, &utmp); + if (ret) + goto err; + utmp = (utmp >> 6) & 0x1; + } + + if (utmp == 0) { + dev_dbg(&client->dev, "diseqc tx took %u ms\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - SEND_BURST_TIMEOUT)); + } else { + dev_dbg(&client->dev, "diseqc tx timeout\n"); + + ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); + if (ret) + goto err; + } + + ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); + if (ret) + goto err; + + if (utmp == 1) { + ret = -ETIMEDOUT; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 3000; + + return 0; +} + +static void m88ds3103_release(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + + i2c_unregister_device(client); +} + +static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct m88ds3103_dev *dev = i2c_mux_priv(muxc); + struct i2c_client *client = dev->client; + int ret; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = "\x03\x11", + }; + + /* Open tuner I2C repeater for 1 xfer, closes automatically */ + ret = __i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c wr failed=%d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + + return 0; +} + +/* + * XXX: That is wrapper to m88ds3103_probe() via driver core in order to provide + * proper I2C client for legacy media attach binding. + * New users must use I2C client binding directly! + */ +struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, + struct i2c_adapter *i2c, + struct i2c_adapter **tuner_i2c_adapter) +{ + struct i2c_client *client; + struct i2c_board_info board_info; + struct m88ds3103_platform_data pdata = {}; + + pdata.clk = cfg->clock; + pdata.i2c_wr_max = cfg->i2c_wr_max; + pdata.ts_mode = cfg->ts_mode; + pdata.ts_clk = cfg->ts_clk; + pdata.ts_clk_pol = cfg->ts_clk_pol; + pdata.spec_inv = cfg->spec_inv; + pdata.agc = cfg->agc; + pdata.agc_inv = cfg->agc_inv; + pdata.clk_out = cfg->clock_out; + pdata.envelope_mode = cfg->envelope_mode; + pdata.lnb_hv_pol = cfg->lnb_hv_pol; + pdata.lnb_en_pol = cfg->lnb_en_pol; + pdata.attach_in_use = true; + + memset(&board_info, 0, sizeof(board_info)); + strscpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); + board_info.addr = cfg->i2c_addr; + board_info.platform_data = &pdata; + client = i2c_new_client_device(i2c, &board_info); + if (!i2c_client_has_driver(client)) + return NULL; + + *tuner_i2c_adapter = pdata.get_i2c_adapter(client); + return pdata.get_dvb_frontend(client); +} +EXPORT_SYMBOL_GPL(m88ds3103_attach); + +static const struct dvb_frontend_ops m88ds3103_ops = { + .delsys = {SYS_DVBS, SYS_DVBS2}, + .info = { + .name = "Montage Technology M88DS3103", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_RECOVER | + FE_CAN_2G_MODULATION + }, + + .release = m88ds3103_release, + + .get_tune_settings = m88ds3103_get_tune_settings, + + .init = m88ds3103_init, + .sleep = m88ds3103_sleep, + + .set_frontend = m88ds3103_set_frontend, + .get_frontend = m88ds3103_get_frontend, + + .read_status = m88ds3103_read_status, + .read_snr = m88ds3103_read_snr, + .read_ber = m88ds3103_read_ber, + + .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, + .diseqc_send_burst = m88ds3103_diseqc_send_burst, + + .set_tone = m88ds3103_set_tone, + .set_voltage = m88ds3103_set_voltage, +}; + +static struct dvb_frontend *m88ds3103_get_dvb_frontend(struct i2c_client *client) +{ + struct m88ds3103_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client) +{ + struct m88ds3103_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return dev->muxc->adapter[0]; +} + +static int m88ds3103_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct m88ds3103_dev *dev; + struct m88ds3103_platform_data *pdata = client->dev.platform_data; + int ret; + unsigned int utmp; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->client = client; + dev->config.clock = pdata->clk; + dev->config.i2c_wr_max = pdata->i2c_wr_max; + dev->config.ts_mode = pdata->ts_mode; + dev->config.ts_clk = pdata->ts_clk * 1000; + dev->config.ts_clk_pol = pdata->ts_clk_pol; + dev->config.spec_inv = pdata->spec_inv; + dev->config.agc_inv = pdata->agc_inv; + dev->config.clock_out = pdata->clk_out; + dev->config.envelope_mode = pdata->envelope_mode; + dev->config.agc = pdata->agc; + dev->config.lnb_hv_pol = pdata->lnb_hv_pol; + dev->config.lnb_en_pol = pdata->lnb_en_pol; + dev->cfg = &dev->config; + /* create regmap */ + dev->regmap_config.reg_bits = 8; + dev->regmap_config.val_bits = 8; + dev->regmap_config.lock_arg = dev; + dev->regmap = devm_regmap_init_i2c(client, &dev->regmap_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */ + ret = regmap_read(dev->regmap, 0x00, &utmp); + if (ret) + goto err_kfree; + + dev->chip_id = utmp >> 1; + dev->chiptype = (u8)id->driver_data; + + dev_dbg(&client->dev, "chip_id=%02x\n", dev->chip_id); + + switch (dev->chip_id) { + case M88RS6000_CHIP_ID: + case M88DS3103_CHIP_ID: + break; + default: + ret = -ENODEV; + dev_err(&client->dev, "Unknown device. Chip_id=%02x\n", dev->chip_id); + goto err_kfree; + } + + switch (dev->cfg->clock_out) { + case M88DS3103_CLOCK_OUT_DISABLED: + utmp = 0x80; + break; + case M88DS3103_CLOCK_OUT_ENABLED: + utmp = 0x00; + break; + case M88DS3103_CLOCK_OUT_ENABLED_DIV2: + utmp = 0x10; + break; + default: + ret = -EINVAL; + goto err_kfree; + } + + if (!pdata->ts_clk) { + ret = -EINVAL; + goto err_kfree; + } + + /* 0x29 register is defined differently for m88rs6000. */ + /* set internal tuner address to 0x21 */ + if (dev->chip_id == M88RS6000_CHIP_ID) + utmp = 0x00; + + ret = regmap_write(dev->regmap, 0x29, utmp); + if (ret) + goto err_kfree; + + /* sleep */ + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); + if (ret) + goto err_kfree; + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); + if (ret) + goto err_kfree; + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); + if (ret) + goto err_kfree; + + /* create mux i2c adapter for tuner */ + dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0, + m88ds3103_select, NULL); + if (!dev->muxc) { + ret = -ENOMEM; + goto err_kfree; + } + dev->muxc->priv = dev; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_kfree; + + /* create dvb_frontend */ + memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B", + sizeof(dev->fe.ops.info.name)); + else if (dev->chip_id == M88RS6000_CHIP_ID) + strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000", + sizeof(dev->fe.ops.info.name)); + if (!pdata->attach_in_use) + dev->fe.ops.release = NULL; + dev->fe.demodulator_priv = dev; + i2c_set_clientdata(client, dev); + + /* setup callbacks */ + pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend; + pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + /* enable i2c repeater for tuner */ + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + /* get frontend address */ + ret = regmap_read(dev->regmap, 0x29, &utmp); + if (ret) + goto err_del_adapters; + dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1; + dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr); + + dev->dt_client = i2c_new_dummy_device(client->adapter, + dev->dt_addr); + if (IS_ERR(dev->dt_client)) { + ret = PTR_ERR(dev->dt_client); + goto err_del_adapters; + } + } + + return 0; + +err_del_adapters: + i2c_mux_del_adapters(dev->muxc); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void m88ds3103_remove(struct i2c_client *client) +{ + struct m88ds3103_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + if (dev->dt_client) + i2c_unregister_device(dev->dt_client); + + i2c_mux_del_adapters(dev->muxc); + + kfree(dev); +} + +static const struct i2c_device_id m88ds3103_id_table[] = { + {"m88ds3103", M88DS3103_CHIPTYPE_3103}, + {"m88rs6000", M88DS3103_CHIPTYPE_RS6000}, + {"m88ds3103b", M88DS3103_CHIPTYPE_3103B}, + {} +}; +MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); + +static struct i2c_driver m88ds3103_driver = { + .driver = { + .name = "m88ds3103", + .suppress_bind_attrs = true, + }, + .probe = m88ds3103_probe, + .remove = m88ds3103_remove, + .id_table = m88ds3103_id_table, +}; + +module_i2c_driver(m88ds3103_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Montage Technology M88DS3103 DVB-S/S2 demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(M88DS3103_FIRMWARE); +MODULE_FIRMWARE(M88RS6000_FIRMWARE); +MODULE_FIRMWARE(M88DS3103B_FIRMWARE); diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h new file mode 100644 index 0000000000..e32b68c0df --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Montage Technology M88DS3103/M88RS6000 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + */ + +#ifndef M88DS3103_H +#define M88DS3103_H + +#include <linux/dvb/frontend.h> + +/* + * I2C address + * 0x68, + */ + +/** + * enum m88ds3103_ts_mode - TS connection mode + * @M88DS3103_TS_SERIAL: TS output pin D0, normal + * @M88DS3103_TS_SERIAL_D7: TS output pin D7 + * @M88DS3103_TS_PARALLEL: TS Parallel mode + * @M88DS3103_TS_CI: TS CI Mode + */ +enum m88ds3103_ts_mode { + M88DS3103_TS_SERIAL, + M88DS3103_TS_SERIAL_D7, + M88DS3103_TS_PARALLEL, + M88DS3103_TS_CI +}; + +/** + * enum m88ds3103_clock_out + * @M88DS3103_CLOCK_OUT_DISABLED: Clock output is disabled + * @M88DS3103_CLOCK_OUT_ENABLED: Clock output is enabled with crystal + * clock. + * @M88DS3103_CLOCK_OUT_ENABLED_DIV2: Clock output is enabled with half + * crystal clock. + */ +enum m88ds3103_clock_out { + M88DS3103_CLOCK_OUT_DISABLED, + M88DS3103_CLOCK_OUT_ENABLED, + M88DS3103_CLOCK_OUT_ENABLED_DIV2 +}; + +/** + * struct m88ds3103_platform_data - Platform data for the m88ds3103 driver + * @clk: Clock frequency. + * @i2c_wr_max: Max bytes I2C adapter can write at once. + * @ts_mode: TS mode. + * @ts_clk: TS clock (KHz). + * @ts_clk_pol: TS clk polarity. 1-active at falling edge; 0-active at rising + * edge. + * @spec_inv: Input spectrum inversion. + * @agc: AGC configuration. + * @agc_inv: AGC polarity. + * @clk_out: Clock output. + * @envelope_mode: DiSEqC envelope mode. + * @lnb_hv_pol: LNB H/V pin polarity. 0: pin high set to VOLTAGE_18, pin low to + * set VOLTAGE_13. 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18. + * @lnb_en_pol: LNB enable pin polarity. 0: pin high to disable, pin low to + * enable. 1: pin high to enable, pin low to disable. + * @get_dvb_frontend: Get DVB frontend. + * @get_i2c_adapter: Get I2C adapter. + */ +struct m88ds3103_platform_data { + u32 clk; + u16 i2c_wr_max; + enum m88ds3103_ts_mode ts_mode; + u32 ts_clk; + enum m88ds3103_clock_out clk_out; + u8 ts_clk_pol:1; + u8 spec_inv:1; + u8 agc; + u8 agc_inv:1; + u8 envelope_mode:1; + u8 lnb_hv_pol:1; + u8 lnb_en_pol:1; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + +/* private: For legacy media attach wrapper. Do not set value. */ + u8 attach_in_use:1; +}; + +/** + * struct m88ds3103_config - m88ds3102 configuration + * + * @i2c_addr: I2C address. Default: none, must set. Example: 0x68, ... + * @clock: Device's clock. Default: none, must set. Example: 27000000 + * @i2c_wr_max: Max bytes I2C provider is asked to write at once. + * Default: none, must set. Example: 33, 65, ... + * @ts_mode: TS output mode, as defined by &enum m88ds3103_ts_mode. + * Default: M88DS3103_TS_SERIAL. + * @ts_clk: TS clk in KHz. Default: 0. + * @ts_clk_pol: TS clk polarity.Default: 0. + * 1-active at falling edge; 0-active at rising edge. + * @spec_inv: Spectrum inversion. Default: 0. + * @agc_inv: AGC polarity. Default: 0. + * @clock_out: Clock output, as defined by &enum m88ds3103_clock_out. + * Default: M88DS3103_CLOCK_OUT_DISABLED. + * @envelope_mode: DiSEqC envelope mode. Default: 0. + * @agc: AGC configuration. Default: none, must set. + * @lnb_hv_pol: LNB H/V pin polarity. Default: 0. Values: + * 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18; + * 0: pin high set to VOLTAGE_18, pin low to set VOLTAGE_13. + * @lnb_en_pol: LNB enable pin polarity. Default: 0. Values: + * 1: pin high to enable, pin low to disable; + * 0: pin high to disable, pin low to enable. + */ +struct m88ds3103_config { + u8 i2c_addr; + u32 clock; + u16 i2c_wr_max; + u8 ts_mode; + u32 ts_clk; + u8 ts_clk_pol:1; + u8 spec_inv:1; + u8 agc_inv:1; + u8 clock_out; + u8 envelope_mode:1; + u8 agc; + u8 lnb_hv_pol:1; + u8 lnb_en_pol:1; +}; + +#if defined(CONFIG_DVB_M88DS3103) || \ + (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE)) +/** + * m88ds3103_attach - Attach a m88ds3103 demod + * + * @config: pointer to &struct m88ds3103_config with demod configuration. + * @i2c: i2c adapter to use. + * @tuner_i2c: on success, returns the I2C adapter associated with + * m88ds3103 tuner. + * + * return: FE pointer on success, NULL on failure. + * Note: Do not add new m88ds3103_attach() users! Use I2C bindings instead. + */ +extern struct dvb_frontend *m88ds3103_attach( + const struct m88ds3103_config *config, + struct i2c_adapter *i2c, + struct i2c_adapter **tuner_i2c); +extern int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm); +#else +static inline struct dvb_frontend *m88ds3103_attach( + const struct m88ds3103_config *config, + struct i2c_adapter *i2c, + struct i2c_adapter **tuner_i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#define m88ds3103_get_agc_pwm NULL +#endif + +#endif diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h new file mode 100644 index 0000000000..594ad9cbc2 --- /dev/null +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -0,0 +1,402 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Montage Technology M88DS3103/M88RS6000 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + */ + +#ifndef M88DS3103_PRIV_H +#define M88DS3103_PRIV_H + +#include <media/dvb_frontend.h> +#include "m88ds3103.h" +#include <linux/int_log.h> +#include <linux/firmware.h> +#include <linux/i2c-mux.h> +#include <linux/regmap.h> +#include <linux/math64.h> + +#define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw" +#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" +#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw" + +#define M88RS6000_CHIP_ID 0x74 +#define M88DS3103_CHIP_ID 0x70 + +#define M88DS3103_CHIPTYPE_3103 0 +#define M88DS3103_CHIPTYPE_RS6000 1 +#define M88DS3103_CHIPTYPE_3103B 2 + +struct m88ds3103_dev { + struct i2c_client *client; + struct i2c_client *dt_client; + struct regmap_config regmap_config; + struct regmap *regmap; + struct m88ds3103_config config; + const struct m88ds3103_config *cfg; + struct dvb_frontend fe; + enum fe_delivery_system delivery_system; + enum fe_status fe_status; + u32 dvbv3_ber; /* for old DVBv3 API read_ber */ + bool warm; /* FW running */ + struct i2c_mux_core *muxc; + /* auto detect chip id to do different config */ + u8 chip_id; + /* chip type to differentiate m88rs6000 from m88ds3103b */ + u8 chiptype; + /* main mclk is calculated for M88RS6000 dynamically */ + s32 mclk; + u64 post_bit_error; + u64 post_bit_count; + u8 dt_addr; +}; + +struct m88ds3103_reg_val { + u8 reg; + u8 val; +}; + +static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x03}, + {0x0c, 0x02}, + {0x21, 0x54}, + {0x25, 0x8a}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x31, 0x40}, + {0x32, 0x32}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x56, 0x01}, + {0x63, 0x0f}, + {0x64, 0x30}, + {0x65, 0x40}, + {0x68, 0x26}, + {0x69, 0x4c}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x7c, 0x00}, + {0xae, 0x82}, + {0x80, 0x64}, + {0x81, 0x66}, + {0x82, 0x44}, + {0x85, 0x04}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0xa0, 0x44}, + {0xc0, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0xff}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xe0, 0xf8}, + {0xe6, 0x8b}, + {0xd0, 0x40}, + {0xf8, 0x20}, + {0xfa, 0x0f}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, +}; + +static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x07}, + {0x0c, 0x02}, + {0x21, 0x54}, + {0x25, 0x8a}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x32, 0x32}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x56, 0x01}, + {0x63, 0x0f}, + {0x64, 0x10}, + {0x65, 0x20}, + {0x68, 0x46}, + {0x69, 0xcd}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x85, 0x08}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0x86, 0x00}, + {0x87, 0x0f}, + {0x89, 0x00}, + {0x8b, 0x44}, + {0x8c, 0x66}, + {0x9d, 0xc1}, + {0x8a, 0x10}, + {0xad, 0x40}, + {0xa0, 0x44}, + {0xc0, 0x08}, + {0xc1, 0x10}, + {0xc2, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0xff}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xca, 0x23}, + {0xcb, 0x24}, + {0xcc, 0xf4}, + {0xce, 0x74}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, +}; + +static const struct m88ds3103_reg_val m88rs6000_dvbs_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x03}, + {0x0c, 0x02}, + {0x20, 0x00}, + {0x21, 0x54}, + {0x25, 0x82}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x31, 0x40}, + {0x32, 0x32}, + {0x33, 0x35}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x63, 0x0f}, + {0x64, 0x30}, + {0x65, 0x40}, + {0x68, 0x26}, + {0x69, 0x4c}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x7c, 0x00}, + {0xae, 0x82}, + {0x80, 0x64}, + {0x81, 0x66}, + {0x82, 0x44}, + {0x85, 0x04}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0xa0, 0x44}, + {0xbe, 0x00}, + {0xc0, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0xff}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xe0, 0xf8}, + {0xe6, 0x8b}, + {0xd0, 0x40}, + {0xf8, 0x20}, + {0xfa, 0x0f}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, + {0x29, 0x11}, +}; + +static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x07}, + {0x0c, 0x02}, + {0x20, 0x00}, + {0x21, 0x54}, + {0x25, 0x82}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x32, 0x32}, + {0x33, 0x35}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x63, 0x0f}, + {0x64, 0x10}, + {0x65, 0x20}, + {0x68, 0x46}, + {0x69, 0xcd}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x85, 0x08}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0x86, 0x00}, + {0x87, 0x0f}, + {0x89, 0x00}, + {0x8b, 0x44}, + {0x8c, 0x66}, + {0x9d, 0xc1}, + {0x8a, 0x10}, + {0xad, 0x40}, + {0xa0, 0x44}, + {0xbe, 0x00}, + {0xc0, 0x08}, + {0xc1, 0x10}, + {0xc2, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0xff}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xca, 0x23}, + {0xcb, 0x24}, + {0xcc, 0xf4}, + {0xce, 0x74}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, + {0x29, 0x01}, +}; +#endif diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c new file mode 100644 index 0000000000..2aa98203cd --- /dev/null +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -0,0 +1,817 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for M88RS2000 demodulator and tuner + + Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) + Beta Driver + + Include various calculation code from DS3000 driver. + Copyright (C) 2009 Konstantin Dimitrov. + + +*/ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/types.h> + + +#include <media/dvb_frontend.h> +#include "m88rs2000.h" + +struct m88rs2000_state { + struct i2c_adapter *i2c; + const struct m88rs2000_config *config; + struct dvb_frontend frontend; + u8 no_lock_count; + u32 tuner_frequency; + u32 symbol_rate; + enum fe_code_rate fec_inner; + u8 tuner_level; + int errmode; +}; + +static int m88rs2000_debug; + +module_param_named(debug, m88rs2000_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define dprintk(level, args...) do { \ + if (level & m88rs2000_debug) \ + printk(KERN_DEBUG "m88rs2000-fe: " args); \ +} while (0) + +#define deb_info(args...) dprintk(0x01, args) +#define info(format, arg...) \ + printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) + +static int m88rs2000_writereg(struct m88rs2000_state *state, + u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_addr, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + { + .addr = state->config->demod_addr, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_addr, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static u32 m88rs2000_get_mclk(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u32 mclk; + u8 reg; + /* Must not be 0x00 or 0xff */ + reg = m88rs2000_readreg(state, 0x86); + if (!reg || reg == 0xff) + return 0; + + reg /= 2; + reg += 1; + + mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28; + + return mclk; +} + +static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u32 mclk; + s32 tmp; + u8 reg; + int ret; + + mclk = m88rs2000_get_mclk(fe); + if (!mclk) + return -EINVAL; + + tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk; + if (tmp < 0) + tmp += 4096; + + /* Carrier Offset */ + ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4)); + + reg = m88rs2000_readreg(state, 0x9d); + reg &= 0xf; + reg |= (u8)(tmp & 0xf) << 4; + + ret |= m88rs2000_writereg(state, 0x9d, reg); + + return ret; +} + +static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + u64 temp; + u32 mclk; + u8 b[3]; + + if ((srate < 1000000) || (srate > 45000000)) + return -EINVAL; + + mclk = m88rs2000_get_mclk(fe); + if (!mclk) + return -EINVAL; + + temp = srate / 1000; + temp *= 1 << 24; + + do_div(temp, mclk); + + b[0] = (u8) (temp >> 16) & 0xff; + b[1] = (u8) (temp >> 8) & 0xff; + b[2] = (u8) temp & 0xff; + + ret = m88rs2000_writereg(state, 0x93, b[2]); + ret |= m88rs2000_writereg(state, 0x94, b[1]); + ret |= m88rs2000_writereg(state, 0x95, b[0]); + + if (srate > 10000000) + ret |= m88rs2000_writereg(state, 0xa0, 0x20); + else + ret |= m88rs2000_writereg(state, 0xa0, 0x60); + + ret |= m88rs2000_writereg(state, 0xa1, 0xe0); + + if (srate > 12000000) + ret |= m88rs2000_writereg(state, 0xa3, 0x20); + else if (srate > 2800000) + ret |= m88rs2000_writereg(state, 0xa3, 0x98); + else + ret |= m88rs2000_writereg(state, 0xa3, 0x90); + + deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); + return ret; +} + +static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + int i; + u8 reg; + deb_info("%s\n", __func__); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0xb2); + reg &= 0x3f; + m88rs2000_writereg(state, 0xb2, reg); + for (i = 0; i < m->msg_len; i++) + m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); + + reg = m88rs2000_readreg(state, 0xb1); + reg &= 0x87; + reg |= ((m->msg_len - 1) << 3) | 0x07; + reg &= 0x7f; + m88rs2000_writereg(state, 0xb1, reg); + + for (i = 0; i < 15; i++) { + if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) + break; + msleep(20); + } + + reg = m88rs2000_readreg(state, 0xb1); + if ((reg & 0x40) > 0x0) { + reg &= 0x7f; + reg |= 0x40; + m88rs2000_writereg(state, 0xb1, reg); + } + + reg = m88rs2000_readreg(state, 0xb2); + reg &= 0x3f; + reg |= 0x80; + m88rs2000_writereg(state, 0xb2, reg); + m88rs2000_writereg(state, 0x9a, 0xb0); + + + return 0; +} + +static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 reg0, reg1; + deb_info("%s\n", __func__); + m88rs2000_writereg(state, 0x9a, 0x30); + msleep(50); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); + /* TODO complete this section */ + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); + + return 0; +} + +static int m88rs2000_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 reg0, reg1; + m88rs2000_writereg(state, 0x9a, 0x30); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); + + reg1 &= 0x3f; + + switch (tone) { + case SEC_TONE_ON: + reg0 |= 0x4; + reg0 &= 0xbc; + break; + case SEC_TONE_OFF: + reg1 |= 0x80; + break; + default: + break; + } + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); + return 0; +} + +struct inittab { + u8 cmd; + u8 reg; + u8 val; +}; + +static struct inittab m88rs2000_setup[] = { + {DEMOD_WRITE, 0x9a, 0x30}, + {DEMOD_WRITE, 0x00, 0x01}, + {WRITE_DELAY, 0x19, 0x00}, + {DEMOD_WRITE, 0x00, 0x00}, + {DEMOD_WRITE, 0x9a, 0xb0}, + {DEMOD_WRITE, 0x81, 0xc1}, + {DEMOD_WRITE, 0x81, 0x81}, + {DEMOD_WRITE, 0x86, 0xc6}, + {DEMOD_WRITE, 0x9a, 0x30}, + {DEMOD_WRITE, 0xf0, 0x22}, + {DEMOD_WRITE, 0xf1, 0xbf}, + {DEMOD_WRITE, 0xb0, 0x45}, + {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/ + {DEMOD_WRITE, 0x9a, 0xb0}, + {0xff, 0xaa, 0xff} +}; + +static struct inittab m88rs2000_shutdown[] = { + {DEMOD_WRITE, 0x9a, 0x30}, + {DEMOD_WRITE, 0xb0, 0x00}, + {DEMOD_WRITE, 0xf1, 0x89}, + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0x9a, 0xb0}, + {DEMOD_WRITE, 0x81, 0x81}, + {0xff, 0xaa, 0xff} +}; + +static struct inittab fe_reset[] = { + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0x20, 0x81}, + {DEMOD_WRITE, 0x21, 0x80}, + {DEMOD_WRITE, 0x10, 0x33}, + {DEMOD_WRITE, 0x11, 0x44}, + {DEMOD_WRITE, 0x12, 0x07}, + {DEMOD_WRITE, 0x18, 0x20}, + {DEMOD_WRITE, 0x28, 0x04}, + {DEMOD_WRITE, 0x29, 0x8e}, + {DEMOD_WRITE, 0x3b, 0xff}, + {DEMOD_WRITE, 0x32, 0x10}, + {DEMOD_WRITE, 0x33, 0x02}, + {DEMOD_WRITE, 0x34, 0x30}, + {DEMOD_WRITE, 0x35, 0xff}, + {DEMOD_WRITE, 0x38, 0x50}, + {DEMOD_WRITE, 0x39, 0x68}, + {DEMOD_WRITE, 0x3c, 0x7f}, + {DEMOD_WRITE, 0x3d, 0x0f}, + {DEMOD_WRITE, 0x45, 0x20}, + {DEMOD_WRITE, 0x46, 0x24}, + {DEMOD_WRITE, 0x47, 0x7c}, + {DEMOD_WRITE, 0x48, 0x16}, + {DEMOD_WRITE, 0x49, 0x04}, + {DEMOD_WRITE, 0x4a, 0x01}, + {DEMOD_WRITE, 0x4b, 0x78}, + {DEMOD_WRITE, 0X4d, 0xd2}, + {DEMOD_WRITE, 0x4e, 0x6d}, + {DEMOD_WRITE, 0x50, 0x30}, + {DEMOD_WRITE, 0x51, 0x30}, + {DEMOD_WRITE, 0x54, 0x7b}, + {DEMOD_WRITE, 0x56, 0x09}, + {DEMOD_WRITE, 0x58, 0x59}, + {DEMOD_WRITE, 0x59, 0x37}, + {DEMOD_WRITE, 0x63, 0xfa}, + {0xff, 0xaa, 0xff} +}; + +static struct inittab fe_trigger[] = { + {DEMOD_WRITE, 0x97, 0x04}, + {DEMOD_WRITE, 0x99, 0x77}, + {DEMOD_WRITE, 0x9b, 0x64}, + {DEMOD_WRITE, 0x9e, 0x00}, + {DEMOD_WRITE, 0x9f, 0xf8}, + {DEMOD_WRITE, 0x98, 0xff}, + {DEMOD_WRITE, 0xc0, 0x0f}, + {DEMOD_WRITE, 0x89, 0x01}, + {DEMOD_WRITE, 0x00, 0x00}, + {WRITE_DELAY, 0x0a, 0x00}, + {DEMOD_WRITE, 0x00, 0x01}, + {DEMOD_WRITE, 0x00, 0x00}, + {DEMOD_WRITE, 0x9a, 0xb0}, + {0xff, 0xaa, 0xff} +}; + +static int m88rs2000_tab_set(struct m88rs2000_state *state, + struct inittab *tab) +{ + int ret = 0; + u8 i; + if (tab == NULL) + return -EINVAL; + + for (i = 0; i < 255; i++) { + switch (tab[i].cmd) { + case 0x01: + ret = m88rs2000_writereg(state, tab[i].reg, + tab[i].val); + break; + case 0x10: + if (tab[i].reg > 0) + mdelay(tab[i].reg); + break; + case 0xff: + if (tab[i].reg == 0xaa && tab[i].val == 0xff) + return 0; + break; + case 0x00: + break; + default: + return -EINVAL; + } + if (ret < 0) + return -ENODEV; + } + return 0; +} + +static int m88rs2000_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage volt) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 data; + + data = m88rs2000_readreg(state, 0xb2); + data |= 0x03; /* bit0 V/H, bit1 off/on */ + + switch (volt) { + case SEC_VOLTAGE_18: + data &= ~0x03; + break; + case SEC_VOLTAGE_13: + data &= ~0x03; + data |= 0x01; + break; + case SEC_VOLTAGE_OFF: + break; + } + + m88rs2000_writereg(state, 0xb2, data); + + return 0; +} + +static int m88rs2000_init(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + + deb_info("m88rs2000: init chip\n"); + /* Setup frontend from shutdown/cold */ + if (state->config->inittab) + ret = m88rs2000_tab_set(state, + (struct inittab *)state->config->inittab); + else + ret = m88rs2000_tab_set(state, m88rs2000_setup); + + return ret; +} + +static int m88rs2000_sleep(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + int ret; + /* Shutdown the frondend */ + ret = m88rs2000_tab_set(state, m88rs2000_shutdown); + return ret; +} + +static int m88rs2000_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 reg = m88rs2000_readreg(state, 0x8c); + + *status = 0; + + if ((reg & 0xee) == 0xee) { + *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI + | FE_HAS_SYNC | FE_HAS_LOCK; + if (state->config->set_ts_params) + state->config->set_ts_params(fe, CALL_IS_READ); + } + return 0; +} + +static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp0, tmp1; + + m88rs2000_writereg(state, 0x9a, 0x30); + tmp0 = m88rs2000_readreg(state, 0xd8); + if ((tmp0 & 0x10) != 0) { + m88rs2000_writereg(state, 0x9a, 0xb0); + *ber = 0xffffffff; + return 0; + } + + *ber = (m88rs2000_readreg(state, 0xd7) << 8) | + m88rs2000_readreg(state, 0xd6); + + tmp1 = m88rs2000_readreg(state, 0xd9); + m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); + /* needs twice */ + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0x9a, 0xb0); + + return 0; +} + +static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, strength); + + return 0; +} + +static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + *snr = 512 * m88rs2000_readreg(state, 0x65); + + return 0; +} + +static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp; + + *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | + m88rs2000_readreg(state, 0xd4); + tmp = m88rs2000_readreg(state, 0xd8); + m88rs2000_writereg(state, 0xd8, tmp & ~0x20); + /* needs two times */ + m88rs2000_writereg(state, 0xd8, tmp | 0x20); + m88rs2000_writereg(state, 0xd8, tmp | 0x20); + + return 0; +} + +static int m88rs2000_set_fec(struct m88rs2000_state *state, + enum fe_code_rate fec) +{ + u8 fec_set, reg; + int ret; + + switch (fec) { + case FEC_1_2: + fec_set = 0x8; + break; + case FEC_2_3: + fec_set = 0x10; + break; + case FEC_3_4: + fec_set = 0x20; + break; + case FEC_5_6: + fec_set = 0x40; + break; + case FEC_7_8: + fec_set = 0x80; + break; + case FEC_AUTO: + default: + fec_set = 0x0; + } + + reg = m88rs2000_readreg(state, 0x70); + reg &= 0x7; + ret = m88rs2000_writereg(state, 0x70, reg | fec_set); + + ret |= m88rs2000_writereg(state, 0x76, 0x8); + + return ret; +} + +static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state) +{ + u8 reg; + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0x76); + m88rs2000_writereg(state, 0x9a, 0xb0); + + reg &= 0xf0; + reg >>= 5; + + switch (reg) { + case 0x4: + return FEC_1_2; + case 0x3: + return FEC_2_3; + case 0x2: + return FEC_3_4; + case 0x1: + return FEC_5_6; + case 0x0: + return FEC_7_8; + default: + break; + } + + return FEC_AUTO; +} + +static int m88rs2000_set_frontend(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + enum fe_status status = 0; + int i, ret = 0; + u32 tuner_freq; + s16 offset = 0; + u8 reg; + + state->no_lock_count = 0; + + if (c->delivery_system != SYS_DVBS) { + deb_info("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + /* Set Tuner */ + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + + if (ret < 0) + return -ENODEV; + + if (fe->ops.tuner_ops.get_frequency) { + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); + + if (ret < 0) + return -ENODEV; + + offset = (s16)((s32)tuner_freq - c->frequency); + } else { + offset = 0; + } + + /* default mclk value 96.4285 * 2 * 1000 = 192857 */ + if (((c->frequency % 192857) >= (192857 - 3000)) || + (c->frequency % 192857) <= 3000) + ret = m88rs2000_writereg(state, 0x86, 0xc2); + else + ret = m88rs2000_writereg(state, 0x86, 0xc6); + + ret |= m88rs2000_set_carrieroffset(fe, offset); + if (ret < 0) + return -ENODEV; + + /* Reset demod by symbol rate */ + if (c->symbol_rate > 27500000) + ret = m88rs2000_writereg(state, 0xf1, 0xa4); + else + ret = m88rs2000_writereg(state, 0xf1, 0xbf); + + ret |= m88rs2000_tab_set(state, fe_reset); + if (ret < 0) + return -ENODEV; + + /* Set FEC */ + ret = m88rs2000_set_fec(state, c->fec_inner); + ret |= m88rs2000_writereg(state, 0x85, 0x1); + ret |= m88rs2000_writereg(state, 0x8a, 0xbf); + ret |= m88rs2000_writereg(state, 0x8d, 0x1e); + ret |= m88rs2000_writereg(state, 0x90, 0xf1); + ret |= m88rs2000_writereg(state, 0x91, 0x08); + + if (ret < 0) + return -ENODEV; + + /* Set Symbol Rate */ + ret = m88rs2000_set_symbolrate(fe, c->symbol_rate); + if (ret < 0) + return -ENODEV; + + /* Set up Demod */ + ret = m88rs2000_tab_set(state, fe_trigger); + if (ret < 0) + return -ENODEV; + + for (i = 0; i < 25; i++) { + reg = m88rs2000_readreg(state, 0x8c); + if ((reg & 0xee) == 0xee) { + status = FE_HAS_LOCK; + break; + } + state->no_lock_count++; + if (state->no_lock_count == 15) { + reg = m88rs2000_readreg(state, 0x70); + reg ^= 0x4; + m88rs2000_writereg(state, 0x70, reg); + state->no_lock_count = 0; + } + msleep(20); + } + + if (status & FE_HAS_LOCK) { + state->fec_inner = m88rs2000_get_fec(state); + /* Unknown suspect SNR level */ + reg = m88rs2000_readreg(state, 0x65); + } + + state->tuner_frequency = c->frequency; + state->symbol_rate = c->symbol_rate; + return 0; +} + +static int m88rs2000_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + c->fec_inner = state->fec_inner; + c->frequency = state->tuner_frequency; + c->symbol_rate = state->symbol_rate; + return 0; +} + +static int m88rs2000_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->symbol_rate > 3000000) + tune->min_delay_ms = 2000; + else + tune->min_delay_ms = 3000; + + tune->step_size = c->symbol_rate / 16000; + tune->max_drift = c->symbol_rate / 2000; + + return 0; +} + +static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + + if (enable) + m88rs2000_writereg(state, 0x81, 0x84); + else + m88rs2000_writereg(state, 0x81, 0x81); + udelay(10); + return 0; +} + +static void m88rs2000_release(struct dvb_frontend *fe) +{ + struct m88rs2000_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops m88rs2000_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "M88RS2000 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO + }, + + .release = m88rs2000_release, + .init = m88rs2000_init, + .sleep = m88rs2000_sleep, + .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, + .read_status = m88rs2000_read_status, + .read_ber = m88rs2000_read_ber, + .read_signal_strength = m88rs2000_read_signal_strength, + .read_snr = m88rs2000_read_snr, + .read_ucblocks = m88rs2000_read_ucblocks, + .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg, + .diseqc_send_burst = m88rs2000_send_diseqc_burst, + .set_tone = m88rs2000_set_tone, + .set_voltage = m88rs2000_set_voltage, + + .set_frontend = m88rs2000_set_frontend, + .get_frontend = m88rs2000_get_frontend, + .get_tune_settings = m88rs2000_get_tune_settings, +}; + +struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, + struct i2c_adapter *i2c) +{ + struct m88rs2000_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &m88rs2000_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL_GPL(m88rs2000_attach); + +MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.13"); + diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h new file mode 100644 index 0000000000..3a141b02d5 --- /dev/null +++ b/drivers/media/dvb-frontends/m88rs2000.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for M88RS2000 demodulator + + +*/ + +#ifndef M88RS2000_H +#define M88RS2000_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +struct m88rs2000_config { + /* Demodulator i2c address */ + u8 demod_addr; + + u8 *inittab; + + /* minimum delay before retuning */ + int min_delay_ms; + + int (*set_ts_params)(struct dvb_frontend *, int); +}; + +enum { + CALL_IS_SET_FRONTEND = 0x0, + CALL_IS_READ, +}; + +#if IS_REACHABLE(CONFIG_DVB_M88RS2000) +extern struct dvb_frontend *m88rs2000_attach( + const struct m88rs2000_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *m88rs2000_attach( + const struct m88rs2000_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_M88RS2000 */ + +#define RS2000_FE_CRYSTAL_KHZ 27000 + +enum { + DEMOD_WRITE = 0x1, + WRITE_DELAY = 0x10, +}; +#endif /* M88RS2000_H */ diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c new file mode 100644 index 0000000000..0fc45896e7 --- /dev/null +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -0,0 +1,1858 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Fujitsu MB86A16 DVB-S/DSS DC Receiver driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "mb86a16.h" +#include "mb86a16_priv.h" + +static unsigned int verbose = 5; +module_param(verbose, int, 0644); + +struct mb86a16_state { + struct i2c_adapter *i2c_adap; + const struct mb86a16_config *config; + struct dvb_frontend frontend; + + /* tuning parameters */ + int frequency; + int srate; + + /* Internal stuff */ + int master_clk; + int deci; + int csel; + int rsel; +}; + +#define MB86A16_ERROR 0 +#define MB86A16_NOTICE 1 +#define MB86A16_INFO 2 +#define MB86A16_DEBUG 3 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((x > MB86A16_ERROR) && (x > y)) \ + printk(KERN_ERR "%s: " format "\n", __func__, ##arg); \ + else if ((x > MB86A16_NOTICE) && (x > y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__, ##arg); \ + else if ((x > MB86A16_INFO) && (x > y)) \ + printk(KERN_INFO "%s: " format "\n", __func__, ##arg); \ + else if ((x > MB86A16_DEBUG) && (x > y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__, ##arg); \ + } else { \ + if (x > y) \ + printk(format, ##arg); \ + } \ +} while (0) + +#define TRACE_IN dprintk(verbose, MB86A16_DEBUG, 1, "-->()") +#define TRACE_OUT dprintk(verbose, MB86A16_DEBUG, 1, "()-->") + +static int mb86a16_write(struct mb86a16_state *state, u8 reg, u8 val) +{ + int ret; + u8 buf[] = { reg, val }; + + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + dprintk(verbose, MB86A16_DEBUG, 1, + "writing to [0x%02x],Reg[0x%02x],Data[0x%02x]", + state->config->demod_address, buf[0], buf[1]); + + ret = i2c_transfer(state->i2c_adap, &msg, 1); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int mb86a16_read(struct mb86a16_state *state, u8 reg, u8 *val) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + ret = i2c_transfer(state->i2c_adap, msg, 2); + if (ret != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=%i)", + reg, ret); + + if (ret < 0) + return ret; + return -EREMOTEIO; + } + *val = b1[0]; + + return ret; +} + +static int CNTM_set(struct mb86a16_state *state, + unsigned char timint1, + unsigned char timint2, + unsigned char cnext) +{ + unsigned char val; + + val = (timint1 << 4) | (timint2 << 2) | cnext; + if (mb86a16_write(state, MB86A16_CNTMR, val) < 0) + goto err; + + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int smrt_set(struct mb86a16_state *state, int rate) +{ + int tmp ; + int m ; + unsigned char STOFS0, STOFS1; + + m = 1 << state->deci; + tmp = (8192 * state->master_clk - 2 * m * rate * 8192 + state->master_clk / 2) / state->master_clk; + + STOFS0 = tmp & 0x0ff; + STOFS1 = (tmp & 0xf00) >> 8; + + if (mb86a16_write(state, MB86A16_SRATE1, (state->deci << 2) | + (state->csel << 1) | + state->rsel) < 0) + goto err; + if (mb86a16_write(state, MB86A16_SRATE2, STOFS0) < 0) + goto err; + if (mb86a16_write(state, MB86A16_SRATE3, STOFS1) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -1; +} + +static int srst(struct mb86a16_state *state) +{ + if (mb86a16_write(state, MB86A16_RESET, 0x04) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + +} + +static int afcex_data_set(struct mb86a16_state *state, + unsigned char AFCEX_L, + unsigned char AFCEX_H) +{ + if (mb86a16_write(state, MB86A16_AFCEXL, AFCEX_L) < 0) + goto err; + if (mb86a16_write(state, MB86A16_AFCEXH, AFCEX_H) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + + return -1; +} + +static int afcofs_data_set(struct mb86a16_state *state, + unsigned char AFCEX_L, + unsigned char AFCEX_H) +{ + if (mb86a16_write(state, 0x58, AFCEX_L) < 0) + goto err; + if (mb86a16_write(state, 0x59, AFCEX_H) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int stlp_set(struct mb86a16_state *state, + unsigned char STRAS, + unsigned char STRBS) +{ + if (mb86a16_write(state, MB86A16_STRFILTCOEF1, (STRBS << 3) | (STRAS)) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int Vi_set(struct mb86a16_state *state, unsigned char ETH, unsigned char VIA) +{ + if (mb86a16_write(state, MB86A16_VISET2, 0x04) < 0) + goto err; + if (mb86a16_write(state, MB86A16_VISET3, 0xf5) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int initial_set(struct mb86a16_state *state) +{ + if (stlp_set(state, 5, 7)) + goto err; + + udelay(100); + if (afcex_data_set(state, 0, 0)) + goto err; + + udelay(100); + if (afcofs_data_set(state, 0, 0)) + goto err; + + udelay(100); + if (mb86a16_write(state, MB86A16_CRLFILTCOEF1, 0x16) < 0) + goto err; + if (mb86a16_write(state, 0x2f, 0x21) < 0) + goto err; + if (mb86a16_write(state, MB86A16_VIMAG, 0x38) < 0) + goto err; + if (mb86a16_write(state, MB86A16_FAGCS1, 0x00) < 0) + goto err; + if (mb86a16_write(state, MB86A16_FAGCS2, 0x1c) < 0) + goto err; + if (mb86a16_write(state, MB86A16_FAGCS3, 0x20) < 0) + goto err; + if (mb86a16_write(state, MB86A16_FAGCS4, 0x1e) < 0) + goto err; + if (mb86a16_write(state, MB86A16_FAGCS5, 0x23) < 0) + goto err; + if (mb86a16_write(state, 0x54, 0xff) < 0) + goto err; + if (mb86a16_write(state, MB86A16_TSOUT, 0x00) < 0) + goto err; + + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int S01T_set(struct mb86a16_state *state, + unsigned char s1t, + unsigned s0t) +{ + if (mb86a16_write(state, 0x33, (s1t << 3) | s0t) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + + +static int EN_set(struct mb86a16_state *state, + int cren, + int afcen) +{ + unsigned char val; + + val = 0x7a | (cren << 7) | (afcen << 2); + if (mb86a16_write(state, 0x49, val) < 0) + goto err; + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int AFCEXEN_set(struct mb86a16_state *state, + int afcexen, + int smrt) +{ + unsigned char AFCA ; + + if (smrt > 18875) + AFCA = 4; + else if (smrt > 9375) + AFCA = 3; + else if (smrt > 2250) + AFCA = 2; + else + AFCA = 1; + + if (mb86a16_write(state, 0x2a, 0x02 | (afcexen << 5) | (AFCA << 2)) < 0) + goto err; + + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int DAGC_data_set(struct mb86a16_state *state, + unsigned char DAGCA, + unsigned char DAGCW) +{ + if (mb86a16_write(state, 0x2d, (DAGCA << 3) | DAGCW) < 0) + goto err; + + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static void smrt_info_get(struct mb86a16_state *state, int rate) +{ + if (rate >= 37501) { + state->deci = 0; state->csel = 0; state->rsel = 0; + } else if (rate >= 30001) { + state->deci = 0; state->csel = 0; state->rsel = 1; + } else if (rate >= 26251) { + state->deci = 0; state->csel = 1; state->rsel = 0; + } else if (rate >= 22501) { + state->deci = 0; state->csel = 1; state->rsel = 1; + } else if (rate >= 18751) { + state->deci = 1; state->csel = 0; state->rsel = 0; + } else if (rate >= 15001) { + state->deci = 1; state->csel = 0; state->rsel = 1; + } else if (rate >= 13126) { + state->deci = 1; state->csel = 1; state->rsel = 0; + } else if (rate >= 11251) { + state->deci = 1; state->csel = 1; state->rsel = 1; + } else if (rate >= 9376) { + state->deci = 2; state->csel = 0; state->rsel = 0; + } else if (rate >= 7501) { + state->deci = 2; state->csel = 0; state->rsel = 1; + } else if (rate >= 6563) { + state->deci = 2; state->csel = 1; state->rsel = 0; + } else if (rate >= 5626) { + state->deci = 2; state->csel = 1; state->rsel = 1; + } else if (rate >= 4688) { + state->deci = 3; state->csel = 0; state->rsel = 0; + } else if (rate >= 3751) { + state->deci = 3; state->csel = 0; state->rsel = 1; + } else if (rate >= 3282) { + state->deci = 3; state->csel = 1; state->rsel = 0; + } else if (rate >= 2814) { + state->deci = 3; state->csel = 1; state->rsel = 1; + } else if (rate >= 2344) { + state->deci = 4; state->csel = 0; state->rsel = 0; + } else if (rate >= 1876) { + state->deci = 4; state->csel = 0; state->rsel = 1; + } else if (rate >= 1641) { + state->deci = 4; state->csel = 1; state->rsel = 0; + } else if (rate >= 1407) { + state->deci = 4; state->csel = 1; state->rsel = 1; + } else if (rate >= 1172) { + state->deci = 5; state->csel = 0; state->rsel = 0; + } else if (rate >= 939) { + state->deci = 5; state->csel = 0; state->rsel = 1; + } else if (rate >= 821) { + state->deci = 5; state->csel = 1; state->rsel = 0; + } else { + state->deci = 5; state->csel = 1; state->rsel = 1; + } + + if (state->csel == 0) + state->master_clk = 92000; + else + state->master_clk = 61333; + +} + +static int signal_det(struct mb86a16_state *state, + int smrt, + unsigned char *SIG) +{ + int ret; + int smrtd; + unsigned char S[3]; + int i; + + if (*SIG > 45) { + if (CNTM_set(state, 2, 1, 2) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); + return -1; + } + } else { + if (CNTM_set(state, 3, 1, 2) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); + return -1; + } + } + for (i = 0; i < 3; i++) { + if (i == 0) + smrtd = smrt * 98 / 100; + else if (i == 1) + smrtd = smrt; + else + smrtd = smrt * 102 / 100; + smrt_info_get(state, smrtd); + smrt_set(state, smrtd); + srst(state); + msleep_interruptible(10); + if (mb86a16_read(state, 0x37, &(S[i])) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + } + if ((S[1] > S[0] * 112 / 100) && (S[1] > S[2] * 112 / 100)) + ret = 1; + else + ret = 0; + + *SIG = S[1]; + + if (CNTM_set(state, 0, 1, 2) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); + return -1; + } + + return ret; +} + +static int rf_val_set(struct mb86a16_state *state, + int f, + int smrt, + unsigned char R) +{ + unsigned char C, F, B; + int M; + unsigned char rf_val[5]; + int ack = -1; + + if (smrt > 37750) + C = 1; + else if (smrt > 18875) + C = 2; + else if (smrt > 5500) + C = 3; + else + C = 4; + + if (smrt > 30500) + F = 3; + else if (smrt > 9375) + F = 1; + else if (smrt > 4625) + F = 0; + else + F = 2; + + if (f < 1060) + B = 0; + else if (f < 1175) + B = 1; + else if (f < 1305) + B = 2; + else if (f < 1435) + B = 3; + else if (f < 1570) + B = 4; + else if (f < 1715) + B = 5; + else if (f < 1845) + B = 6; + else if (f < 1980) + B = 7; + else if (f < 2080) + B = 8; + else + B = 9; + + M = f * (1 << R) / 2; + + rf_val[0] = 0x01 | (C << 3) | (F << 1); + rf_val[1] = (R << 5) | ((M & 0x1f000) >> 12); + rf_val[2] = (M & 0x00ff0) >> 4; + rf_val[3] = ((M & 0x0000f) << 4) | B; + + /* Frequency Set */ + if (mb86a16_write(state, 0x21, rf_val[0]) < 0) + ack = 0; + if (mb86a16_write(state, 0x22, rf_val[1]) < 0) + ack = 0; + if (mb86a16_write(state, 0x23, rf_val[2]) < 0) + ack = 0; + if (mb86a16_write(state, 0x24, rf_val[3]) < 0) + ack = 0; + if (mb86a16_write(state, 0x25, 0x01) < 0) + ack = 0; + if (ack == 0) { + dprintk(verbose, MB86A16_ERROR, 1, "RF Setup - I2C transfer error"); + return -EREMOTEIO; + } + + return 0; +} + +static int afcerr_chk(struct mb86a16_state *state) +{ + unsigned char AFCM_L, AFCM_H ; + int AFCM ; + int afcm, afcerr ; + + if (mb86a16_read(state, 0x0e, &AFCM_L) != 2) + goto err; + if (mb86a16_read(state, 0x0f, &AFCM_H) != 2) + goto err; + + AFCM = (AFCM_H << 8) + AFCM_L; + + if (AFCM > 2048) + afcm = AFCM - 4096; + else + afcm = AFCM; + afcerr = afcm * state->master_clk / 8192; + + return afcerr; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int dagcm_val_get(struct mb86a16_state *state) +{ + int DAGCM; + unsigned char DAGCM_H, DAGCM_L; + + if (mb86a16_read(state, 0x45, &DAGCM_L) != 2) + goto err; + if (mb86a16_read(state, 0x46, &DAGCM_H) != 2) + goto err; + + DAGCM = (DAGCM_H << 8) + DAGCM_L; + + return DAGCM; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int mb86a16_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + u8 stat, stat2; + struct mb86a16_state *state = fe->demodulator_priv; + + *status = 0; + + if (mb86a16_read(state, MB86A16_SIG1, &stat) != 2) + goto err; + if (mb86a16_read(state, MB86A16_SIG2, &stat2) != 2) + goto err; + if ((stat > 25) && (stat2 > 25)) + *status |= FE_HAS_SIGNAL; + if ((stat > 45) && (stat2 > 45)) + *status |= FE_HAS_CARRIER; + + if (mb86a16_read(state, MB86A16_STATUS, &stat) != 2) + goto err; + + if (stat & 0x01) + *status |= FE_HAS_SYNC; + if (stat & 0x01) + *status |= FE_HAS_VITERBI; + + if (mb86a16_read(state, MB86A16_FRAMESYNC, &stat) != 2) + goto err; + + if ((stat & 0x0f) && (*status & FE_HAS_VITERBI)) + *status |= FE_HAS_LOCK; + + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int sync_chk(struct mb86a16_state *state, + unsigned char *VIRM) +{ + unsigned char val; + int sync; + + if (mb86a16_read(state, 0x0d, &val) != 2) + goto err; + + dprintk(verbose, MB86A16_INFO, 1, "Status = %02x,", val); + sync = val & 0x01; + *VIRM = (val & 0x1c) >> 2; + + return sync; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + *VIRM = 0; + return -EREMOTEIO; + +} + +static int freqerr_chk(struct mb86a16_state *state, + int fTP, + int smrt, + int unit) +{ + unsigned char CRM, AFCML, AFCMH; + unsigned char temp1, temp2, temp3; + int crm, afcm, AFCM; + int crrerr, afcerr; /* kHz */ + int frqerr; /* MHz */ + int afcen, afcexen = 0; + int R, M, fOSC, fOSC_OFS; + + if (mb86a16_read(state, 0x43, &CRM) != 2) + goto err; + + if (CRM > 127) + crm = CRM - 256; + else + crm = CRM; + + crrerr = smrt * crm / 256; + if (mb86a16_read(state, 0x49, &temp1) != 2) + goto err; + + afcen = (temp1 & 0x04) >> 2; + if (afcen == 0) { + if (mb86a16_read(state, 0x2a, &temp1) != 2) + goto err; + afcexen = (temp1 & 0x20) >> 5; + } + + if (afcen == 1) { + if (mb86a16_read(state, 0x0e, &AFCML) != 2) + goto err; + if (mb86a16_read(state, 0x0f, &AFCMH) != 2) + goto err; + } else if (afcexen == 1) { + if (mb86a16_read(state, 0x2b, &AFCML) != 2) + goto err; + if (mb86a16_read(state, 0x2c, &AFCMH) != 2) + goto err; + } + if ((afcen == 1) || (afcexen == 1)) { + smrt_info_get(state, smrt); + AFCM = ((AFCMH & 0x01) << 8) + AFCML; + if (AFCM > 255) + afcm = AFCM - 512; + else + afcm = AFCM; + + afcerr = afcm * state->master_clk / 8192; + } else + afcerr = 0; + + if (mb86a16_read(state, 0x22, &temp1) != 2) + goto err; + if (mb86a16_read(state, 0x23, &temp2) != 2) + goto err; + if (mb86a16_read(state, 0x24, &temp3) != 2) + goto err; + + R = (temp1 & 0xe0) >> 5; + M = ((temp1 & 0x1f) << 12) + (temp2 << 4) + (temp3 >> 4); + if (R == 0) + fOSC = 2 * M; + else + fOSC = M; + + fOSC_OFS = fOSC - fTP; + + if (unit == 0) { /* MHz */ + if (crrerr + afcerr + fOSC_OFS * 1000 >= 0) + frqerr = (crrerr + afcerr + fOSC_OFS * 1000 + 500) / 1000; + else + frqerr = (crrerr + afcerr + fOSC_OFS * 1000 - 500) / 1000; + } else { /* kHz */ + frqerr = crrerr + afcerr + fOSC_OFS * 1000; + } + + return frqerr; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static unsigned char vco_dev_get(struct mb86a16_state *state, int smrt) +{ + unsigned char R; + + if (smrt > 9375) + R = 0; + else + R = 1; + + return R; +} + +static void swp_info_get(struct mb86a16_state *state, + int fOSC_start, + int smrt, + int v, int R, + int swp_ofs, + int *fOSC, + int *afcex_freq, + unsigned char *AFCEX_L, + unsigned char *AFCEX_H) +{ + int AFCEX ; + int crnt_swp_freq ; + + crnt_swp_freq = fOSC_start * 1000 + v * swp_ofs; + + if (R == 0) + *fOSC = (crnt_swp_freq + 1000) / 2000 * 2; + else + *fOSC = (crnt_swp_freq + 500) / 1000; + + if (*fOSC >= crnt_swp_freq) + *afcex_freq = *fOSC * 1000 - crnt_swp_freq; + else + *afcex_freq = crnt_swp_freq - *fOSC * 1000; + + AFCEX = *afcex_freq * 8192 / state->master_clk; + *AFCEX_L = AFCEX & 0x00ff; + *AFCEX_H = (AFCEX & 0x0f00) >> 8; +} + + +static int swp_freq_calcuation(struct mb86a16_state *state, int i, int v, int *V, int vmax, int vmin, + int SIGMIN, int fOSC, int afcex_freq, int swp_ofs, unsigned char *SIG1) +{ + int swp_freq ; + + if ((i % 2 == 1) && (v <= vmax)) { + /* positive v (case 1) */ + if ((v - 1 == vmin) && + (*(V + 30 + v) >= 0) && + (*(V + 30 + v - 1) >= 0) && + (*(V + 30 + v - 1) > *(V + 30 + v)) && + (*(V + 30 + v - 1) > SIGMIN)) { + + swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; + *SIG1 = *(V + 30 + v - 1); + } else if ((v == vmax) && + (*(V + 30 + v) >= 0) && + (*(V + 30 + v - 1) >= 0) && + (*(V + 30 + v) > *(V + 30 + v - 1)) && + (*(V + 30 + v) > SIGMIN)) { + /* (case 2) */ + swp_freq = fOSC * 1000 + afcex_freq; + *SIG1 = *(V + 30 + v); + } else if ((*(V + 30 + v) > 0) && + (*(V + 30 + v - 1) > 0) && + (*(V + 30 + v - 2) > 0) && + (*(V + 30 + v - 3) > 0) && + (*(V + 30 + v - 1) > *(V + 30 + v)) && + (*(V + 30 + v - 2) > *(V + 30 + v - 3)) && + ((*(V + 30 + v - 1) > SIGMIN) || + (*(V + 30 + v - 2) > SIGMIN))) { + /* (case 3) */ + if (*(V + 30 + v - 1) >= *(V + 30 + v - 2)) { + swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; + *SIG1 = *(V + 30 + v - 1); + } else { + swp_freq = fOSC * 1000 + afcex_freq - swp_ofs * 2; + *SIG1 = *(V + 30 + v - 2); + } + } else if ((v == vmax) && + (*(V + 30 + v) >= 0) && + (*(V + 30 + v - 1) >= 0) && + (*(V + 30 + v - 2) >= 0) && + (*(V + 30 + v) > *(V + 30 + v - 2)) && + (*(V + 30 + v - 1) > *(V + 30 + v - 2)) && + ((*(V + 30 + v) > SIGMIN) || + (*(V + 30 + v - 1) > SIGMIN))) { + /* (case 4) */ + if (*(V + 30 + v) >= *(V + 30 + v - 1)) { + swp_freq = fOSC * 1000 + afcex_freq; + *SIG1 = *(V + 30 + v); + } else { + swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; + *SIG1 = *(V + 30 + v - 1); + } + } else { + swp_freq = -1 ; + } + } else if ((i % 2 == 0) && (v >= vmin)) { + /* Negative v (case 1) */ + if ((*(V + 30 + v) > 0) && + (*(V + 30 + v + 1) > 0) && + (*(V + 30 + v + 2) > 0) && + (*(V + 30 + v + 1) > *(V + 30 + v)) && + (*(V + 30 + v + 1) > *(V + 30 + v + 2)) && + (*(V + 30 + v + 1) > SIGMIN)) { + + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; + *SIG1 = *(V + 30 + v + 1); + } else if ((v + 1 == vmax) && + (*(V + 30 + v) >= 0) && + (*(V + 30 + v + 1) >= 0) && + (*(V + 30 + v + 1) > *(V + 30 + v)) && + (*(V + 30 + v + 1) > SIGMIN)) { + /* (case 2) */ + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; + *SIG1 = *(V + 30 + v); + } else if ((v == vmin) && + (*(V + 30 + v) > 0) && + (*(V + 30 + v + 1) > 0) && + (*(V + 30 + v + 2) > 0) && + (*(V + 30 + v) > *(V + 30 + v + 1)) && + (*(V + 30 + v) > *(V + 30 + v + 2)) && + (*(V + 30 + v) > SIGMIN)) { + /* (case 3) */ + swp_freq = fOSC * 1000 + afcex_freq; + *SIG1 = *(V + 30 + v); + } else if ((*(V + 30 + v) >= 0) && + (*(V + 30 + v + 1) >= 0) && + (*(V + 30 + v + 2) >= 0) && + (*(V + 30 + v + 3) >= 0) && + (*(V + 30 + v + 1) > *(V + 30 + v)) && + (*(V + 30 + v + 2) > *(V + 30 + v + 3)) && + ((*(V + 30 + v + 1) > SIGMIN) || + (*(V + 30 + v + 2) > SIGMIN))) { + /* (case 4) */ + if (*(V + 30 + v + 1) >= *(V + 30 + v + 2)) { + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; + *SIG1 = *(V + 30 + v + 1); + } else { + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs * 2; + *SIG1 = *(V + 30 + v + 2); + } + } else if ((*(V + 30 + v) >= 0) && + (*(V + 30 + v + 1) >= 0) && + (*(V + 30 + v + 2) >= 0) && + (*(V + 30 + v + 3) >= 0) && + (*(V + 30 + v) > *(V + 30 + v + 2)) && + (*(V + 30 + v + 1) > *(V + 30 + v + 2)) && + (*(V + 30 + v) > *(V + 30 + v + 3)) && + (*(V + 30 + v + 1) > *(V + 30 + v + 3)) && + ((*(V + 30 + v) > SIGMIN) || + (*(V + 30 + v + 1) > SIGMIN))) { + /* (case 5) */ + if (*(V + 30 + v) >= *(V + 30 + v + 1)) { + swp_freq = fOSC * 1000 + afcex_freq; + *SIG1 = *(V + 30 + v); + } else { + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; + *SIG1 = *(V + 30 + v + 1); + } + } else if ((v + 2 == vmin) && + (*(V + 30 + v) >= 0) && + (*(V + 30 + v + 1) >= 0) && + (*(V + 30 + v + 2) >= 0) && + (*(V + 30 + v + 1) > *(V + 30 + v)) && + (*(V + 30 + v + 2) > *(V + 30 + v)) && + ((*(V + 30 + v + 1) > SIGMIN) || + (*(V + 30 + v + 2) > SIGMIN))) { + /* (case 6) */ + if (*(V + 30 + v + 1) >= *(V + 30 + v + 2)) { + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; + *SIG1 = *(V + 30 + v + 1); + } else { + swp_freq = fOSC * 1000 + afcex_freq + swp_ofs * 2; + *SIG1 = *(V + 30 + v + 2); + } + } else if ((vmax == 0) && (vmin == 0) && (*(V + 30 + v) > SIGMIN)) { + swp_freq = fOSC * 1000; + *SIG1 = *(V + 30 + v); + } else + swp_freq = -1; + } else + swp_freq = -1; + + return swp_freq; +} + +static void swp_info_get2(struct mb86a16_state *state, + int smrt, + int R, + int swp_freq, + int *afcex_freq, + int *fOSC, + unsigned char *AFCEX_L, + unsigned char *AFCEX_H) +{ + int AFCEX ; + + if (R == 0) + *fOSC = (swp_freq + 1000) / 2000 * 2; + else + *fOSC = (swp_freq + 500) / 1000; + + if (*fOSC >= swp_freq) + *afcex_freq = *fOSC * 1000 - swp_freq; + else + *afcex_freq = swp_freq - *fOSC * 1000; + + AFCEX = *afcex_freq * 8192 / state->master_clk; + *AFCEX_L = AFCEX & 0x00ff; + *AFCEX_H = (AFCEX & 0x0f00) >> 8; +} + +static void afcex_info_get(struct mb86a16_state *state, + int afcex_freq, + unsigned char *AFCEX_L, + unsigned char *AFCEX_H) +{ + int AFCEX ; + + AFCEX = afcex_freq * 8192 / state->master_clk; + *AFCEX_L = AFCEX & 0x00ff; + *AFCEX_H = (AFCEX & 0x0f00) >> 8; +} + +static int SEQ_set(struct mb86a16_state *state, unsigned char loop) +{ + /* SLOCK0 = 0 */ + if (mb86a16_write(state, 0x32, 0x02 | (loop << 2)) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + return 0; +} + +static int iq_vt_set(struct mb86a16_state *state, unsigned char IQINV) +{ + /* Viterbi Rate, IQ Settings */ + if (mb86a16_write(state, 0x06, 0xdf | (IQINV << 5)) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + return 0; +} + +static int FEC_srst(struct mb86a16_state *state) +{ + if (mb86a16_write(state, MB86A16_RESET, 0x02) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + return 0; +} + +static int S2T_set(struct mb86a16_state *state, unsigned char S2T) +{ + if (mb86a16_write(state, 0x34, 0x70 | S2T) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + return 0; +} + +static int S45T_set(struct mb86a16_state *state, unsigned char S4T, unsigned char S5T) +{ + if (mb86a16_write(state, 0x35, 0x00 | (S5T << 4) | S4T) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + return 0; +} + + +static int mb86a16_set_fe(struct mb86a16_state *state) +{ + u8 agcval, cnmval; + + int i, j; + int fOSC = 0; + int fOSC_start = 0; + int wait_t; + int fcp; + int swp_ofs; + int V[60]; + u8 SIG1MIN; + + unsigned char CREN, AFCEN, AFCEXEN; + unsigned char SIG1; + unsigned char TIMINT1, TIMINT2, TIMEXT; + unsigned char S0T, S1T; + unsigned char S2T; +/* unsigned char S2T, S3T; */ + unsigned char S4T, S5T; + unsigned char AFCEX_L, AFCEX_H; + unsigned char R; + unsigned char VIRM; + unsigned char ETH, VIA; + unsigned char junk; + + int loop; + int ftemp; + int v, vmax, vmin; + int vmax_his, vmin_his; + int swp_freq, prev_swp_freq[20]; + int prev_freq_num; + int signal_dupl; + int afcex_freq; + int signal; + int afcerr; + int temp_freq, delta_freq; + int dagcm[4]; + int smrt_d; +/* int freq_err; */ + int n; + int ret = -1; + int sync; + + dprintk(verbose, MB86A16_INFO, 1, "freq=%d Mhz, symbrt=%d Ksps", state->frequency, state->srate); + + fcp = 3000; + swp_ofs = state->srate / 4; + + for (i = 0; i < 60; i++) + V[i] = -1; + + for (i = 0; i < 20; i++) + prev_swp_freq[i] = 0; + + SIG1MIN = 25; + + for (n = 0; ((n < 3) && (ret == -1)); n++) { + SEQ_set(state, 0); + iq_vt_set(state, 0); + + CREN = 0; + AFCEN = 0; + AFCEXEN = 1; + TIMINT1 = 0; + TIMINT2 = 1; + TIMEXT = 2; + S1T = 0; + S0T = 0; + + if (initial_set(state) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "initial set failed"); + return -1; + } + if (DAGC_data_set(state, 3, 2) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); + return -1; + } + if (EN_set(state, CREN, AFCEN) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); + return -1; /* (0, 0) */ + } + if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); + return -1; /* (1, smrt) = (1, symbolrate) */ + } + if (CNTM_set(state, TIMINT1, TIMINT2, TIMEXT) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "CNTM set error"); + return -1; /* (0, 1, 2) */ + } + if (S01T_set(state, S1T, S0T) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); + return -1; /* (0, 0) */ + } + smrt_info_get(state, state->srate); + if (smrt_set(state, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "smrt info get error"); + return -1; + } + + R = vco_dev_get(state, state->srate); + if (R == 1) + fOSC_start = state->frequency; + + else if (R == 0) { + if (state->frequency % 2 == 0) { + fOSC_start = state->frequency; + } else { + fOSC_start = state->frequency + 1; + if (fOSC_start > 2150) + fOSC_start = state->frequency - 1; + } + } + loop = 1; + ftemp = fOSC_start * 1000; + vmax = 0 ; + while (loop == 1) { + ftemp = ftemp + swp_ofs; + vmax++; + + /* Upper bound */ + if (ftemp > 2150000) { + loop = 0; + vmax--; + } else { + if ((ftemp == 2150000) || + (ftemp - state->frequency * 1000 >= fcp + state->srate / 4)) + loop = 0; + } + } + + loop = 1; + ftemp = fOSC_start * 1000; + vmin = 0 ; + while (loop == 1) { + ftemp = ftemp - swp_ofs; + vmin--; + + /* Lower bound */ + if (ftemp < 950000) { + loop = 0; + vmin++; + } else { + if ((ftemp == 950000) || + (state->frequency * 1000 - ftemp >= fcp + state->srate / 4)) + loop = 0; + } + } + + wait_t = (8000 + state->srate / 2) / state->srate; + if (wait_t == 0) + wait_t = 1; + + i = 0; + j = 0; + prev_freq_num = 0; + loop = 1; + signal = 0; + vmax_his = 0; + vmin_his = 0; + v = 0; + + while (loop == 1) { + swp_info_get(state, fOSC_start, state->srate, + v, R, swp_ofs, &fOSC, + &afcex_freq, &AFCEX_L, &AFCEX_H); + + udelay(100); + if (rf_val_set(state, fOSC, state->srate, R) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); + return -1; + } + udelay(100); + if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); + return -1; + } + if (srst(state) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "srst error"); + return -1; + } + msleep_interruptible(wait_t); + + if (mb86a16_read(state, 0x37, &SIG1) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -1; + } + V[30 + v] = SIG1 ; + swp_freq = swp_freq_calcuation(state, i, v, V, vmax, vmin, + SIG1MIN, fOSC, afcex_freq, + swp_ofs, &SIG1); /* changed */ + + signal_dupl = 0; + for (j = 0; j < prev_freq_num; j++) { + if ((abs(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) { + signal_dupl = 1; + dprintk(verbose, MB86A16_INFO, 1, "Probably Duplicate Signal, j = %d", j); + } + } + if ((signal_dupl == 0) && (swp_freq > 0) && (abs(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) { + dprintk(verbose, MB86A16_DEBUG, 1, "------ Signal detect ------ [swp_freq=[%07d, srate=%05d]]", swp_freq, state->srate); + prev_swp_freq[prev_freq_num] = swp_freq; + prev_freq_num++; + swp_info_get2(state, state->srate, R, swp_freq, + &afcex_freq, &fOSC, + &AFCEX_L, &AFCEX_H); + + if (rf_val_set(state, fOSC, state->srate, R) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); + return -1; + } + if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); + return -1; + } + signal = signal_det(state, state->srate, &SIG1); + if (signal == 1) { + dprintk(verbose, MB86A16_ERROR, 1, "***** Signal Found *****"); + loop = 0; + } else { + dprintk(verbose, MB86A16_ERROR, 1, "!!!!! No signal !!!!!, try again..."); + smrt_info_get(state, state->srate); + if (smrt_set(state, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); + return -1; + } + } + } + if (v > vmax) + vmax_his = 1 ; + if (v < vmin) + vmin_his = 1 ; + i++; + + if ((i % 2 == 1) && (vmax_his == 1)) + i++; + if ((i % 2 == 0) && (vmin_his == 1)) + i++; + + if (i % 2 == 1) + v = (i + 1) / 2; + else + v = -i / 2; + + if ((vmax_his == 1) && (vmin_his == 1)) + loop = 0 ; + } + + if (signal == 1) { + dprintk(verbose, MB86A16_INFO, 1, " Start Freq Error Check"); + S1T = 7 ; + S0T = 1 ; + CREN = 0 ; + AFCEN = 1 ; + AFCEXEN = 0 ; + + if (S01T_set(state, S1T, S0T) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); + return -1; + } + smrt_info_get(state, state->srate); + if (smrt_set(state, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); + return -1; + } + if (EN_set(state, CREN, AFCEN) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); + return -1; + } + if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); + return -1; + } + afcex_info_get(state, afcex_freq, &AFCEX_L, &AFCEX_H); + if (afcofs_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "AFCOFS data set error"); + return -1; + } + if (srst(state) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "srst error"); + return -1; + } + /* delay 4~200 */ + wait_t = 200000 / state->master_clk + 200000 / state->srate; + msleep(wait_t); + afcerr = afcerr_chk(state); + if (afcerr == -1) + return -1; + + swp_freq = fOSC * 1000 + afcerr ; + AFCEXEN = 1 ; + if (state->srate >= 1500) + smrt_d = state->srate / 3; + else + smrt_d = state->srate / 2; + smrt_info_get(state, smrt_d); + if (smrt_set(state, smrt_d) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); + return -1; + } + if (AFCEXEN_set(state, AFCEXEN, smrt_d) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); + return -1; + } + R = vco_dev_get(state, smrt_d); + if (DAGC_data_set(state, 2, 0) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); + return -1; + } + for (i = 0; i < 3; i++) { + temp_freq = swp_freq + (i - 1) * state->srate / 8; + swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); + if (rf_val_set(state, fOSC, smrt_d, R) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); + return -1; + } + if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); + return -1; + } + wait_t = 200000 / state->master_clk + 40000 / smrt_d; + msleep(wait_t); + dagcm[i] = dagcm_val_get(state); + } + if ((dagcm[0] > dagcm[1]) && + (dagcm[0] > dagcm[2]) && + (dagcm[0] - dagcm[1] > 2 * (dagcm[2] - dagcm[1]))) { + + temp_freq = swp_freq - 2 * state->srate / 8; + swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); + if (rf_val_set(state, fOSC, smrt_d, R) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); + return -1; + } + if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "afcex data set"); + return -1; + } + wait_t = 200000 / state->master_clk + 40000 / smrt_d; + msleep(wait_t); + dagcm[3] = dagcm_val_get(state); + if (dagcm[3] > dagcm[1]) + delta_freq = (dagcm[2] - dagcm[0] + dagcm[1] - dagcm[3]) * state->srate / 300; + else + delta_freq = 0; + } else if ((dagcm[2] > dagcm[1]) && + (dagcm[2] > dagcm[0]) && + (dagcm[2] - dagcm[1] > 2 * (dagcm[0] - dagcm[1]))) { + + temp_freq = swp_freq + 2 * state->srate / 8; + swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); + if (rf_val_set(state, fOSC, smrt_d, R) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "rf val set"); + return -1; + } + if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "afcex data set"); + return -1; + } + wait_t = 200000 / state->master_clk + 40000 / smrt_d; + msleep(wait_t); + dagcm[3] = dagcm_val_get(state); + if (dagcm[3] > dagcm[1]) + delta_freq = (dagcm[2] - dagcm[0] + dagcm[3] - dagcm[1]) * state->srate / 300; + else + delta_freq = 0 ; + + } else { + delta_freq = 0 ; + } + dprintk(verbose, MB86A16_INFO, 1, "SWEEP Frequency = %d", swp_freq); + swp_freq += delta_freq; + dprintk(verbose, MB86A16_INFO, 1, "Adjusting .., DELTA Freq = %d, SWEEP Freq=%d", delta_freq, swp_freq); + if (abs(state->frequency * 1000 - swp_freq) > 3800) { + dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL !"); + } else { + + S1T = 0; + S0T = 3; + CREN = 1; + AFCEN = 0; + AFCEXEN = 1; + + if (S01T_set(state, S1T, S0T) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); + return -1; + } + if (DAGC_data_set(state, 0, 0) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); + return -1; + } + R = vco_dev_get(state, state->srate); + smrt_info_get(state, state->srate); + if (smrt_set(state, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); + return -1; + } + if (EN_set(state, CREN, AFCEN) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); + return -1; + } + if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); + return -1; + } + swp_info_get2(state, state->srate, R, swp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); + if (rf_val_set(state, fOSC, state->srate, R) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); + return -1; + } + if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); + return -1; + } + if (srst(state) < 0) { + dprintk(verbose, MB86A16_ERROR, 1, "srst error"); + return -1; + } + wait_t = 7 + (10000 + state->srate / 2) / state->srate; + if (wait_t == 0) + wait_t = 1; + msleep_interruptible(wait_t); + if (mb86a16_read(state, 0x37, &SIG1) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + if (SIG1 > 110) { + S2T = 4; S4T = 1; S5T = 6; ETH = 4; VIA = 6; + wait_t = 7 + (917504 + state->srate / 2) / state->srate; + } else if (SIG1 > 105) { + S2T = 4; S4T = 2; S5T = 8; ETH = 7; VIA = 2; + wait_t = 7 + (1048576 + state->srate / 2) / state->srate; + } else if (SIG1 > 85) { + S2T = 5; S4T = 2; S5T = 8; ETH = 7; VIA = 2; + wait_t = 7 + (1310720 + state->srate / 2) / state->srate; + } else if (SIG1 > 65) { + S2T = 6; S4T = 2; S5T = 8; ETH = 7; VIA = 2; + wait_t = 7 + (1572864 + state->srate / 2) / state->srate; + } else { + S2T = 7; S4T = 2; S5T = 8; ETH = 7; VIA = 2; + wait_t = 7 + (2097152 + state->srate / 2) / state->srate; + } + wait_t *= 2; /* FOS */ + S2T_set(state, S2T); + S45T_set(state, S4T, S5T); + Vi_set(state, ETH, VIA); + srst(state); + msleep_interruptible(wait_t); + sync = sync_chk(state, &VIRM); + dprintk(verbose, MB86A16_INFO, 1, "-------- Viterbi=[%d] SYNC=[%d] ---------", VIRM, sync); + if (VIRM) { + if (VIRM == 4) { + /* 5/6 */ + if (SIG1 > 110) + wait_t = (786432 + state->srate / 2) / state->srate; + else + wait_t = (1572864 + state->srate / 2) / state->srate; + + msleep_interruptible(wait_t); + + if (sync_chk(state, &junk) == 0) { + iq_vt_set(state, 1); + FEC_srst(state); + } + } + /* 1/2, 2/3, 3/4, 7/8 */ + if (SIG1 > 110) + wait_t = (786432 + state->srate / 2) / state->srate; + else + wait_t = (1572864 + state->srate / 2) / state->srate; + msleep_interruptible(wait_t); + SEQ_set(state, 1); + } else { + dprintk(verbose, MB86A16_INFO, 1, "NO -- SYNC"); + SEQ_set(state, 1); + ret = -1; + } + } + } else { + dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL"); + ret = -1; + } + + sync = sync_chk(state, &junk); + if (sync) { + dprintk(verbose, MB86A16_INFO, 1, "******* SYNC *******"); + freqerr_chk(state, state->frequency, state->srate, 1); + ret = 0; + break; + } + } + + if (mb86a16_read(state, 0x15, &agcval) != 2 || mb86a16_read(state, 0x26, &cnmval) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + ret = -EREMOTEIO; + } else { + dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval); + } + return ret; +} + +static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct mb86a16_state *state = fe->demodulator_priv; + int ret = -EREMOTEIO; + int i; + u8 regs; + + if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCCOUT, 0x00) < 0) + goto err; + if (mb86a16_write(state, MB86A16_TONEOUT2, 0x04) < 0) + goto err; + + regs = 0x18; + + if (cmd->msg_len > 5 || cmd->msg_len < 4) { + ret = -EINVAL; + goto err; + } + + for (i = 0; i < cmd->msg_len; i++) { + if (mb86a16_write(state, regs, cmd->msg[i]) < 0) + goto err; + + regs++; + } + i += 0x90; + + msleep_interruptible(10); + + if (mb86a16_write(state, MB86A16_DCC1, i) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) + goto err; + + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return ret; +} + +static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct mb86a16_state *state = fe->demodulator_priv; + + switch (burst) { + case SEC_MINI_A: + if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | + MB86A16_DCC1_TBEN | + MB86A16_DCC1_TBO) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) + goto err; + break; + case SEC_MINI_B: + if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | + MB86A16_DCC1_TBEN) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) + goto err; + break; + } + + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int mb86a16_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct mb86a16_state *state = fe->demodulator_priv; + + switch (tone) { + case SEC_TONE_ON: + if (mb86a16_write(state, MB86A16_TONEOUT2, 0x00) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | + MB86A16_DCC1_CTOE) < 0) + + goto err; + if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) + goto err; + break; + case SEC_TONE_OFF: + if (mb86a16_write(state, MB86A16_TONEOUT2, 0x04) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA) < 0) + goto err; + if (mb86a16_write(state, MB86A16_DCCOUT, 0x00) < 0) + goto err; + break; + default: + return -EINVAL; + } + return 0; + +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static enum dvbfe_search mb86a16_search(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mb86a16_state *state = fe->demodulator_priv; + + state->frequency = p->frequency / 1000; + state->srate = p->symbol_rate / 1000; + + if (!mb86a16_set_fe(state)) { + dprintk(verbose, MB86A16_ERROR, 1, "Successfully acquired LOCK"); + return DVBFE_ALGO_SEARCH_SUCCESS; + } + + dprintk(verbose, MB86A16_ERROR, 1, "Lock acquisition failed!"); + return DVBFE_ALGO_SEARCH_FAILED; +} + +static void mb86a16_release(struct dvb_frontend *fe) +{ + struct mb86a16_state *state = fe->demodulator_priv; + kfree(state); +} + +static int mb86a16_init(struct dvb_frontend *fe) +{ + return 0; +} + +static int mb86a16_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int mb86a16_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + u8 ber_mon, ber_tab, ber_lsb, ber_mid, ber_msb, ber_tim, ber_rst; + u32 timer; + + struct mb86a16_state *state = fe->demodulator_priv; + + *ber = 0; + if (mb86a16_read(state, MB86A16_BERMON, &ber_mon) != 2) + goto err; + if (mb86a16_read(state, MB86A16_BERTAB, &ber_tab) != 2) + goto err; + if (mb86a16_read(state, MB86A16_BERLSB, &ber_lsb) != 2) + goto err; + if (mb86a16_read(state, MB86A16_BERMID, &ber_mid) != 2) + goto err; + if (mb86a16_read(state, MB86A16_BERMSB, &ber_msb) != 2) + goto err; + /* BER monitor invalid when BER_EN = 0 */ + if (ber_mon & 0x04) { + /* coarse, fast calculation */ + *ber = ber_tab & 0x1f; + dprintk(verbose, MB86A16_DEBUG, 1, "BER coarse=[0x%02x]", *ber); + if (ber_mon & 0x01) { + /* + * BER_SEL = 1, The monitored BER is the estimated + * value with a Reed-Solomon decoder error amount at + * the deinterleaver output. + * monitored BER is expressed as a 20 bit output in total + */ + ber_rst = (ber_mon >> 3) & 0x03; + *ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb; + if (ber_rst == 0) + timer = 12500000; + else if (ber_rst == 1) + timer = 25000000; + else if (ber_rst == 2) + timer = 50000000; + else /* ber_rst == 3 */ + timer = 100000000; + + *ber /= timer; + dprintk(verbose, MB86A16_DEBUG, 1, "BER fine=[0x%02x]", *ber); + } else { + /* + * BER_SEL = 0, The monitored BER is the estimated + * value with a Viterbi decoder error amount at the + * QPSK demodulator output. + * monitored BER is expressed as a 24 bit output in total + */ + ber_tim = (ber_mon >> 1) & 0x01; + *ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb; + if (ber_tim == 0) + timer = 16; + else /* ber_tim == 1 */ + timer = 24; + + *ber /= 2 ^ timer; + dprintk(verbose, MB86A16_DEBUG, 1, "BER fine=[0x%02x]", *ber); + } + } + return 0; +err: + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; +} + +static int mb86a16_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + u8 agcm = 0; + struct mb86a16_state *state = fe->demodulator_priv; + + *strength = 0; + if (mb86a16_read(state, MB86A16_AGCM, &agcm) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + *strength = ((0xff - agcm) * 100) / 256; + dprintk(verbose, MB86A16_DEBUG, 1, "Signal strength=[%d %%]", (u8) *strength); + *strength = (0xffff - 0xff) + agcm; + + return 0; +} + +struct cnr { + u8 cn_reg; + u8 cn_val; +}; + +static const struct cnr cnr_tab[] = { + { 35, 2 }, + { 40, 3 }, + { 50, 4 }, + { 60, 5 }, + { 70, 6 }, + { 80, 7 }, + { 92, 8 }, + { 103, 9 }, + { 115, 10 }, + { 138, 12 }, + { 162, 15 }, + { 180, 18 }, + { 185, 19 }, + { 189, 20 }, + { 195, 22 }, + { 199, 24 }, + { 201, 25 }, + { 202, 26 }, + { 203, 27 }, + { 205, 28 }, + { 208, 30 } +}; + +static int mb86a16_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mb86a16_state *state = fe->demodulator_priv; + int i = 0; + int low_tide = 2, high_tide = 30, q_level; + u8 cn; + + *snr = 0; + if (mb86a16_read(state, 0x26, &cn) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + + for (i = 0; i < ARRAY_SIZE(cnr_tab); i++) { + if (cn < cnr_tab[i].cn_reg) { + *snr = cnr_tab[i].cn_val; + break; + } + } + q_level = (*snr * 100) / (high_tide - low_tide); + dprintk(verbose, MB86A16_ERROR, 1, "SNR (Quality) = [%d dB], Level=%d %%", *snr, q_level); + *snr = (0xffff - 0xff) + *snr; + + return 0; +} + +static int mb86a16_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + u8 dist; + struct mb86a16_state *state = fe->demodulator_priv; + + if (mb86a16_read(state, MB86A16_DISTMON, &dist) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + return -EREMOTEIO; + } + *ucblocks = dist; + + return 0; +} + +static enum dvbfe_algo mb86a16_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_CUSTOM; +} + +static const struct dvb_frontend_ops mb86a16_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Fujitsu MB86A16 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 3 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + .release = mb86a16_release, + + .get_frontend_algo = mb86a16_frontend_algo, + .search = mb86a16_search, + .init = mb86a16_init, + .sleep = mb86a16_sleep, + .read_status = mb86a16_read_status, + + .read_ber = mb86a16_read_ber, + .read_signal_strength = mb86a16_read_signal_strength, + .read_snr = mb86a16_read_snr, + .read_ucblocks = mb86a16_read_ucblocks, + + .diseqc_send_master_cmd = mb86a16_send_diseqc_msg, + .diseqc_send_burst = mb86a16_send_diseqc_burst, + .set_tone = mb86a16_set_tone, +}; + +struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, + struct i2c_adapter *i2c_adap) +{ + u8 dev_id = 0; + struct mb86a16_state *state = NULL; + + state = kmalloc(sizeof(struct mb86a16_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->config = config; + state->i2c_adap = i2c_adap; + + mb86a16_read(state, 0x7f, &dev_id); + if (dev_id != 0xfe) + goto error; + + memcpy(&state->frontend.ops, &mb86a16_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + state->frontend.ops.set_voltage = state->config->set_voltage; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(mb86a16_attach); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h new file mode 100644 index 0000000000..c0c5419280 --- /dev/null +++ b/drivers/media/dvb-frontends/mb86a16.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Fujitsu MB86A16 DVB-S/DSS DC Receiver driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +#ifndef __MB86A16_H +#define __MB86A16_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + + +struct mb86a16_config { + u8 demod_address; + + int (*set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); +}; + + + +#if IS_REACHABLE(CONFIG_DVB_MB86A16) + +extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, + struct i2c_adapter *i2c_adap); + +#else + +static inline struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, + struct i2c_adapter *i2c_adap) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_MB86A16 */ + +#endif /* __MB86A16_H */ diff --git a/drivers/media/dvb-frontends/mb86a16_priv.h b/drivers/media/dvb-frontends/mb86a16_priv.h new file mode 100644 index 0000000000..1670e4b02c --- /dev/null +++ b/drivers/media/dvb-frontends/mb86a16_priv.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Fujitsu MB86A16 DVB-S/DSS DC Receiver driver + + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +#ifndef __MB86A16_PRIV_H +#define __MB86A16_PRIV_H + +#define MB86A16_TSOUT 0x00 +#define MB86A16_TSOUT_HIZSEL (0x01 << 5) +#define MB86A16_TSOUT_HIZCNTI (0x01 << 4) +#define MB86A16_TSOUT_MODE (0x01 << 3) +#define MB86A16_TSOUT_ORDER (0x01 << 2) +#define MB86A16_TSOUT_ERROR (0x01 << 1) +#define Mb86A16_TSOUT_EDGE (0x01 << 0) + +#define MB86A16_FEC 0x01 +#define MB86A16_FEC_FSYNC (0x01 << 5) +#define MB86A16_FEC_PCKB8 (0x01 << 4) +#define MB86A16_FEC_DVDS (0x01 << 3) +#define MB86A16_FEC_EREN (0x01 << 2) +#define Mb86A16_FEC_RSEN (0x01 << 1) +#define MB86A16_FEC_DIEN (0x01 << 0) + +#define MB86A16_AGC 0x02 +#define MB86A16_AGC_AGMD (0x01 << 6) +#define MB86A16_AGC_AGCW (0x0f << 2) +#define MB86A16_AGC_AGCP (0x01 << 1) +#define MB86A16_AGC_AGCR (0x01 << 0) + +#define MB86A16_SRATE1 0x03 +#define MB86A16_SRATE1_DECI (0x07 << 2) +#define MB86A16_SRATE1_CSEL (0x01 << 1) +#define MB86A16_SRATE1_RSEL (0x01 << 0) + +#define MB86A16_SRATE2 0x04 +#define MB86A16_SRATE2_STOFSL (0xff << 0) + +#define MB86A16_SRATE3 0x05 +#define MB86A16_SRATE2_STOFSH (0xff << 0) + +#define MB86A16_VITERBI 0x06 +#define MB86A16_FRAMESYNC 0x07 +#define MB86A16_CRLFILTCOEF1 0x08 +#define MB86A16_CRLFILTCOEF2 0x09 +#define MB86A16_STRFILTCOEF1 0x0a +#define MB86A16_STRFILTCOEF2 0x0b +#define MB86A16_RESET 0x0c +#define MB86A16_STATUS 0x0d +#define MB86A16_AFCML 0x0e +#define MB86A16_AFCMH 0x0f +#define MB86A16_BERMON 0x10 +#define MB86A16_BERTAB 0x11 +#define MB86A16_BERLSB 0x12 +#define MB86A16_BERMID 0x13 +#define MB86A16_BERMSB 0x14 +#define MB86A16_AGCM 0x15 + +#define MB86A16_DCC1 0x16 +#define MB86A16_DCC1_DISTA (0x01 << 7) +#define MB86A16_DCC1_PRTY (0x01 << 6) +#define MB86A16_DCC1_CTOE (0x01 << 5) +#define MB86A16_DCC1_TBEN (0x01 << 4) +#define MB86A16_DCC1_TBO (0x01 << 3) +#define MB86A16_DCC1_NUM (0x07 << 0) + +#define MB86A16_DCC2 0x17 +#define MB86A16_DCC2_DCBST (0x01 << 0) + +#define MB86A16_DCC3 0x18 +#define MB86A16_DCC3_CODE0 (0xff << 0) + +#define MB86A16_DCC4 0x19 +#define MB86A16_DCC4_CODE1 (0xff << 0) + +#define MB86A16_DCC5 0x1a +#define MB86A16_DCC5_CODE2 (0xff << 0) + +#define MB86A16_DCC6 0x1b +#define MB86A16_DCC6_CODE3 (0xff << 0) + +#define MB86A16_DCC7 0x1c +#define MB86A16_DCC7_CODE4 (0xff << 0) + +#define MB86A16_DCC8 0x1d +#define MB86A16_DCC8_CODE5 (0xff << 0) + +#define MB86A16_DCCOUT 0x1e +#define MB86A16_DCCOUT_DISEN (0x01 << 0) + +#define MB86A16_TONEOUT1 0x1f +#define MB86A16_TONE_TDIVL (0xff << 0) + +#define MB86A16_TONEOUT2 0x20 +#define MB86A16_TONE_TMD (0x03 << 2) +#define MB86A16_TONE_TDIVH (0x03 << 0) + +#define MB86A16_FREQ1 0x21 +#define MB86A16_FREQ2 0x22 +#define MB86A16_FREQ3 0x23 +#define MB86A16_FREQ4 0x24 +#define MB86A16_FREQSET 0x25 +#define MB86A16_CNM 0x26 +#define MB86A16_PORT0 0x27 +#define MB86A16_PORT1 0x28 +#define MB86A16_DRCFILT 0x29 +#define MB86A16_AFC 0x2a +#define MB86A16_AFCEXL 0x2b +#define MB86A16_AFCEXH 0x2c +#define MB86A16_DAGC 0x2d +#define MB86A16_SEQMODE 0x32 +#define MB86A16_S0S1T 0x33 +#define MB86A16_S2S3T 0x34 +#define MB86A16_S4S5T 0x35 +#define MB86A16_CNTMR 0x36 +#define MB86A16_SIG1 0x37 +#define MB86A16_SIG2 0x38 +#define MB86A16_VIMAG 0x39 +#define MB86A16_VISET1 0x3a +#define MB86A16_VISET2 0x3b +#define MB86A16_VISET3 0x3c +#define MB86A16_FAGCS1 0x3d +#define MB86A16_FAGCS2 0x3e +#define MB86A16_FAGCS3 0x3f +#define MB86A16_FAGCS4 0x40 +#define MB86A16_FAGCS5 0x41 +#define MB86A16_FAGCS6 0x42 +#define MB86A16_CRM 0x43 +#define MB86A16_STRM 0x44 +#define MB86A16_DAGCML 0x45 +#define MB86A16_DAGCMH 0x46 +#define MB86A16_QPSKTST 0x49 +#define MB86A16_DISTMON 0x52 +#define MB86A16_VERSION 0x7f + +#endif /* __MB86A16_PRIV_H */ diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c new file mode 100644 index 0000000000..f8e4bbee5b --- /dev/null +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -0,0 +1,2112 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver + * + * Copyright (C) 2010-2013 Mauro Carvalho Chehab + * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com> + */ + +#include <linux/kernel.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "mb86a20s.h" + +#define NUM_LAYERS 3 + +enum mb86a20s_bandwidth { + MB86A20S_13SEG = 0, + MB86A20S_13SEG_PARTIAL = 1, + MB86A20S_1SEG = 2, + MB86A20S_3SEG = 3, +}; + +static u8 mb86a20s_subchannel[] = { + 0xb0, 0xc0, 0xd0, 0xe0, + 0xf0, 0x00, 0x10, 0x20, +}; + +struct mb86a20s_state { + struct i2c_adapter *i2c; + const struct mb86a20s_config *config; + u32 last_frequency; + + struct dvb_frontend frontend; + + u32 if_freq; + enum mb86a20s_bandwidth bw; + bool inversion; + u32 subchannel; + + u32 estimated_rate[NUM_LAYERS]; + unsigned long get_strength_time; + + bool need_init; +}; + +struct regdata { + u8 reg; + u8 data; +}; + +#define BER_SAMPLING_RATE 1 /* Seconds */ + +/* + * Initialization sequence: Use whatevere default values that PV SBTVD + * does on its initialisation, obtained via USB snoop + */ +static struct regdata mb86a20s_init1[] = { + { 0x70, 0x0f }, + { 0x70, 0xff }, + { 0x08, 0x01 }, + { 0x50, 0xd1 }, { 0x51, 0x20 }, +}; + +static struct regdata mb86a20s_init2[] = { + { 0x50, 0xd1 }, { 0x51, 0x22 }, + { 0x39, 0x01 }, + { 0x71, 0x00 }, + { 0x3b, 0x21 }, + { 0x3c, 0x3a }, + { 0x01, 0x0d }, + { 0x04, 0x08 }, { 0x05, 0x05 }, + { 0x04, 0x0e }, { 0x05, 0x00 }, + { 0x04, 0x0f }, { 0x05, 0x14 }, + { 0x04, 0x0b }, { 0x05, 0x8c }, + { 0x04, 0x00 }, { 0x05, 0x00 }, + { 0x04, 0x01 }, { 0x05, 0x07 }, + { 0x04, 0x02 }, { 0x05, 0x0f }, + { 0x04, 0x03 }, { 0x05, 0xa0 }, + { 0x04, 0x09 }, { 0x05, 0x00 }, + { 0x04, 0x0a }, { 0x05, 0xff }, + { 0x04, 0x27 }, { 0x05, 0x64 }, + { 0x04, 0x28 }, { 0x05, 0x00 }, + { 0x04, 0x1e }, { 0x05, 0xff }, + { 0x04, 0x29 }, { 0x05, 0x0a }, + { 0x04, 0x32 }, { 0x05, 0x0a }, + { 0x04, 0x14 }, { 0x05, 0x02 }, + { 0x04, 0x04 }, { 0x05, 0x00 }, + { 0x04, 0x05 }, { 0x05, 0x22 }, + { 0x04, 0x06 }, { 0x05, 0x0e }, + { 0x04, 0x07 }, { 0x05, 0xd8 }, + { 0x04, 0x12 }, { 0x05, 0x00 }, + { 0x04, 0x13 }, { 0x05, 0xff }, + + /* + * On this demod, when the bit count reaches the count below, + * it collects the bit error count. The bit counters are initialized + * to 65535 here. This warrants that all of them will be quickly + * calculated when device gets locked. As TMCC is parsed, the values + * will be adjusted later in the driver's code. + */ + { 0x52, 0x01 }, /* Turn on BER before Viterbi */ + { 0x50, 0xa7 }, { 0x51, 0x00 }, + { 0x50, 0xa8 }, { 0x51, 0xff }, + { 0x50, 0xa9 }, { 0x51, 0xff }, + { 0x50, 0xaa }, { 0x51, 0x00 }, + { 0x50, 0xab }, { 0x51, 0xff }, + { 0x50, 0xac }, { 0x51, 0xff }, + { 0x50, 0xad }, { 0x51, 0x00 }, + { 0x50, 0xae }, { 0x51, 0xff }, + { 0x50, 0xaf }, { 0x51, 0xff }, + + /* + * On this demod, post BER counts blocks. When the count reaches the + * value below, it collects the block error count. The block counters + * are initialized to 127 here. This warrants that all of them will be + * quickly calculated when device gets locked. As TMCC is parsed, the + * values will be adjusted later in the driver's code. + */ + { 0x5e, 0x07 }, /* Turn on BER after Viterbi */ + { 0x50, 0xdc }, { 0x51, 0x00 }, + { 0x50, 0xdd }, { 0x51, 0x7f }, + { 0x50, 0xde }, { 0x51, 0x00 }, + { 0x50, 0xdf }, { 0x51, 0x7f }, + { 0x50, 0xe0 }, { 0x51, 0x00 }, + { 0x50, 0xe1 }, { 0x51, 0x7f }, + + /* + * On this demod, when the block count reaches the count below, + * it collects the block error count. The block counters are initialized + * to 127 here. This warrants that all of them will be quickly + * calculated when device gets locked. As TMCC is parsed, the values + * will be adjusted later in the driver's code. + */ + { 0x50, 0xb0 }, { 0x51, 0x07 }, /* Enable PER */ + { 0x50, 0xb2 }, { 0x51, 0x00 }, + { 0x50, 0xb3 }, { 0x51, 0x7f }, + { 0x50, 0xb4 }, { 0x51, 0x00 }, + { 0x50, 0xb5 }, { 0x51, 0x7f }, + { 0x50, 0xb6 }, { 0x51, 0x00 }, + { 0x50, 0xb7 }, { 0x51, 0x7f }, + + { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */ + { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ + { 0x45, 0x04 }, /* CN symbol 4 */ + { 0x48, 0x04 }, /* CN manual mode */ + { 0x50, 0xd5 }, { 0x51, 0x01 }, + { 0x50, 0xd6 }, { 0x51, 0x1f }, + { 0x50, 0xd2 }, { 0x51, 0x03 }, + { 0x50, 0xd7 }, { 0x51, 0x3f }, + { 0x1c, 0x01 }, + { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, + { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, + { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, + { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, + { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, + { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, + { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, + { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, + { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, + { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, + { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, + { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, + { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, + { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, + { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, + { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, + { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, + { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, + { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, + { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, + { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, + { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, + { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, + { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, + { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, + { 0x50, 0x1e }, { 0x51, 0x5d }, + { 0x50, 0x22 }, { 0x51, 0x00 }, + { 0x50, 0x23 }, { 0x51, 0xc8 }, + { 0x50, 0x24 }, { 0x51, 0x00 }, + { 0x50, 0x25 }, { 0x51, 0xf0 }, + { 0x50, 0x26 }, { 0x51, 0x00 }, + { 0x50, 0x27 }, { 0x51, 0xc3 }, + { 0x50, 0x39 }, { 0x51, 0x02 }, + { 0x50, 0xd5 }, { 0x51, 0x01 }, + { 0xd0, 0x00 }, +}; + +static struct regdata mb86a20s_reset_reception[] = { + { 0x70, 0xf0 }, + { 0x70, 0xff }, + { 0x08, 0x01 }, + { 0x08, 0x00 }, +}; + +static struct regdata mb86a20s_per_ber_reset[] = { + { 0x53, 0x00 }, /* pre BER Counter reset */ + { 0x53, 0x07 }, + + { 0x5f, 0x00 }, /* post BER Counter reset */ + { 0x5f, 0x07 }, + + { 0x50, 0xb1 }, /* PER Counter reset */ + { 0x51, 0x07 }, + { 0x51, 0x00 }, +}; + +/* + * I2C read/write functions and macros + */ + +static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, + u8 i2c_addr, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 + }; + int rc; + + rc = i2c_transfer(state->i2c, &msg, 1); + if (rc != 1) { + dev_err(&state->i2c->dev, + "%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, rc, reg, data); + return rc; + } + + return 0; +} + +static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state, + u8 i2c_addr, struct regdata *rd, int size) +{ + int i, rc; + + for (i = 0; i < size; i++) { + rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg, + rd[i].data); + if (rc < 0) + return rc; + } + return 0; +} + +static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, + u8 i2c_addr, u8 reg) +{ + u8 val; + int rc; + struct i2c_msg msg[] = { + { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } + }; + + rc = i2c_transfer(state->i2c, msg, 2); + + if (rc != 2) { + dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n", + __func__, reg, rc); + return (rc < 0) ? rc : -EIO; + } + + return val; +} + +#define mb86a20s_readreg(state, reg) \ + mb86a20s_i2c_readreg(state, state->config->demod_address, reg) +#define mb86a20s_writereg(state, reg, val) \ + mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val) +#define mb86a20s_writeregdata(state, regdata) \ + mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ + regdata, ARRAY_SIZE(regdata)) + +/* + * Ancillary internal routines (likely compiled inlined) + * + * The functions below assume that gateway lock has already obtained + */ + +static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int val; + + *status = 0; + + val = mb86a20s_readreg(state, 0x0a); + if (val < 0) + return val; + + val &= 0xf; + if (val >= 2) + *status |= FE_HAS_SIGNAL; + + if (val >= 4) + *status |= FE_HAS_CARRIER; + + if (val >= 5) + *status |= FE_HAS_VITERBI; + + if (val >= 7) + *status |= FE_HAS_SYNC; + + /* + * Actually, on state S8, it starts receiving TS, but the TS + * output is only on normal state after the transition to S9. + */ + if (val >= 9) + *status |= FE_HAS_LOCK; + + dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", + __func__, *status, val); + + return val; +} + +static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + unsigned rf_max, rf_min, rf; + + if (state->get_strength_time && + (!time_after(jiffies, state->get_strength_time))) + return c->strength.stat[0].uvalue; + + /* Reset its value if an error happen */ + c->strength.stat[0].uvalue = 0; + + /* Does a binary search to get RF strength */ + rf_max = 0xfff; + rf_min = 0; + do { + rf = (rf_max + rf_min) / 2; + rc = mb86a20s_writereg(state, 0x04, 0x1f); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x05, rf >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, 0x20); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x05, rf); + if (rc < 0) + return rc; + + rc = mb86a20s_readreg(state, 0x02); + if (rc < 0) + return rc; + if (rc & 0x08) + rf_min = (rf_max + rf_min) / 2; + else + rf_max = (rf_max + rf_min) / 2; + if (rf_max - rf_min < 4) { + rf = (rf_max + rf_min) / 2; + + /* Rescale it from 2^12 (4096) to 2^16 */ + rf = rf << (16 - 12); + if (rf) + rf |= (1 << 12) - 1; + + dev_dbg(&state->i2c->dev, + "%s: signal strength = %d (%d < RF=%d < %d)\n", + __func__, rf, rf_min, rf >> 4, rf_max); + c->strength.stat[0].uvalue = rf; + state->get_strength_time = jiffies + + msecs_to_jiffies(1000); + return 0; + } + } while (1); +} + +static int mb86a20s_get_modulation(struct mb86a20s_state *state, + unsigned layer) +{ + int rc; + static unsigned char reg[] = { + [0] = 0x86, /* Layer A */ + [1] = 0x8a, /* Layer B */ + [2] = 0x8e, /* Layer C */ + }; + + if (layer >= ARRAY_SIZE(reg)) + return -EINVAL; + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + switch ((rc >> 4) & 0x07) { + case 0: + return DQPSK; + case 1: + return QPSK; + case 2: + return QAM_16; + case 3: + return QAM_64; + default: + return QAM_AUTO; + } +} + +static int mb86a20s_get_fec(struct mb86a20s_state *state, + unsigned layer) +{ + int rc; + + static unsigned char reg[] = { + [0] = 0x87, /* Layer A */ + [1] = 0x8b, /* Layer B */ + [2] = 0x8f, /* Layer C */ + }; + + if (layer >= ARRAY_SIZE(reg)) + return -EINVAL; + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + switch ((rc >> 4) & 0x07) { + case 0: + return FEC_1_2; + case 1: + return FEC_2_3; + case 2: + return FEC_3_4; + case 3: + return FEC_5_6; + case 4: + return FEC_7_8; + default: + return FEC_AUTO; + } +} + +static int mb86a20s_get_interleaving(struct mb86a20s_state *state, + unsigned layer) +{ + int rc; + static const int interleaving[] = { + 0, 1, 2, 4, 8 + }; + + static const unsigned char reg[] = { + [0] = 0x88, /* Layer A */ + [1] = 0x8c, /* Layer B */ + [2] = 0x90, /* Layer C */ + }; + + if (layer >= ARRAY_SIZE(reg)) + return -EINVAL; + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + + return interleaving[(rc >> 4) & 0x07]; +} + +static int mb86a20s_get_segment_count(struct mb86a20s_state *state, + unsigned layer) +{ + int rc, count; + static unsigned char reg[] = { + [0] = 0x89, /* Layer A */ + [1] = 0x8d, /* Layer B */ + [2] = 0x91, /* Layer C */ + }; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= ARRAY_SIZE(reg)) + return -EINVAL; + + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + count = (rc >> 4) & 0x0f; + + dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count); + + return count; +} + +static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Fixed parameters */ + c->delivery_system = SYS_ISDBT; + c->bandwidth_hz = 6000000; + + /* Initialize values that will be later autodetected */ + c->isdbt_layer_enabled = 0; + c->transmission_mode = TRANSMISSION_MODE_AUTO; + c->guard_interval = GUARD_INTERVAL_AUTO; + c->isdbt_sb_mode = 0; + c->isdbt_sb_segment_count = 0; +} + +/* + * Estimates the bit rate using the per-segment bit rate given by + * ABNT/NBR 15601 spec (table 4). + */ +static const u32 isdbt_rate[3][5][4] = { + { /* DQPSK/QPSK */ + { 280850, 312060, 330420, 340430 }, /* 1/2 */ + { 374470, 416080, 440560, 453910 }, /* 2/3 */ + { 421280, 468090, 495630, 510650 }, /* 3/4 */ + { 468090, 520100, 550700, 567390 }, /* 5/6 */ + { 491500, 546110, 578230, 595760 }, /* 7/8 */ + }, { /* QAM16 */ + { 561710, 624130, 660840, 680870 }, /* 1/2 */ + { 748950, 832170, 881120, 907820 }, /* 2/3 */ + { 842570, 936190, 991260, 1021300 }, /* 3/4 */ + { 936190, 1040210, 1101400, 1134780 }, /* 5/6 */ + { 983000, 1092220, 1156470, 1191520 }, /* 7/8 */ + }, { /* QAM64 */ + { 842570, 936190, 991260, 1021300 }, /* 1/2 */ + { 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */ + { 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */ + { 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */ + { 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */ + } +}; + +static u32 isdbt_layer_min_bitrate(struct dtv_frontend_properties *c, + u32 layer) +{ + int mod, fec, guard; + + /* + * If modulation/fec/guard is not detected, the default is + * to consider the lowest bit rate, to avoid taking too long time + * to get BER. + */ + switch (c->layer[layer].modulation) { + case DQPSK: + case QPSK: + default: + mod = 0; + break; + case QAM_16: + mod = 1; + break; + case QAM_64: + mod = 2; + break; + } + + switch (c->layer[layer].fec) { + default: + case FEC_1_2: + case FEC_AUTO: + fec = 0; + break; + case FEC_2_3: + fec = 1; + break; + case FEC_3_4: + fec = 2; + break; + case FEC_5_6: + fec = 3; + break; + case FEC_7_8: + fec = 4; + break; + } + + switch (c->guard_interval) { + default: + case GUARD_INTERVAL_1_4: + guard = 0; + break; + case GUARD_INTERVAL_1_8: + guard = 1; + break; + case GUARD_INTERVAL_1_16: + guard = 2; + break; + case GUARD_INTERVAL_1_32: + guard = 3; + break; + } + + return isdbt_rate[mod][fec][guard] * c->layer[layer].segment_count; +} + +static int mb86a20s_get_frontend(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int layer, rc, rate, counter; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Reset frontend cache to default values */ + mb86a20s_reset_frontend_cache(fe); + + /* Check for partial reception */ + rc = mb86a20s_writereg(state, 0x6d, 0x85); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; + + /* Get per-layer data */ + + for (layer = 0; layer < NUM_LAYERS; layer++) { + dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n", + __func__, 'A' + layer); + + rc = mb86a20s_get_segment_count(state, layer); + if (rc < 0) + goto noperlayer_error; + if (rc >= 0 && rc < 14) { + c->layer[layer].segment_count = rc; + } else { + c->layer[layer].segment_count = 0; + state->estimated_rate[layer] = 0; + continue; + } + c->isdbt_layer_enabled |= 1 << layer; + rc = mb86a20s_get_modulation(state, layer); + if (rc < 0) + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: modulation %d.\n", + __func__, rc); + c->layer[layer].modulation = rc; + rc = mb86a20s_get_fec(state, layer); + if (rc < 0) + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: FEC %d.\n", + __func__, rc); + c->layer[layer].fec = rc; + rc = mb86a20s_get_interleaving(state, layer); + if (rc < 0) + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", + __func__, rc); + c->layer[layer].interleaving = rc; + + rate = isdbt_layer_min_bitrate(c, layer); + counter = rate * BER_SAMPLING_RATE; + + /* Avoids sampling too quickly or to overflow the register */ + if (counter < 256) + counter = 256; + else if (counter > (1 << 24) - 1) + counter = (1 << 24) - 1; + + dev_dbg(&state->i2c->dev, + "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", + __func__, 'A' + layer, rate / 1000, counter, counter); + + state->estimated_rate[layer] = counter; + } + + rc = mb86a20s_writereg(state, 0x6d, 0x84); + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + c->isdbt_sb_mode = 1; + /* At least, one segment should exist */ + if (!c->isdbt_sb_segment_count) + c->isdbt_sb_segment_count = 1; + } + + /* Get transmission mode and guard interval */ + rc = mb86a20s_readreg(state, 0x07); + if (rc < 0) + return rc; + c->transmission_mode = TRANSMISSION_MODE_AUTO; + if ((rc & 0x60) == 0x20) { + /* Only modes 2 and 3 are supported */ + switch ((rc >> 2) & 0x03) { + case 1: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case 2: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + } + c->guard_interval = GUARD_INTERVAL_AUTO; + if (!(rc & 0x10)) { + /* Guard interval 1/32 is not supported */ + switch (rc & 0x3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + } + } + return 0; + +noperlayer_error: + + /* per-layer info is incomplete; discard all per-layer */ + c->isdbt_layer_enabled = 0; + + return rc; +} + +static int mb86a20s_reset_counters(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Reset the counters, if the channel changed */ + if (state->last_frequency != c->frequency) { + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error)); + memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count)); + memset(&c->post_bit_error, 0, sizeof(c->post_bit_error)); + memset(&c->post_bit_count, 0, sizeof(c->post_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + memset(&c->block_count, 0, sizeof(c->block_count)); + + state->last_frequency = c->frequency; + } + + /* Clear status for most stats */ + + /* BER/PER counter reset */ + rc = mb86a20s_writeregdata(state, mb86a20s_per_ber_reset); + if (rc < 0) + goto err; + + /* CNR counter reset */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + if (rc < 0) + goto err; + + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + goto err; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + goto err; + + goto ok; +err: + dev_err(&state->i2c->dev, + "%s: Can't reset FE statistics (error %d).\n", + __func__, rc); +ok: + return rc; +} + +static int mb86a20s_get_pre_ber(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= NUM_LAYERS) + return -EINVAL; + + /* Check if the BER measures are already available */ + rc = mb86a20s_readreg(state, 0x54); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: preBER for layer %c is not available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Bit Error Count */ + rc = mb86a20s_readreg(state, 0x55 + layer * 3); + if (rc < 0) + return rc; + *error = rc << 16; + rc = mb86a20s_readreg(state, 0x56 + layer * 3); + if (rc < 0) + return rc; + *error |= rc << 8; + rc = mb86a20s_readreg(state, 0x57 + layer * 3); + if (rc < 0) + return rc; + *error |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit error before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit count before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (state->estimated_rate[layer] + && state->estimated_rate[layer] != *count) { + dev_dbg(&state->i2c->dev, + "%s: updating layer %c preBER counter to %d.\n", + __func__, 'A' + layer, state->estimated_rate[layer]); + + /* Turn off BER before Viterbi */ + rc = mb86a20s_writereg(state, 0x52, 0x00); + + /* Update counter for this layer */ + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 16); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer]); + if (rc < 0) + return rc; + + /* Turn on BER before Viterbi */ + rc = mb86a20s_writereg(state, 0x52, 0x01); + + /* Reset all preBER counters */ + rc = mb86a20s_writereg(state, 0x53, 0x00); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x53, 0x07); + } else { + /* Reset counter to collect new data */ + rc = mb86a20s_readreg(state, 0x53); + if (rc < 0) + return rc; + val = rc; + rc = mb86a20s_writereg(state, 0x53, val & ~(1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x53, val | (1 << layer)); + } + + return rc; +} + +static int mb86a20s_get_post_ber(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + u32 counter, collect_rate; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= NUM_LAYERS) + return -EINVAL; + + /* Check if the BER measures are already available */ + rc = mb86a20s_readreg(state, 0x60); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: post BER for layer %c is not available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Bit Error Count */ + rc = mb86a20s_readreg(state, 0x64 + layer * 3); + if (rc < 0) + return rc; + *error = rc << 16; + rc = mb86a20s_readreg(state, 0x65 + layer * 3); + if (rc < 0) + return rc; + *error |= rc << 8; + rc = mb86a20s_readreg(state, 0x66 + layer * 3); + if (rc < 0) + return rc; + *error |= rc; + + dev_dbg(&state->i2c->dev, + "%s: post bit error for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + counter = rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + counter |= rc; + *count = counter * 204 * 8; + + dev_dbg(&state->i2c->dev, + "%s: post bit count for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (!state->estimated_rate[layer]) + goto reset_measurement; + + collect_rate = state->estimated_rate[layer] / 204 / 8; + if (collect_rate < 32) + collect_rate = 32; + if (collect_rate > 65535) + collect_rate = 65535; + if (collect_rate != counter) { + dev_dbg(&state->i2c->dev, + "%s: updating postBER counter on layer %c to %d.\n", + __func__, 'A' + layer, collect_rate); + + /* Turn off BER after Viterbi */ + rc = mb86a20s_writereg(state, 0x5e, 0x00); + + /* Update counter for this layer */ + rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff); + if (rc < 0) + return rc; + + /* Turn on BER after Viterbi */ + rc = mb86a20s_writereg(state, 0x5e, 0x07); + + /* Reset all preBER counters */ + rc = mb86a20s_writereg(state, 0x5f, 0x00); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x5f, 0x07); + + return rc; + } + +reset_measurement: + /* Reset counter to collect new data */ + rc = mb86a20s_readreg(state, 0x5f); + if (rc < 0) + return rc; + val = rc; + rc = mb86a20s_writereg(state, 0x5f, val & ~(1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x5f, val | (1 << layer)); + + return rc; +} + +static int mb86a20s_get_blk_error(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc, val; + u32 collect_rate; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= NUM_LAYERS) + return -EINVAL; + + /* Check if the PER measures are already available */ + rc = mb86a20s_writereg(state, 0x50, 0xb8); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: block counts for layer %c aren't available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Packet error Count */ + rc = mb86a20s_writereg(state, 0x50, 0xb9 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *error = rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xba + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *error |= rc; + dev_dbg(&state->i2c->dev, "%s: block error for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count = rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc; + + dev_dbg(&state->i2c->dev, + "%s: block count for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (!state->estimated_rate[layer]) + goto reset_measurement; + + collect_rate = state->estimated_rate[layer] / 204 / 8; + if (collect_rate < 32) + collect_rate = 32; + if (collect_rate > 65535) + collect_rate = 65535; + + if (collect_rate != *count) { + dev_dbg(&state->i2c->dev, + "%s: updating PER counter on layer %c to %d.\n", + __func__, 'A' + layer, collect_rate); + + /* Stop PER measurement */ + rc = mb86a20s_writereg(state, 0x50, 0xb0); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x00); + if (rc < 0) + return rc; + + /* Update this layer's counter */ + rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff); + if (rc < 0) + return rc; + + /* start PER measurement */ + rc = mb86a20s_writereg(state, 0x50, 0xb0); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x07); + if (rc < 0) + return rc; + + /* Reset all counters to collect new data */ + rc = mb86a20s_writereg(state, 0x50, 0xb1); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x07); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, 0x00); + + return rc; + } + +reset_measurement: + /* Reset counter to collect new data */ + rc = mb86a20s_writereg(state, 0x50, 0xb1); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + val = rc; + rc = mb86a20s_writereg(state, 0x51, val | (1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, val & ~(1 << layer)); + + return rc; +} + +struct linear_segments { + unsigned x, y; +}; + +/* + * All tables below return a dB/1000 measurement + */ + +static const struct linear_segments cnr_to_db_table[] = { + { 19648, 0}, + { 18187, 1000}, + { 16534, 2000}, + { 14823, 3000}, + { 13161, 4000}, + { 11622, 5000}, + { 10279, 6000}, + { 9089, 7000}, + { 8042, 8000}, + { 7137, 9000}, + { 6342, 10000}, + { 5641, 11000}, + { 5030, 12000}, + { 4474, 13000}, + { 3988, 14000}, + { 3556, 15000}, + { 3180, 16000}, + { 2841, 17000}, + { 2541, 18000}, + { 2276, 19000}, + { 2038, 20000}, + { 1800, 21000}, + { 1625, 22000}, + { 1462, 23000}, + { 1324, 24000}, + { 1175, 25000}, + { 1063, 26000}, + { 980, 27000}, + { 907, 28000}, + { 840, 29000}, + { 788, 30000}, +}; + +static const struct linear_segments cnr_64qam_table[] = { + { 3922688, 0}, + { 3920384, 1000}, + { 3902720, 2000}, + { 3894784, 3000}, + { 3882496, 4000}, + { 3872768, 5000}, + { 3858944, 6000}, + { 3851520, 7000}, + { 3838976, 8000}, + { 3829248, 9000}, + { 3818240, 10000}, + { 3806976, 11000}, + { 3791872, 12000}, + { 3767040, 13000}, + { 3720960, 14000}, + { 3637504, 15000}, + { 3498496, 16000}, + { 3296000, 17000}, + { 3031040, 18000}, + { 2715392, 19000}, + { 2362624, 20000}, + { 1963264, 21000}, + { 1649664, 22000}, + { 1366784, 23000}, + { 1120768, 24000}, + { 890880, 25000}, + { 723456, 26000}, + { 612096, 27000}, + { 518912, 28000}, + { 448256, 29000}, + { 388864, 30000}, +}; + +static const struct linear_segments cnr_16qam_table[] = { + { 5314816, 0}, + { 5219072, 1000}, + { 5118720, 2000}, + { 4998912, 3000}, + { 4875520, 4000}, + { 4736000, 5000}, + { 4604160, 6000}, + { 4458752, 7000}, + { 4300288, 8000}, + { 4092928, 9000}, + { 3836160, 10000}, + { 3521024, 11000}, + { 3155968, 12000}, + { 2756864, 13000}, + { 2347008, 14000}, + { 1955072, 15000}, + { 1593600, 16000}, + { 1297920, 17000}, + { 1043968, 18000}, + { 839680, 19000}, + { 672256, 20000}, + { 523008, 21000}, + { 424704, 22000}, + { 345088, 23000}, + { 280064, 24000}, + { 221440, 25000}, + { 179712, 26000}, + { 151040, 27000}, + { 128512, 28000}, + { 110080, 29000}, + { 95744, 30000}, +}; + +static const struct linear_segments cnr_qpsk_table[] = { + { 2834176, 0}, + { 2683648, 1000}, + { 2536960, 2000}, + { 2391808, 3000}, + { 2133248, 4000}, + { 1906176, 5000}, + { 1666560, 6000}, + { 1422080, 7000}, + { 1189632, 8000}, + { 976384, 9000}, + { 790272, 10000}, + { 633344, 11000}, + { 505600, 12000}, + { 402944, 13000}, + { 320768, 14000}, + { 255488, 15000}, + { 204032, 16000}, + { 163072, 17000}, + { 130304, 18000}, + { 105216, 19000}, + { 83456, 20000}, + { 65024, 21000}, + { 52480, 22000}, + { 42752, 23000}, + { 34560, 24000}, + { 27136, 25000}, + { 22016, 26000}, + { 18432, 27000}, + { 15616, 28000}, + { 13312, 29000}, + { 11520, 30000}, +}; + +static u32 interpolate_value(u32 value, const struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx, dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i].y - segments[i - 1].y; + dx = segments[i - 1].x - segments[i].x; + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y - tmp64; + + return ret; +} + +static int mb86a20s_get_main_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 cnr_linear, cnr; + int rc, val; + + /* Check if CNR is available */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + return rc; + + if (!(rc & 0x40)) { + dev_dbg(&state->i2c->dev, "%s: CNR is not available yet.\n", + __func__); + return -EBUSY; + } + val = rc; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear = rc << 8; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear |= rc; + + cnr = interpolate_value(cnr_linear, + cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table)); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + + dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n", + __func__, cnr / 1000, cnr % 1000, cnr_linear); + + /* CNR counter reset */ + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + + return rc; +} + +static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 mer, cnr; + int rc, val, layer; + const struct linear_segments *segs; + unsigned segs_len; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Check if the measures are already available */ + rc = mb86a20s_writereg(state, 0x50, 0x5b); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + + /* Check if data is available */ + if (!(rc & 0x01)) { + dev_dbg(&state->i2c->dev, + "%s: MER measures aren't available yet.\n", __func__); + return -EBUSY; + } + + /* Read all layers */ + for (layer = 0; layer < NUM_LAYERS; layer++) { + if (!(c->isdbt_layer_enabled & (1 << layer))) { + c->cnr.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + continue; + } + + rc = mb86a20s_writereg(state, 0x50, 0x52 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0x53 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0x54 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc; + + switch (c->layer[layer].modulation) { + case DQPSK: + case QPSK: + segs = cnr_qpsk_table; + segs_len = ARRAY_SIZE(cnr_qpsk_table); + break; + case QAM_16: + segs = cnr_16qam_table; + segs_len = ARRAY_SIZE(cnr_16qam_table); + break; + default: + case QAM_64: + segs = cnr_64qam_table; + segs_len = ARRAY_SIZE(cnr_64qam_table); + break; + } + cnr = interpolate_value(mer, segs, segs_len); + + c->cnr.stat[1 + layer].scale = FE_SCALE_DECIBEL; + c->cnr.stat[1 + layer].svalue = cnr; + + dev_dbg(&state->i2c->dev, + "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n", + __func__, 'A' + layer, cnr / 1000, cnr % 1000, mer); + + } + + /* Start a new MER measurement */ + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + val = rc; + + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + return rc; + + return 0; +} + +static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int layer; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Fill the length of each status counter */ + + /* Only global stats */ + c->strength.len = 1; + + /* Per-layer stats - 3 layers + global */ + c->cnr.len = NUM_LAYERS + 1; + c->pre_bit_error.len = NUM_LAYERS + 1; + c->pre_bit_count.len = NUM_LAYERS + 1; + c->post_bit_error.len = NUM_LAYERS + 1; + c->post_bit_count.len = NUM_LAYERS + 1; + c->block_error.len = NUM_LAYERS + 1; + c->block_count.len = NUM_LAYERS + 1; + + /* Signal is always available */ + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = 0; + + /* Put all of them at FE_SCALE_NOT_AVAILABLE */ + for (layer = 0; layer < NUM_LAYERS + 1; layer++) { + c->cnr.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE; + } +} + +static int mb86a20s_get_stats(struct dvb_frontend *fe, int status_nr) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc = 0, layer; + u32 bit_error = 0, bit_count = 0; + u32 t_pre_bit_error = 0, t_pre_bit_count = 0; + u32 t_post_bit_error = 0, t_post_bit_count = 0; + u32 block_error = 0, block_count = 0; + u32 t_block_error = 0, t_block_count = 0; + int pre_ber_layers = 0, post_ber_layers = 0; + int per_layers = 0; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + mb86a20s_get_main_CNR(fe); + + /* Get per-layer stats */ + mb86a20s_get_blk_error_layer_CNR(fe); + + /* + * At state 7, only CNR is available + * For BER measures, state=9 is required + * FIXME: we may get MER measures with state=8 + */ + if (status_nr < 9) + return 0; + + for (layer = 0; layer < NUM_LAYERS; layer++) { + if (c->isdbt_layer_enabled & (1 << layer)) { + /* Handle BER before vterbi */ + rc = mb86a20s_get_pre_ber(fe, layer, + &bit_error, &bit_count); + if (rc >= 0) { + c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[1 + layer].uvalue += bit_error; + c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[1 + layer].uvalue += bit_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get BER for layer %c (error %d).\n", + __func__, 'A' + layer, rc); + } + if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE) + pre_ber_layers++; + + /* Handle BER post vterbi */ + rc = mb86a20s_get_post_ber(fe, layer, + &bit_error, &bit_count); + if (rc >= 0) { + c->post_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[1 + layer].uvalue += bit_error; + c->post_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[1 + layer].uvalue += bit_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->post_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get BER for layer %c (error %d).\n", + __func__, 'A' + layer, rc); + } + if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE) + post_ber_layers++; + + /* Handle Block errors for PER/UCB reports */ + rc = mb86a20s_get_blk_error(fe, layer, + &block_error, + &block_count); + if (rc >= 0) { + c->block_error.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->block_error.stat[1 + layer].uvalue += block_error; + c->block_count.stat[1 + layer].scale = FE_SCALE_COUNTER; + c->block_count.stat[1 + layer].uvalue += block_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->block_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get PER for layer %c (error %d).\n", + __func__, 'A' + layer, rc); + + } + if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE) + per_layers++; + + /* Update total preBER */ + t_pre_bit_error += c->pre_bit_error.stat[1 + layer].uvalue; + t_pre_bit_count += c->pre_bit_count.stat[1 + layer].uvalue; + + /* Update total postBER */ + t_post_bit_error += c->post_bit_error.stat[1 + layer].uvalue; + t_post_bit_count += c->post_bit_count.stat[1 + layer].uvalue; + + /* Update total PER */ + t_block_error += c->block_error.stat[1 + layer].uvalue; + t_block_count += c->block_count.stat[1 + layer].uvalue; + } + } + + /* + * Start showing global count if at least one error count is + * available. + */ + if (pre_ber_layers) { + /* + * At least one per-layer BER measure was read. We can now + * calculate the total BER + * + * Total Bit Error/Count is calculated as the sum of the + * bit errors on all active layers. + */ + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue = t_pre_bit_error; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue = t_pre_bit_count; + } else { + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + } + + /* + * Start showing global count if at least one error count is + * available. + */ + if (post_ber_layers) { + /* + * At least one per-layer BER measure was read. We can now + * calculate the total BER + * + * Total Bit Error/Count is calculated as the sum of the + * bit errors on all active layers. + */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = t_post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = t_post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + } + + if (per_layers) { + /* + * At least one per-layer UCB measure was read. We can now + * calculate the total UCB + * + * Total block Error/Count is calculated as the sum of the + * block errors on all active layers. + */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = t_block_error; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue = t_block_count; + } else { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + } + + return rc; +} + +/* + * The functions below are called via DVB callbacks, so they need to + * properly use the I2C gate control + */ + +static int mb86a20s_initfe(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + u64 pll; + u32 fclk; + int rc; + u8 regD5 = 1, reg71, reg09 = 0x3a; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* Initialize the frontend */ + rc = mb86a20s_writeregdata(state, mb86a20s_init1); + if (rc < 0) + goto err; + + if (!state->inversion) + reg09 |= 0x04; + rc = mb86a20s_writereg(state, 0x09, reg09); + if (rc < 0) + goto err; + if (!state->bw) + reg71 = 1; + else + reg71 = 0; + rc = mb86a20s_writereg(state, 0x39, reg71); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x71, state->bw); + if (rc < 0) + goto err; + if (state->subchannel) { + rc = mb86a20s_writereg(state, 0x44, state->subchannel); + if (rc < 0) + goto err; + } + + fclk = state->config->fclk; + if (!fclk) + fclk = 32571428; + + /* Adjust IF frequency to match tuner */ + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &state->if_freq); + + if (!state->if_freq) + state->if_freq = 3300000; + + pll = (((u64)1) << 34) * state->if_freq; + do_div(pll, 63 * fclk); + pll = (1 << 25) - pll; + rc = mb86a20s_writereg(state, 0x28, 0x2a); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2b, pll & 0xff); + if (rc < 0) + goto err; + dev_dbg(&state->i2c->dev, "%s: fclk=%d, IF=%d, clock reg=0x%06llx\n", + __func__, fclk, state->if_freq, (long long)pll); + + /* pll = freq[Hz] * 2^24/10^6 / 16.285714286 */ + pll = state->if_freq * 1677721600L; + do_div(pll, 1628571429L); + rc = mb86a20s_writereg(state, 0x28, 0x20); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x2b, pll & 0xff); + if (rc < 0) + goto err; + dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n", + __func__, state->if_freq, (long long)pll); + + if (!state->config->is_serial) + regD5 &= ~1; + + rc = mb86a20s_writereg(state, 0x50, 0xd5); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, regD5); + if (rc < 0) + goto err; + + rc = mb86a20s_writeregdata(state, mb86a20s_init2); + if (rc < 0) + goto err; + + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (rc < 0) { + state->need_init = true; + dev_info(&state->i2c->dev, + "mb86a20s: Init failed. Will try again later\n"); + } else { + state->need_init = false; + dev_dbg(&state->i2c->dev, "Initialization succeeded.\n"); + } + return rc; +} + +static int mb86a20s_set_frontend(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc, if_freq; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (!c->isdbt_layer_enabled) + c->isdbt_layer_enabled = 7; + + if (c->isdbt_layer_enabled == 1) + state->bw = MB86A20S_1SEG; + else if (c->isdbt_partial_reception) + state->bw = MB86A20S_13SEG_PARTIAL; + else + state->bw = MB86A20S_13SEG; + + if (c->inversion == INVERSION_ON) + state->inversion = true; + else + state->inversion = false; + + if (!c->isdbt_sb_mode) { + state->subchannel = 0; + } else { + if (c->isdbt_sb_subchannel >= ARRAY_SIZE(mb86a20s_subchannel)) + c->isdbt_sb_subchannel = 0; + + state->subchannel = mb86a20s_subchannel[c->isdbt_sb_subchannel]; + } + + /* + * Gate should already be opened, but it doesn't hurt to + * double-check + */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + + /* + * Make it more reliable: if, for some reason, the initial + * device initialization doesn't happen, initialize it when + * a SBTVD parameters are adjusted. + * + * Unfortunately, due to a hard to track bug at tda829x/tda18271, + * the agc callback logic is not called during DVB attach time, + * causing mb86a20s to not be initialized with Kworld SBTVD. + * So, this hack is needed, in order to make Kworld SBTVD to work. + * + * It is also needed to change the IF after the initial init. + * + * HACK: Always init the frontend when set_frontend is called: + * it was noticed that, on some devices, it fails to lock on a + * different channel. So, it is better to reset everything, even + * wasting some time, than to loose channel lock. + */ + mb86a20s_initfe(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); + mb86a20s_reset_counters(fe); + mb86a20s_stats_not_ready(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return rc; +} + +static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc, status_nr; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* Get lock */ + status_nr = mb86a20s_read_status(fe, status); + if (status_nr < 7) { + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + } + if (status_nr < 0) { + dev_err(&state->i2c->dev, + "%s: Can't read frontend lock status\n", __func__); + rc = status_nr; + goto error; + } + + /* Get signal strength */ + rc = mb86a20s_read_signal_strength(fe); + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't reset VBER registers.\n", __func__); + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + + rc = 0; /* Status is OK */ + goto error; + } + + if (status_nr >= 7) { + /* Get TMCC info*/ + rc = mb86a20s_get_frontend(fe); + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't get FE TMCC data.\n", __func__); + rc = 0; /* Status is OK */ + goto error; + } + + /* Get statistics */ + rc = mb86a20s_get_stats(fe, status_nr); + if (rc < 0 && rc != -EBUSY) { + dev_err(&state->i2c->dev, + "%s: Can't get FE statistics.\n", __func__); + rc = 0; + goto error; + } + rc = 0; /* Don't return EBUSY to userspace */ + } + goto ok; + +error: + mb86a20s_stats_not_ready(fe); + +ok: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return rc; +} + +static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + + *strength = c->strength.stat[0].uvalue; + + return 0; +} + +static int mb86a20s_tune(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc = 0; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (re_tune) + rc = mb86a20s_set_frontend(fe); + + if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) + mb86a20s_read_status_and_stats(fe, status); + + return rc; +} + +static void mb86a20s_release(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + kfree(state); +} + +static enum dvbfe_algo mb86a20s_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static const struct dvb_frontend_ops mb86a20s_ops; + +struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, + struct i2c_adapter *i2c) +{ + struct mb86a20s_state *state; + u8 rev; + + dev_dbg(&i2c->dev, "%s called.\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &mb86a20s_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* Check if it is a mb86a20s frontend */ + rev = mb86a20s_readreg(state, 0); + if (rev != 0x13) { + kfree(state); + dev_dbg(&i2c->dev, + "Frontend revision %d is unknown - aborting.\n", + rev); + return NULL; + } + + dev_info(&i2c->dev, "Detected a Fujitsu mb86a20s frontend\n"); + return &state->frontend; +} +EXPORT_SYMBOL_GPL(mb86a20s_attach); + +static const struct dvb_frontend_ops mb86a20s_ops = { + .delsys = { SYS_ISDBT }, + /* Use dib8000 values per default */ + .info = { + .name = "Fujitsu mb86A20s", + .caps = FE_CAN_RECOVER | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + /* Actually, those values depend on the used tuner */ + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_stepsize_hz = 62500, + }, + + .release = mb86a20s_release, + + .init = mb86a20s_initfe, + .set_frontend = mb86a20s_set_frontend, + .read_status = mb86a20s_read_status_and_stats, + .read_signal_strength = mb86a20s_read_signal_strength_from_cache, + .tune = mb86a20s_tune, + .get_frontend_algo = mb86a20s_get_frontend_algo, +}; + +MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h new file mode 100644 index 0000000000..d20d22bf75 --- /dev/null +++ b/drivers/media/dvb-frontends/mb86a20s.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Fujitsu mb86a20s driver + * + * Copyright (C) 2010 Mauro Carvalho Chehab + */ + +#ifndef MB86A20S_H +#define MB86A20S_H + +#include <linux/dvb/frontend.h> + +/** + * struct mb86a20s_config - Define the per-device attributes of the frontend + * + * @fclk: Clock frequency. If zero, assumes the default + * (32.57142 Mhz) + * @demod_address: the demodulator's i2c address + * @is_serial: if true, TS is serial. Otherwise, TS is parallel + */ +struct mb86a20s_config { + u32 fclk; + u8 demod_address; + bool is_serial; +}; + +#if IS_REACHABLE(CONFIG_DVB_MB86A20S) +/** + * mb86a20s_attach - Attach a mb86a20s demod + * + * @config: pointer to &struct mb86a20s_config with demod configuration. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, + struct i2c_adapter *i2c); + +#else +static inline struct dvb_frontend *mb86a20s_attach( + const struct mb86a20s_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* MB86A20S */ diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c new file mode 100644 index 0000000000..7a58f53ab9 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -0,0 +1,812 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T. +// +// Copyright (c) 2018 Socionext Inc. + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/int_log.h> + +#include "mn88443x.h" + +/* ISDB-S registers */ +#define ATSIDU_S 0x2f +#define ATSIDL_S 0x30 +#define TSSET_S 0x31 +#define AGCREAD_S 0x5a +#define CPMON1_S 0x5e +#define CPMON1_S_FSYNC BIT(5) +#define CPMON1_S_ERRMON BIT(4) +#define CPMON1_S_SIGOFF BIT(3) +#define CPMON1_S_W2LOCK BIT(2) +#define CPMON1_S_W1LOCK BIT(1) +#define CPMON1_S_DW1LOCK BIT(0) +#define TRMON_S 0x60 +#define BERCNFLG_S 0x68 +#define BERCNFLG_S_BERVRDY BIT(5) +#define BERCNFLG_S_BERVCHK BIT(4) +#define BERCNFLG_S_BERDRDY BIT(3) +#define BERCNFLG_S_BERDCHK BIT(2) +#define CNRDXU_S 0x69 +#define CNRDXL_S 0x6a +#define CNRDYU_S 0x6b +#define CNRDYL_S 0x6c +#define BERVRDU_S 0x71 +#define BERVRDL_S 0x72 +#define DOSET1_S 0x73 + +/* Primary ISDB-T */ +#define PLLASET1 0x00 +#define PLLASET2 0x01 +#define PLLBSET1 0x02 +#define PLLBSET2 0x03 +#define PLLSET 0x04 +#define OUTCSET 0x08 +#define OUTCSET_CHDRV_8MA 0xff +#define OUTCSET_CHDRV_4MA 0x00 +#define PLDWSET 0x09 +#define PLDWSET_NORMAL 0x00 +#define PLDWSET_PULLDOWN 0xff +#define HIZSET1 0x0a +#define HIZSET2 0x0b + +/* Secondary ISDB-T (for MN884434 only) */ +#define RCVSET 0x00 +#define TSSET1_M 0x01 +#define TSSET2_M 0x02 +#define TSSET3_M 0x03 +#define INTACSET 0x08 +#define HIZSET3 0x0b + +/* ISDB-T registers */ +#define TSSET1 0x05 +#define TSSET1_TSASEL_MASK GENMASK(4, 3) +#define TSSET1_TSASEL_ISDBT (0x0 << 3) +#define TSSET1_TSASEL_ISDBS (0x1 << 3) +#define TSSET1_TSASEL_NONE (0x2 << 3) +#define TSSET1_TSBSEL_MASK GENMASK(2, 1) +#define TSSET1_TSBSEL_ISDBS (0x0 << 1) +#define TSSET1_TSBSEL_ISDBT (0x1 << 1) +#define TSSET1_TSBSEL_NONE (0x2 << 1) +#define TSSET2 0x06 +#define TSSET3 0x07 +#define TSSET3_INTASEL_MASK GENMASK(7, 6) +#define TSSET3_INTASEL_T (0x0 << 6) +#define TSSET3_INTASEL_S (0x1 << 6) +#define TSSET3_INTASEL_NONE (0x2 << 6) +#define TSSET3_INTBSEL_MASK GENMASK(5, 4) +#define TSSET3_INTBSEL_S (0x0 << 4) +#define TSSET3_INTBSEL_T (0x1 << 4) +#define TSSET3_INTBSEL_NONE (0x2 << 4) +#define OUTSET2 0x0d +#define PWDSET 0x0f +#define PWDSET_OFDMPD_MASK GENMASK(3, 2) +#define PWDSET_OFDMPD_DOWN BIT(3) +#define PWDSET_PSKPD_MASK GENMASK(1, 0) +#define PWDSET_PSKPD_DOWN BIT(1) +#define CLKSET1_T 0x11 +#define MDSET_T 0x13 +#define MDSET_T_MDAUTO_MASK GENMASK(7, 4) +#define MDSET_T_MDAUTO_AUTO (0xf << 4) +#define MDSET_T_MDAUTO_MANUAL (0x0 << 4) +#define MDSET_T_FFTS_MASK GENMASK(3, 2) +#define MDSET_T_FFTS_MODE1 (0x0 << 2) +#define MDSET_T_FFTS_MODE2 (0x1 << 2) +#define MDSET_T_FFTS_MODE3 (0x2 << 2) +#define MDSET_T_GI_MASK GENMASK(1, 0) +#define MDSET_T_GI_1_32 (0x0 << 0) +#define MDSET_T_GI_1_16 (0x1 << 0) +#define MDSET_T_GI_1_8 (0x2 << 0) +#define MDSET_T_GI_1_4 (0x3 << 0) +#define MDASET_T 0x14 +#define ADCSET1_T 0x20 +#define ADCSET1_T_REFSEL_MASK GENMASK(1, 0) +#define ADCSET1_T_REFSEL_2V (0x3 << 0) +#define ADCSET1_T_REFSEL_1_5V (0x2 << 0) +#define ADCSET1_T_REFSEL_1V (0x1 << 0) +#define NCOFREQU_T 0x24 +#define NCOFREQM_T 0x25 +#define NCOFREQL_T 0x26 +#define FADU_T 0x27 +#define FADM_T 0x28 +#define FADL_T 0x29 +#define AGCSET2_T 0x2c +#define AGCSET2_T_IFPOLINV_INC BIT(0) +#define AGCSET2_T_RFPOLINV_INC BIT(1) +#define AGCV3_T 0x3e +#define MDRD_T 0xa2 +#define MDRD_T_SEGID_MASK GENMASK(5, 4) +#define MDRD_T_SEGID_13 (0x0 << 4) +#define MDRD_T_SEGID_1 (0x1 << 4) +#define MDRD_T_SEGID_3 (0x2 << 4) +#define MDRD_T_FFTS_MASK GENMASK(3, 2) +#define MDRD_T_FFTS_MODE1 (0x0 << 2) +#define MDRD_T_FFTS_MODE2 (0x1 << 2) +#define MDRD_T_FFTS_MODE3 (0x2 << 2) +#define MDRD_T_GI_MASK GENMASK(1, 0) +#define MDRD_T_GI_1_32 (0x0 << 0) +#define MDRD_T_GI_1_16 (0x1 << 0) +#define MDRD_T_GI_1_8 (0x2 << 0) +#define MDRD_T_GI_1_4 (0x3 << 0) +#define SSEQRD_T 0xa3 +#define SSEQRD_T_SSEQSTRD_MASK GENMASK(3, 0) +#define SSEQRD_T_SSEQSTRD_RESET (0x0 << 0) +#define SSEQRD_T_SSEQSTRD_TUNING (0x1 << 0) +#define SSEQRD_T_SSEQSTRD_AGC (0x2 << 0) +#define SSEQRD_T_SSEQSTRD_SEARCH (0x3 << 0) +#define SSEQRD_T_SSEQSTRD_CLOCK_SYNC (0x4 << 0) +#define SSEQRD_T_SSEQSTRD_FREQ_SYNC (0x8 << 0) +#define SSEQRD_T_SSEQSTRD_FRAME_SYNC (0x9 << 0) +#define SSEQRD_T_SSEQSTRD_SYNC (0xa << 0) +#define SSEQRD_T_SSEQSTRD_LOCK (0xb << 0) +#define AGCRDU_T 0xa8 +#define AGCRDL_T 0xa9 +#define CNRDU_T 0xbe +#define CNRDL_T 0xbf +#define BERFLG_T 0xc0 +#define BERFLG_T_BERDRDY BIT(7) +#define BERFLG_T_BERDCHK BIT(6) +#define BERFLG_T_BERVRDYA BIT(5) +#define BERFLG_T_BERVCHKA BIT(4) +#define BERFLG_T_BERVRDYB BIT(3) +#define BERFLG_T_BERVCHKB BIT(2) +#define BERFLG_T_BERVRDYC BIT(1) +#define BERFLG_T_BERVCHKC BIT(0) +#define BERRDU_T 0xc1 +#define BERRDM_T 0xc2 +#define BERRDL_T 0xc3 +#define BERLENRDU_T 0xc4 +#define BERLENRDL_T 0xc5 +#define ERRFLG_T 0xc6 +#define ERRFLG_T_BERDOVF BIT(7) +#define ERRFLG_T_BERVOVFA BIT(6) +#define ERRFLG_T_BERVOVFB BIT(5) +#define ERRFLG_T_BERVOVFC BIT(4) +#define ERRFLG_T_NERRFA BIT(3) +#define ERRFLG_T_NERRFB BIT(2) +#define ERRFLG_T_NERRFC BIT(1) +#define ERRFLG_T_NERRF BIT(0) +#define DOSET1_T 0xcf + +#define CLK_LOW 4000000 +#define CLK_DIRECT 20200000 +#define CLK_MAX 25410000 + +#define S_T_FREQ 8126984 /* 512 / 63 MHz */ + +struct mn88443x_spec { + bool primary; +}; + +struct mn88443x_priv { + const struct mn88443x_spec *spec; + + struct dvb_frontend fe; + struct clk *mclk; + struct gpio_desc *reset_gpio; + u32 clk_freq; + u32 if_freq; + + /* Common */ + bool use_clkbuf; + + /* ISDB-S */ + struct i2c_client *client_s; + struct regmap *regmap_s; + + /* ISDB-T */ + struct i2c_client *client_t; + struct regmap *regmap_t; +}; + +static int mn88443x_cmn_power_on(struct mn88443x_priv *chip) +{ + struct device *dev = &chip->client_s->dev; + struct regmap *r_t = chip->regmap_t; + int ret; + + ret = clk_prepare_enable(chip->mclk); + if (ret) { + dev_err(dev, "Failed to prepare and enable mclk: %d\n", + ret); + return ret; + } + + gpiod_set_value_cansleep(chip->reset_gpio, 1); + usleep_range(100, 1000); + gpiod_set_value_cansleep(chip->reset_gpio, 0); + + if (chip->spec->primary) { + regmap_write(r_t, OUTCSET, OUTCSET_CHDRV_8MA); + regmap_write(r_t, PLDWSET, PLDWSET_NORMAL); + regmap_write(r_t, HIZSET1, 0x80); + regmap_write(r_t, HIZSET2, 0xe0); + } else { + regmap_write(r_t, HIZSET3, 0x8f); + } + + return 0; +} + +static void mn88443x_cmn_power_off(struct mn88443x_priv *chip) +{ + gpiod_set_value_cansleep(chip->reset_gpio, 1); + + clk_disable_unprepare(chip->mclk); +} + +static void mn88443x_s_sleep(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, + PWDSET_PSKPD_DOWN); +} + +static void mn88443x_s_wake(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, 0); +} + +static void mn88443x_s_tune(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c) +{ + struct regmap *r_s = chip->regmap_s; + + regmap_write(r_s, ATSIDU_S, c->stream_id >> 8); + regmap_write(r_s, ATSIDL_S, c->stream_id); + regmap_write(r_s, TSSET_S, 0); +} + +static int mn88443x_s_read_status(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c, + enum fe_status *status) +{ + struct regmap *r_s = chip->regmap_s; + u32 cpmon, tmpu, tmpl, flg; + u64 tmp; + + /* Sync detection */ + regmap_read(r_s, CPMON1_S, &cpmon); + + *status = 0; + if (cpmon & CPMON1_S_FSYNC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if (cpmon & CPMON1_S_W2LOCK) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + /* Signal strength */ + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SIGNAL) { + u32 agc; + + regmap_read(r_s, AGCREAD_S, &tmpu); + agc = tmpu << 8; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = agc; + } + + /* C/N rate */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_VITERBI) { + u32 cnr = 0, x, y, d; + u64 d_3 = 0; + + regmap_read(r_s, CNRDXU_S, &tmpu); + regmap_read(r_s, CNRDXL_S, &tmpl); + x = (tmpu << 8) | tmpl; + regmap_read(r_s, CNRDYU_S, &tmpu); + regmap_read(r_s, CNRDYL_S, &tmpl); + y = (tmpu << 8) | tmpl; + + /* CNR[dB]: 10 * log10(D) - 30.74 / D^3 - 3 */ + /* D = x^2 / (2^15 * y - x^2) */ + d = (y << 15) - x * x; + if (d > 0) { + /* (2^4 * D)^3 = 2^12 * D^3 */ + /* 3.074 * 2^(12 + 24) = 211243671486 */ + d_3 = div_u64(16 * x * x, d); + d_3 = d_3 * d_3 * d_3; + if (d_3) + d_3 = div_u64(211243671486ULL, d_3); + } + + if (d_3) { + /* 0.3 * 2^24 = 5033164 */ + tmp = (s64)2 * intlog10(x) - intlog10(abs(d)) - d_3 + - 5033164; + cnr = div_u64(tmp * 10000, 1 << 24); + } + + if (cnr) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].uvalue = cnr; + } + } + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + regmap_read(r_s, BERCNFLG_S, &flg); + + if ((*status & FE_HAS_VITERBI) && (flg & BERCNFLG_S_BERVRDY)) { + u32 bit_err, bit_cnt; + + regmap_read(r_s, BERVRDU_S, &tmpu); + regmap_read(r_s, BERVRDL_S, &tmpl); + bit_err = (tmpu << 8) | tmpl; + bit_cnt = (1 << 13) * 204; + + if (bit_cnt) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = bit_cnt; + } + } + + return 0; +} + +static void mn88443x_t_sleep(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, + PWDSET_OFDMPD_DOWN); +} + +static void mn88443x_t_wake(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, 0); +} + +static bool mn88443x_t_is_valid_clk(u32 adckt, u32 if_freq) +{ + if (if_freq == DIRECT_IF_57MHZ) { + if (adckt >= CLK_DIRECT && adckt <= 21000000) + return true; + if (adckt >= 25300000 && adckt <= CLK_MAX) + return true; + } else if (if_freq == DIRECT_IF_44MHZ) { + if (adckt >= 25000000 && adckt <= CLK_MAX) + return true; + } else if (if_freq >= LOW_IF_4MHZ && if_freq < DIRECT_IF_44MHZ) { + if (adckt >= CLK_DIRECT && adckt <= CLK_MAX) + return true; + } + + return false; +} + +static int mn88443x_t_set_freq(struct mn88443x_priv *chip) +{ + struct device *dev = &chip->client_s->dev; + struct regmap *r_t = chip->regmap_t; + s64 adckt, nco, ad_t; + u32 m, v; + + /* Clock buffer (but not supported) or XTAL */ + if (chip->clk_freq >= CLK_LOW && chip->clk_freq < CLK_DIRECT) { + chip->use_clkbuf = true; + regmap_write(r_t, CLKSET1_T, 0x07); + + adckt = 0; + } else { + chip->use_clkbuf = false; + regmap_write(r_t, CLKSET1_T, 0x00); + + adckt = chip->clk_freq; + } + if (!mn88443x_t_is_valid_clk(adckt, chip->if_freq)) { + dev_err(dev, "Invalid clock, CLK:%d, ADCKT:%lld, IF:%d\n", + chip->clk_freq, adckt, chip->if_freq); + return -EINVAL; + } + + /* Direct IF or Low IF */ + if (chip->if_freq == DIRECT_IF_57MHZ || + chip->if_freq == DIRECT_IF_44MHZ) + nco = adckt * 2 - chip->if_freq; + else + nco = -((s64)chip->if_freq); + nco = div_s64(nco << 24, adckt); + ad_t = div_s64(adckt << 22, S_T_FREQ); + + regmap_write(r_t, NCOFREQU_T, nco >> 16); + regmap_write(r_t, NCOFREQM_T, nco >> 8); + regmap_write(r_t, NCOFREQL_T, nco); + regmap_write(r_t, FADU_T, ad_t >> 16); + regmap_write(r_t, FADM_T, ad_t >> 8); + regmap_write(r_t, FADL_T, ad_t); + + /* Level of IF */ + m = ADCSET1_T_REFSEL_MASK; + v = ADCSET1_T_REFSEL_1_5V; + regmap_update_bits(r_t, ADCSET1_T, m, v); + + /* Polarity of AGC */ + v = AGCSET2_T_IFPOLINV_INC | AGCSET2_T_RFPOLINV_INC; + regmap_update_bits(r_t, AGCSET2_T, v, v); + + /* Lower output level of AGC */ + regmap_write(r_t, AGCV3_T, 0x00); + + regmap_write(r_t, MDSET_T, 0xfa); + + return 0; +} + +static void mn88443x_t_tune(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c) +{ + struct regmap *r_t = chip->regmap_t; + u32 m, v; + + m = MDSET_T_MDAUTO_MASK | MDSET_T_FFTS_MASK | MDSET_T_GI_MASK; + v = MDSET_T_MDAUTO_AUTO | MDSET_T_FFTS_MODE3 | MDSET_T_GI_1_8; + regmap_update_bits(r_t, MDSET_T, m, v); + + regmap_write(r_t, MDASET_T, 0); +} + +static int mn88443x_t_read_status(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c, + enum fe_status *status) +{ + struct regmap *r_t = chip->regmap_t; + u32 seqrd, st, flg, tmpu, tmpm, tmpl; + u64 tmp; + + /* Sync detection */ + regmap_read(r_t, SSEQRD_T, &seqrd); + st = seqrd & SSEQRD_T_SSEQSTRD_MASK; + + *status = 0; + if (st >= SSEQRD_T_SSEQSTRD_SYNC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if (st >= SSEQRD_T_SSEQSTRD_FRAME_SYNC) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + /* Signal strength */ + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SIGNAL) { + u32 agc; + + regmap_read(r_t, AGCRDU_T, &tmpu); + regmap_read(r_t, AGCRDL_T, &tmpl); + agc = (tmpu << 8) | tmpl; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = agc; + } + + /* C/N rate */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_VITERBI) { + u32 cnr; + + regmap_read(r_t, CNRDU_T, &tmpu); + regmap_read(r_t, CNRDL_T, &tmpl); + + if (tmpu || tmpl) { + /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ + /* intlog10(65536) = 80807124, 0.2 * 2^24 = 3355443 */ + tmp = (u64)80807124 - intlog10((tmpu << 8) | tmpl) + + 3355443; + cnr = div_u64(tmp * 10000, 1 << 24); + } else { + cnr = 0; + } + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].uvalue = cnr; + } + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + regmap_read(r_t, BERFLG_T, &flg); + + if ((*status & FE_HAS_VITERBI) && (flg & BERFLG_T_BERVRDYA)) { + u32 bit_err, bit_cnt; + + regmap_read(r_t, BERRDU_T, &tmpu); + regmap_read(r_t, BERRDM_T, &tmpm); + regmap_read(r_t, BERRDL_T, &tmpl); + bit_err = (tmpu << 16) | (tmpm << 8) | tmpl; + + regmap_read(r_t, BERLENRDU_T, &tmpu); + regmap_read(r_t, BERLENRDL_T, &tmpl); + bit_cnt = ((tmpu << 8) | tmpl) * 203 * 8; + + if (bit_cnt) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = bit_cnt; + } + } + + return 0; +} + +static int mn88443x_sleep(struct dvb_frontend *fe) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + + mn88443x_s_sleep(chip); + mn88443x_t_sleep(chip); + + return 0; +} + +static int mn88443x_set_frontend(struct dvb_frontend *fe) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct regmap *r_s = chip->regmap_s; + struct regmap *r_t = chip->regmap_t; + u8 tssel = 0, intsel = 0; + + if (c->delivery_system == SYS_ISDBS) { + mn88443x_s_wake(chip); + mn88443x_t_sleep(chip); + + tssel = TSSET1_TSASEL_ISDBS; + intsel = TSSET3_INTASEL_S; + } else if (c->delivery_system == SYS_ISDBT) { + mn88443x_s_sleep(chip); + mn88443x_t_wake(chip); + + mn88443x_t_set_freq(chip); + + tssel = TSSET1_TSASEL_ISDBT; + intsel = TSSET3_INTASEL_T; + } + + regmap_update_bits(r_t, TSSET1, + TSSET1_TSASEL_MASK | TSSET1_TSBSEL_MASK, + tssel | TSSET1_TSBSEL_NONE); + regmap_write(r_t, TSSET2, 0); + regmap_update_bits(r_t, TSSET3, + TSSET3_INTASEL_MASK | TSSET3_INTBSEL_MASK, + intsel | TSSET3_INTBSEL_NONE); + + regmap_write(r_t, DOSET1_T, 0x95); + regmap_write(r_s, DOSET1_S, 0x80); + + if (c->delivery_system == SYS_ISDBS) + mn88443x_s_tune(chip, c); + else if (c->delivery_system == SYS_ISDBT) + mn88443x_t_tune(chip, c); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int mn88443x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + s->min_delay_ms = 850; + + if (c->delivery_system == SYS_ISDBS) { + s->max_drift = 30000 * 2 + 1; + s->step_size = 30000; + } else if (c->delivery_system == SYS_ISDBT) { + s->max_drift = 142857 * 2 + 1; + s->step_size = 142857 * 2; + } + + return 0; +} + +static int mn88443x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->delivery_system == SYS_ISDBS) + return mn88443x_s_read_status(chip, c, status); + + if (c->delivery_system == SYS_ISDBT) + return mn88443x_t_read_status(chip, c, status); + + return -EINVAL; +} + +static const struct dvb_frontend_ops mn88443x_ops = { + .delsys = { SYS_ISDBS, SYS_ISDBT }, + .info = { + .name = "Socionext MN88443x", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 2071 * MHz, + .symbol_rate_min = 28860000, + .symbol_rate_max = 28860000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .sleep = mn88443x_sleep, + .set_frontend = mn88443x_set_frontend, + .get_tune_settings = mn88443x_get_tune_settings, + .read_status = mn88443x_read_status, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static int mn88443x_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct mn88443x_config *conf = client->dev.platform_data; + struct mn88443x_priv *chip; + struct device *dev = &client->dev; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + if (dev->of_node) + chip->spec = of_device_get_match_data(dev); + else + chip->spec = (struct mn88443x_spec *)id->driver_data; + if (!chip->spec) + return -EINVAL; + + chip->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(chip->mclk) && !conf) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(chip->mclk)); + return PTR_ERR(chip->mclk); + } + + ret = of_property_read_u32(dev->of_node, "if-frequency", + &chip->if_freq); + if (ret && !conf) { + dev_err(dev, "Failed to load IF frequency: %d.\n", ret); + return ret; + } + + chip->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(chip->reset_gpio)) { + dev_err(dev, "Failed to request reset_gpio: %ld\n", + PTR_ERR(chip->reset_gpio)); + return PTR_ERR(chip->reset_gpio); + } + + if (conf) { + chip->mclk = conf->mclk; + chip->if_freq = conf->if_freq; + chip->reset_gpio = conf->reset_gpio; + + *conf->fe = &chip->fe; + } + + chip->client_s = client; + chip->regmap_s = devm_regmap_init_i2c(chip->client_s, ®map_config); + if (IS_ERR(chip->regmap_s)) + return PTR_ERR(chip->regmap_s); + + /* + * Chip has two I2C addresses for each satellite/terrestrial system. + * ISDB-T uses address ISDB-S + 4, so we register a dummy client. + */ + chip->client_t = i2c_new_dummy_device(client->adapter, client->addr + 4); + if (IS_ERR(chip->client_t)) + return PTR_ERR(chip->client_t); + + chip->regmap_t = devm_regmap_init_i2c(chip->client_t, ®map_config); + if (IS_ERR(chip->regmap_t)) { + ret = PTR_ERR(chip->regmap_t); + goto err_i2c_t; + } + + chip->clk_freq = clk_get_rate(chip->mclk); + + memcpy(&chip->fe.ops, &mn88443x_ops, sizeof(mn88443x_ops)); + chip->fe.demodulator_priv = chip; + i2c_set_clientdata(client, chip); + + ret = mn88443x_cmn_power_on(chip); + if (ret) + goto err_i2c_t; + + mn88443x_s_sleep(chip); + mn88443x_t_sleep(chip); + + return 0; + +err_i2c_t: + i2c_unregister_device(chip->client_t); + + return ret; +} + +static void mn88443x_remove(struct i2c_client *client) +{ + struct mn88443x_priv *chip = i2c_get_clientdata(client); + + mn88443x_cmn_power_off(chip); + + i2c_unregister_device(chip->client_t); +} + +static const struct mn88443x_spec mn88443x_spec_pri = { + .primary = true, +}; + +static const struct mn88443x_spec mn88443x_spec_sec = { + .primary = false, +}; + +static const struct of_device_id mn88443x_of_match[] = { + { .compatible = "socionext,mn884433", .data = &mn88443x_spec_pri, }, + { .compatible = "socionext,mn884434-0", .data = &mn88443x_spec_pri, }, + { .compatible = "socionext,mn884434-1", .data = &mn88443x_spec_sec, }, + {} +}; +MODULE_DEVICE_TABLE(of, mn88443x_of_match); + +static const struct i2c_device_id mn88443x_i2c_id[] = { + { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri }, + { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri }, + { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); + +static struct i2c_driver mn88443x_driver = { + .driver = { + .name = "mn88443x", + .of_match_table = mn88443x_of_match, + }, + .probe = mn88443x_probe, + .remove = mn88443x_remove, + .id_table = mn88443x_i2c_id, +}; + +module_i2c_driver(mn88443x_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); +MODULE_DESCRIPTION("Socionext MN88443x series demodulator driver."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/mn88443x.h b/drivers/media/dvb-frontends/mn88443x.h new file mode 100644 index 0000000000..b19aaf6a1e --- /dev/null +++ b/drivers/media/dvb-frontends/mn88443x.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T. + * + * Copyright (c) 2018 Socionext Inc. + */ + +#ifndef MN88443X_H +#define MN88443X_H + +#include <media/dvb_frontend.h> + +/* ISDB-T IF frequency */ +#define DIRECT_IF_57MHZ 57000000 +#define DIRECT_IF_44MHZ 44000000 +#define LOW_IF_4MHZ 4000000 + +struct mn88443x_config { + struct clk *mclk; + u32 if_freq; + struct gpio_desc *reset_gpio; + + /* Everything after that is returned by the driver. */ + struct dvb_frontend **fe; +}; + +#endif /* MN88443X_H */ diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c new file mode 100644 index 0000000000..73d1e52de5 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88472.c @@ -0,0 +1,731 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Panasonic MN88472 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + */ + +#include "mn88472_priv.h" + +static int mn88472_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + return 0; +} + +static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, stmp; + unsigned int utmp, utmp1, utmp2; + u8 buf[5]; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x7f, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x92, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((utmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x84, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x08) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + default: + ret = -EINVAL; + goto err; + } + + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + for (i = 0; i < 2; i++) { + ret = regmap_bulk_read(dev->regmap[2], 0x8e + i, + &buf[i], 1); + if (ret) + goto err; + } + + utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2; + dev_dbg(&client->dev, "strength=%u\n", utmp1); + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = utmp1; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) { + /* DVB-T CNR */ + ret = regmap_bulk_read(dev->regmap[0], 0x9c, buf, 2); + if (ret) + goto err; + + utmp = buf[0] << 8 | buf[1] << 0; + if (utmp) { + /* CNR[dB]: 10 * log10(65536 / value) + 2 */ + /* log10(65536) = 80807124, 0.2 = 3355443 */ + stmp = ((u64)80807124 - intlog10(utmp) + 3355443) + * 10000 >> 24; + + dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp); + } else { + stmp = 0; + } + + c->cnr.stat[0].svalue = stmp; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else if (*status & FE_HAS_VITERBI && + c->delivery_system == SYS_DVBT2) { + /* DVB-T2 CNR */ + for (i = 0; i < 3; i++) { + ret = regmap_bulk_read(dev->regmap[2], 0xbc + i, + &buf[i], 1); + if (ret) + goto err; + } + + utmp = buf[1] << 8 | buf[2] << 0; + utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */ + if (utmp) { + if (utmp1) { + /* CNR[dB]: 10 * log10(16384 / value) - 6 */ + /* log10(16384) = 70706234, 0.6 = 10066330 */ + stmp = ((u64)70706234 - intlog10(utmp) + - 10066330) * 10000 >> 24; + dev_dbg(&client->dev, "cnr=%d value=%u MISO\n", + stmp, utmp); + } else { + /* CNR[dB]: 10 * log10(65536 / value) + 2 */ + /* log10(65536) = 80807124, 0.2 = 3355443 */ + stmp = ((u64)80807124 - intlog10(utmp) + + 3355443) * 10000 >> 24; + + dev_dbg(&client->dev, "cnr=%d value=%u SISO\n", + stmp, utmp); + } + } else { + stmp = 0; + } + + c->cnr.stat[0].svalue = stmp; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else if (*status & FE_HAS_VITERBI && + c->delivery_system == SYS_DVBC_ANNEX_A) { + /* DVB-C CNR */ + ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4); + if (ret) + goto err; + + utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */ + utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */ + if (utmp1 && utmp2) { + /* CNR[dB]: 10 * log10(8 * (signal / noise)) */ + /* log10(8) = 15151336 */ + stmp = ((u64)15151336 + intlog10(utmp1) + - intlog10(utmp2)) * 10000 >> 24; + + dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n", + stmp, utmp1, utmp2); + } else { + stmp = 0; + } + + c->cnr.stat[0].svalue = stmp; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* PER */ + if (*status & FE_HAS_SYNC) { + ret = regmap_bulk_read(dev->regmap[0], 0xe1, buf, 4); + if (ret) + goto err; + + utmp1 = buf[0] << 8 | buf[1] << 0; + utmp2 = buf[2] << 8 | buf[3] << 0; + dev_dbg(&client->dev, "block_error=%u block_count=%u\n", + utmp1, utmp2); + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += utmp1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += utmp2; + } else { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_set_frontend(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; + u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; + u8 reg_bank0_d6_val; + + dev_dbg(&client->dev, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + delivery_system_val = 0x02; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x1f; + reg_bank0_d4_val = 0x0a; + reg_bank0_d6_val = 0x48; + break; + case SYS_DVBT2: + delivery_system_val = 0x03; + reg_bank0_b4_val = 0xf6; + reg_bank0_cd_val = 0x01; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x46; + break; + case SYS_DVBC_ANNEX_A: + delivery_system_val = 0x04; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x17; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x48; + break; + default: + ret = -EINVAL; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 5000000: + bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; + bandwidth_val = 0x03; + break; + case 6000000: + bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; + bandwidth_val = 0x02; + break; + case 7000000: + bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; + bandwidth_val = 0x01; + break; + case 8000000: + bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; + bandwidth_val = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + bandwidth_vals_ptr = NULL; + bandwidth_val = 0x00; + break; + default: + break; + } + + /* Program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto err; + + dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); + } else { + ret = -EINVAL; + goto err; + } + + ret = regmap_write(dev->regmap[2], 0x00, 0x66); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x01, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x02, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); + if (ret) + goto err; + + /* IF */ + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + for (i = 0; i < 3; i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); + if (ret) + goto err; + } + + /* Bandwidth */ + if (bandwidth_vals_ptr) { + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + bandwidth_vals_ptr[i]); + if (ret) + goto err; + } + } + + ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); + if (ret) + goto err; + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_write(dev->regmap[0], 0x07, 0x26); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x00, 0xba); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x01, 0x13); + if (ret) + goto err; + break; + case SYS_DVBT2: + ret = regmap_write(dev->regmap[2], 0x2b, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x4f, 0x05); + if (ret) + goto err; + ret = regmap_write(dev->regmap[1], 0xf6, 0x05); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x32, + (c->stream_id == NO_STREAM_ID_FILTER) ? 0 : + c->stream_id ); + if (ret) + goto err; + break; + case SYS_DVBC_ANNEX_A: + break; + default: + break; + } + + /* Reset FSM */ + ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_init(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + int ret, len, rem; + unsigned int utmp; + const struct firmware *firmware; + const char *name = MN88472_FIRMWARE; + + dev_dbg(&client->dev, "\n"); + + /* Power up */ + ret = regmap_write(dev->regmap[2], 0x05, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0c, 0x00); + if (ret) + goto err; + + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &utmp); + if (ret) + goto err; + if (!(utmp & 0x01)) + goto warm; + + ret = request_firmware(&firmware, name, &client->dev); + if (ret) { + dev_err(&client->dev, "firmware file '%s' not found\n", name); + goto err; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); + + ret = regmap_write(dev->regmap[0], 0xf5, 0x03); + if (ret) + goto err_release_firmware; + + for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { + len = min(dev->i2c_write_max - 1, rem); + ret = regmap_bulk_write(dev->regmap[0], 0xf6, + &firmware->data[firmware->size - rem], + len); + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; + } + } + + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &utmp); + if (ret) + goto err_release_firmware; + if (utmp & 0x10) { + ret = -EINVAL; + dev_err(&client->dev, "firmware did not run\n"); + goto err_release_firmware; + } + + ret = regmap_write(dev->regmap[0], 0xf5, 0x00); + if (ret) + goto err_release_firmware; + + release_firmware(firmware); +warm: + /* TS config */ + switch (dev->ts_mode) { + case SERIAL_TS_MODE: + utmp = 0x1d; + break; + case PARALLEL_TS_MODE: + utmp = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[2], 0x08, utmp); + if (ret) + goto err; + + switch (dev->ts_clk) { + case VARIABLE_TS_CLOCK: + utmp = 0xe3; + break; + case FIXED_TS_CLOCK: + utmp = 0xe1; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[0], 0xd9, utmp); + if (ret) + goto err; + + dev->active = true; + + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_sleep(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "\n"); + + /* Power down */ + ret = regmap_write(dev->regmap[2], 0x0c, 0x30); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0b, 0x30); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops mn88472_ops = { + .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, + .info = { + .name = "Panasonic MN88472", + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM + }, + + .get_tune_settings = mn88472_get_tune_settings, + + .init = mn88472_init, + .sleep = mn88472_sleep, + + .set_frontend = mn88472_set_frontend, + + .read_status = mn88472_read_status, +}; + +static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static int mn88472_probe(struct i2c_client *client) +{ + struct mn88472_config *pdata = client->dev.platform_data; + struct mn88472_dev *dev; + struct dtv_frontend_properties *c; + int ret; + unsigned int utmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + dev_dbg(&client->dev, "\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; + dev->clk = pdata->xtal; + dev->ts_mode = pdata->ts_mode; + dev->ts_clk = pdata->ts_clock; + dev->client[0] = client; + dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); + if (IS_ERR(dev->regmap[0])) { + ret = PTR_ERR(dev->regmap[0]); + goto err_kfree; + } + + /* + * Chip has three I2C addresses for different register banks. Used + * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. + */ + dev->client[1] = i2c_new_dummy_device(client->adapter, 0x1a); + if (IS_ERR(dev->client[1])) { + ret = PTR_ERR(dev->client[1]); + dev_err(&client->dev, "I2C registration failed\n"); + goto err_regmap_0_regmap_exit; + } + dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); + if (IS_ERR(dev->regmap[1])) { + ret = PTR_ERR(dev->regmap[1]); + goto err_client_1_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[1], dev); + + dev->client[2] = i2c_new_dummy_device(client->adapter, 0x1c); + if (IS_ERR(dev->client[2])) { + ret = PTR_ERR(dev->client[2]); + dev_err(&client->dev, "2nd I2C registration failed\n"); + goto err_regmap_1_regmap_exit; + } + dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); + if (IS_ERR(dev->regmap[2])) { + ret = PTR_ERR(dev->regmap[2]); + goto err_client_2_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[2], dev); + + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[2], 0xff, &utmp); + if (ret) + goto err_regmap_2_regmap_exit; + + dev_dbg(&client->dev, "chip id=%02x\n", utmp); + + if (utmp != 0x02) { + ret = -ENODEV; + goto err_regmap_2_regmap_exit; + } + + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_regmap_2_regmap_exit; + + /* Create dvb frontend */ + memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = client; + *pdata->fe = &dev->fe; + i2c_set_clientdata(client, dev); + + /* Init stats to indicate which stats are supported */ + c = &dev->fe.dtv_property_cache; + c->strength.len = 1; + c->cnr.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; + + /* Setup callbacks */ + pdata->get_dvb_frontend = mn88472_get_dvb_frontend; + + dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); + + return 0; +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); +err_client_2_i2c_unregister_device: + i2c_unregister_device(dev->client[2]); +err_regmap_1_regmap_exit: + regmap_exit(dev->regmap[1]); +err_client_1_i2c_unregister_device: + i2c_unregister_device(dev->client[1]); +err_regmap_0_regmap_exit: + regmap_exit(dev->regmap[0]); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void mn88472_remove(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + regmap_exit(dev->regmap[2]); + i2c_unregister_device(dev->client[2]); + + regmap_exit(dev->regmap[1]); + i2c_unregister_device(dev->client[1]); + + regmap_exit(dev->regmap[0]); + + kfree(dev); +} + +static const struct i2c_device_id mn88472_id_table[] = { + {"mn88472", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88472_id_table); + +static struct i2c_driver mn88472_driver = { + .driver = { + .name = "mn88472", + .suppress_bind_attrs = true, + }, + .probe = mn88472_probe, + .remove = mn88472_remove, + .id_table = mn88472_id_table, +}; + +module_i2c_driver(mn88472_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Panasonic MN88472 DVB-T/T2/C demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(MN88472_FIRMWARE); diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h new file mode 100644 index 0000000000..e4f2177478 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88472.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Panasonic MN88472 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + */ + +#ifndef MN88472_H +#define MN88472_H + +#include <linux/dvb/frontend.h> + +/* Define old names for backward compatibility */ +#define VARIABLE_TS_CLOCK MN88472_TS_CLK_VARIABLE +#define FIXED_TS_CLOCK MN88472_TS_CLK_FIXED +#define SERIAL_TS_MODE MN88472_TS_MODE_SERIAL +#define PARALLEL_TS_MODE MN88472_TS_MODE_PARALLEL + +/** + * struct mn88472_config - Platform data for the mn88472 driver + * @xtal: Clock frequency. + * @ts_mode: TS mode. + * @ts_clock: TS clock config. + * @i2c_wr_max: Max number of bytes driver writes to I2C at once. + * @fe: pointer to a frontend pointer + * @get_dvb_frontend: Get DVB frontend callback. + */ +struct mn88472_config { + unsigned int xtal; + +#define MN88472_TS_MODE_SERIAL 0 +#define MN88472_TS_MODE_PARALLEL 1 + int ts_mode; + +#define MN88472_TS_CLK_FIXED 0 +#define MN88472_TS_CLK_VARIABLE 1 + int ts_clock; + + u16 i2c_wr_max; + + /* Everything after that is returned by the driver. */ + + /* + * DVB frontend. + */ + struct dvb_frontend **fe; + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); +}; + +#endif diff --git a/drivers/media/dvb-frontends/mn88472_priv.h b/drivers/media/dvb-frontends/mn88472_priv.h new file mode 100644 index 0000000000..41f14bd67b --- /dev/null +++ b/drivers/media/dvb-frontends/mn88472_priv.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Panasonic MN88472 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + */ + +#ifndef MN88472_PRIV_H +#define MN88472_PRIV_H + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "mn88472.h" +#include <linux/firmware.h> +#include <linux/regmap.h> + +#define MN88472_FIRMWARE "dvb-demod-mn88472-02.fw" + +struct mn88472_dev { + struct i2c_client *client[3]; + struct regmap *regmap[3]; + struct dvb_frontend fe; + u16 i2c_write_max; + unsigned int clk; + unsigned int active:1; + unsigned int ts_mode:1; + unsigned int ts_clk:1; +}; + +#endif diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c new file mode 100644 index 0000000000..eb50591c0e --- /dev/null +++ b/drivers/media/dvb-frontends/mn88473.c @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Panasonic MN88473 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + */ + +#include "mn88473_priv.h" + +static int mn88473_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + return 0; +} + +static int mn88473_set_frontend(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + unsigned int uitmp; + u32 if_frequency; + u8 delivery_system_val, if_val[3], *conf_val_ptr; + u8 reg_bank2_2d_val, reg_bank0_d2_val; + + dev_dbg(&client->dev, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + delivery_system_val = 0x02; + reg_bank2_2d_val = 0x23; + reg_bank0_d2_val = 0x2a; + break; + case SYS_DVBT2: + delivery_system_val = 0x03; + reg_bank2_2d_val = 0x3b; + reg_bank0_d2_val = 0x29; + break; + case SYS_DVBC_ANNEX_A: + delivery_system_val = 0x04; + reg_bank2_2d_val = 0x3b; + reg_bank0_d2_val = 0x29; + break; + default: + ret = -EINVAL; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 6000000: + conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29"; + break; + case 7000000: + conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a"; + break; + case 8000000: + conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec"; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d"; + break; + default: + break; + } + + /* Program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto err; + + dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto err; + } + + /* Calculate IF registers */ + uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk); + if_val[0] = (uitmp >> 16) & 0xff; + if_val[1] = (uitmp >> 8) & 0xff; + if_val[2] = (uitmp >> 0) & 0xff; + + ret = regmap_write(dev->regmap[2], 0x05, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0xfb, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0xef, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0xf9, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x00, 0x18); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x01, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x02, 0x21); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); + if (ret) + goto err; + + for (i = 0; i < sizeof(if_val); i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); + if (ret) + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + conf_val_ptr[i]); + if (ret) + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6); + if (ret) + goto err; + break; + default: + break; + } + + ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x2e, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x56, 0x0d); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap[0], 0x01, + "\xba\x13\x80\xba\x91\xdd\xe7\x28", 8); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x0a, 0x1a); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x13, 0x1f); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x19, 0x03); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x1d, 0xb0); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x2a, 0x72); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x2d, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x3c, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x3f, 0xf8); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, 0x55); + if (ret) + goto err; + ret = regmap_write(dev->regmap[1], 0xbe, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xb2, 0x37); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd7, 0x04); + if (ret) + goto err; + + /* PLP */ + if (c->delivery_system == SYS_DVBT2) { + ret = regmap_write(dev->regmap[2], 0x36, + (c->stream_id == NO_STREAM_ID_FILTER) ? 0 : + c->stream_id ); + if (ret) + goto err; + } + + /* Reset FSM */ + ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, stmp; + unsigned int utmp, utmp1, utmp2; + u8 buf[5]; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + /* Lock detection */ + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x62, &utmp); + if (ret) + goto err; + + if (!(utmp & 0xa0)) { + if ((utmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x03) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + } else { + *status = 0; + } + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x8b, &utmp); + if (ret) + goto err; + + if (!(utmp & 0x40)) { + if ((utmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((utmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + } else { + *status = 0; + } + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x85, &utmp); + if (ret) + goto err; + + if (!(utmp & 0x40)) { + ret = regmap_read(dev->regmap[1], 0x89, &utmp); + if (ret) + goto err; + + if (utmp & 0x01) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } else { + *status = 0; + } + break; + default: + ret = -EINVAL; + goto err; + } + + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + for (i = 0; i < 2; i++) { + ret = regmap_bulk_read(dev->regmap[2], 0x86 + i, + &buf[i], 1); + if (ret) + goto err; + } + + /* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */ + utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2; + dev_dbg(&client->dev, "strength=%u\n", utmp1); + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = utmp1; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) { + /* DVB-T CNR */ + ret = regmap_bulk_read(dev->regmap[0], 0x8f, buf, 2); + if (ret) + goto err; + + utmp = buf[0] << 8 | buf[1] << 0; + if (utmp) { + /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ + /* log10(65536) = 80807124, 0.2 = 3355443 */ + stmp = div_u64(((u64)80807124 - intlog10(utmp) + + 3355443) * 10000, 1 << 24); + dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp); + } else { + stmp = 0; + } + + c->cnr.stat[0].svalue = stmp; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else if (*status & FE_HAS_VITERBI && + c->delivery_system == SYS_DVBT2) { + /* DVB-T2 CNR */ + for (i = 0; i < 3; i++) { + ret = regmap_bulk_read(dev->regmap[2], 0xb7 + i, + &buf[i], 1); + if (ret) + goto err; + } + + utmp = buf[1] << 8 | buf[2] << 0; + utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */ + if (utmp) { + if (utmp1) { + /* CNR[dB]: 10 * (log10(16384 / value) - 0.6) */ + /* log10(16384) = 70706234, 0.6 = 10066330 */ + stmp = div_u64(((u64)70706234 - intlog10(utmp) + - 10066330) * 10000, 1 << 24); + dev_dbg(&client->dev, "cnr=%d value=%u MISO\n", + stmp, utmp); + } else { + /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ + /* log10(65536) = 80807124, 0.2 = 3355443 */ + stmp = div_u64(((u64)80807124 - intlog10(utmp) + + 3355443) * 10000, 1 << 24); + dev_dbg(&client->dev, "cnr=%d value=%u SISO\n", + stmp, utmp); + } + } else { + stmp = 0; + } + + c->cnr.stat[0].svalue = stmp; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else if (*status & FE_HAS_VITERBI && + c->delivery_system == SYS_DVBC_ANNEX_A) { + /* DVB-C CNR */ + ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4); + if (ret) + goto err; + + utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */ + utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */ + if (utmp1 && utmp2) { + /* CNR[dB]: 10 * log10(8 * (signal / noise)) */ + /* log10(8) = 15151336 */ + stmp = div_u64(((u64)15151336 + intlog10(utmp1) + - intlog10(utmp2)) * 10000, 1 << 24); + dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n", + stmp, utmp1, utmp2); + } else { + stmp = 0; + } + + c->cnr.stat[0].svalue = stmp; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (*status & FE_HAS_LOCK && (c->delivery_system == SYS_DVBT || + c->delivery_system == SYS_DVBC_ANNEX_A)) { + /* DVB-T & DVB-C BER */ + ret = regmap_bulk_read(dev->regmap[0], 0x92, buf, 5); + if (ret) + goto err; + + utmp1 = buf[0] << 16 | buf[1] << 8 | buf[2] << 0; + utmp2 = buf[3] << 8 | buf[4] << 0; + utmp2 = utmp2 * 8 * 204; + dev_dbg(&client->dev, "post_bit_error=%u post_bit_count=%u\n", + utmp1, utmp2); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += utmp1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp2; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* PER */ + if (*status & FE_HAS_LOCK) { + ret = regmap_bulk_read(dev->regmap[0], 0xdd, buf, 4); + if (ret) + goto err; + + utmp1 = buf[0] << 8 | buf[1] << 0; + utmp2 = buf[2] << 8 | buf[3] << 0; + dev_dbg(&client->dev, "block_error=%u block_count=%u\n", + utmp1, utmp2); + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += utmp1; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += utmp2; + } else { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_init(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, len, remain; + unsigned int uitmp; + const struct firmware *fw; + const char *name = MN88473_FIRMWARE; + + dev_dbg(&client->dev, "\n"); + + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &uitmp); + if (ret) + goto err; + + if (!(uitmp & 0x01)) + goto warm; + + /* Request the firmware, this will block and timeout */ + ret = request_firmware(&fw, name, &client->dev); + if (ret) { + dev_err(&client->dev, "firmware file '%s' not found\n", name); + goto err; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); + + ret = regmap_write(dev->regmap[0], 0xf5, 0x03); + if (ret) + goto err_release_firmware; + + for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) { + len = min(dev->i2c_wr_max - 1, remain); + ret = regmap_bulk_write(dev->regmap[0], 0xf6, + &fw->data[fw->size - remain], len); + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; + } + } + + release_firmware(fw); + + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &uitmp); + if (ret) + goto err; + + if (uitmp & 0x10) { + dev_err(&client->dev, "firmware parity check failed\n"); + ret = -EINVAL; + goto err; + } + + ret = regmap_write(dev->regmap[0], 0xf5, 0x00); + if (ret) + goto err; +warm: + /* TS config */ + ret = regmap_write(dev->regmap[2], 0x09, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x08, 0x1d); + if (ret) + goto err; + + dev->active = true; + + /* init stats here to indicate which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +err_release_firmware: + release_firmware(fw); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_sleep(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "\n"); + + dev->active = false; + + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops mn88473_ops = { + .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, + .info = { + .name = "Panasonic MN88473", + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM + }, + + .get_tune_settings = mn88473_get_tune_settings, + + .init = mn88473_init, + .sleep = mn88473_sleep, + + .set_frontend = mn88473_set_frontend, + + .read_status = mn88473_read_status, +}; + +static int mn88473_probe(struct i2c_client *client) +{ + struct mn88473_config *config = client->dev.platform_data; + struct mn88473_dev *dev; + int ret; + unsigned int uitmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + dev_dbg(&client->dev, "\n"); + + /* Caller really need to provide pointer for frontend we create */ + if (config->fe == NULL) { + dev_err(&client->dev, "frontend pointer not defined\n"); + ret = -EINVAL; + goto err; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + goto err; + } + + if (config->i2c_wr_max) + dev->i2c_wr_max = config->i2c_wr_max; + else + dev->i2c_wr_max = ~0; + + if (config->xtal) + dev->clk = config->xtal; + else + dev->clk = 25000000; + dev->client[0] = client; + dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); + if (IS_ERR(dev->regmap[0])) { + ret = PTR_ERR(dev->regmap[0]); + goto err_kfree; + } + + /* + * Chip has three I2C addresses for different register banks. Used + * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. + */ + dev->client[1] = i2c_new_dummy_device(client->adapter, 0x1a); + if (IS_ERR(dev->client[1])) { + ret = PTR_ERR(dev->client[1]); + dev_err(&client->dev, "I2C registration failed\n"); + goto err_regmap_0_regmap_exit; + } + dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); + if (IS_ERR(dev->regmap[1])) { + ret = PTR_ERR(dev->regmap[1]); + goto err_client_1_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[1], dev); + + dev->client[2] = i2c_new_dummy_device(client->adapter, 0x1c); + if (IS_ERR(dev->client[2])) { + ret = PTR_ERR(dev->client[2]); + dev_err(&client->dev, "2nd I2C registration failed\n"); + goto err_regmap_1_regmap_exit; + } + dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); + if (IS_ERR(dev->regmap[2])) { + ret = PTR_ERR(dev->regmap[2]); + goto err_client_2_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[2], dev); + + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[2], 0xff, &uitmp); + if (ret) + goto err_regmap_2_regmap_exit; + + dev_dbg(&client->dev, "chip id=%02x\n", uitmp); + + if (uitmp != 0x03) { + ret = -ENODEV; + goto err_regmap_2_regmap_exit; + } + + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_regmap_2_regmap_exit; + + /* Create dvb frontend */ + memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); + dev->frontend.demodulator_priv = client; + *config->fe = &dev->frontend; + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); + + return 0; +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); +err_client_2_i2c_unregister_device: + i2c_unregister_device(dev->client[2]); +err_regmap_1_regmap_exit: + regmap_exit(dev->regmap[1]); +err_client_1_i2c_unregister_device: + i2c_unregister_device(dev->client[1]); +err_regmap_0_regmap_exit: + regmap_exit(dev->regmap[0]); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void mn88473_remove(struct i2c_client *client) +{ + struct mn88473_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + regmap_exit(dev->regmap[2]); + i2c_unregister_device(dev->client[2]); + + regmap_exit(dev->regmap[1]); + i2c_unregister_device(dev->client[1]); + + regmap_exit(dev->regmap[0]); + + kfree(dev); +} + +static const struct i2c_device_id mn88473_id_table[] = { + {"mn88473", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88473_id_table); + +static struct i2c_driver mn88473_driver = { + .driver = { + .name = "mn88473", + .suppress_bind_attrs = true, + }, + .probe = mn88473_probe, + .remove = mn88473_remove, + .id_table = mn88473_id_table, +}; + +module_i2c_driver(mn88473_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(MN88473_FIRMWARE); diff --git a/drivers/media/dvb-frontends/mn88473.h b/drivers/media/dvb-frontends/mn88473.h new file mode 100644 index 0000000000..b8b75c8cfd --- /dev/null +++ b/drivers/media/dvb-frontends/mn88473.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Panasonic MN88473 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + */ + +#ifndef MN88473_H +#define MN88473_H + +#include <linux/dvb/frontend.h> + +struct mn88473_config { + /* + * Max num of bytes given I2C adapter could write at once. + * Default: unlimited + */ + u16 i2c_wr_max; + + /* + * Xtal frequency Hz. + * Default: 25000000 + */ + u32 xtal; + + + /* Everything after that is returned by the driver. */ + + /* + * DVB frontend. + */ + struct dvb_frontend **fe; +}; + +#endif diff --git a/drivers/media/dvb-frontends/mn88473_priv.h b/drivers/media/dvb-frontends/mn88473_priv.h new file mode 100644 index 0000000000..e9daaacfa2 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88473_priv.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Panasonic MN88473 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + */ + +#ifndef MN88473_PRIV_H +#define MN88473_PRIV_H + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "mn88473.h" +#include <linux/math64.h> +#include <linux/firmware.h> +#include <linux/regmap.h> + +#define MN88473_FIRMWARE "dvb-demod-mn88473-01.fw" + +struct mn88473_dev { + struct i2c_client *client[3]; + struct regmap *regmap[3]; + struct dvb_frontend frontend; + u16 i2c_wr_max; + bool active; + u32 clk; +}; + +#endif diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c new file mode 100644 index 0000000000..fb867dd8a2 --- /dev/null +++ b/drivers/media/dvb-frontends/mt312.c @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for Zarlink VP310/MT312/ZL10313 Satellite Channel Decoder + + Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> + Copyright (C) 2008 Matthias Schwarzott <zzam@gentoo.org> + + + References: + http://products.zarlink.com/product_profiles/MT312.htm + http://products.zarlink.com/product_profiles/SL1935.htm +*/ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "mt312_priv.h" +#include "mt312.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +struct mt312_state { + struct i2c_adapter *i2c; + /* configuration settings */ + const struct mt312_config *config; + struct dvb_frontend frontend; + + u8 id; + unsigned long xtal; + u8 freq_mult; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "mt312: " args); \ + } while (0) + +#define MT312_PLL_CLK 10000000UL /* 10 MHz */ +#define MT312_PLL_CLK_10_111 10111000UL /* 10.111 MHz */ + +static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, + u8 *buf, const size_t count) +{ + int ret; + struct i2c_msg msg[2]; + u8 regbuf[1] = { reg }; + + msg[0].addr = state->config->demod_address; + msg[0].flags = 0; + msg[0].buf = regbuf; + msg[0].len = 1; + msg[1].addr = state->config->demod_address; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_DEBUG "%s: ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + if (debug) { + int i; + dprintk("R(%d):", reg & 0x7f); + for (i = 0; i < count; i++) + printk(KERN_CONT " %02x", buf[i]); + printk("\n"); + } + + return 0; +} + +static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, + const u8 *src, const size_t count) +{ + int ret; + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg msg; + + if (1 + count > sizeof(buf)) { + printk(KERN_WARNING + "mt312: write: len=%zu is too big!\n", count); + return -EINVAL; + } + + if (debug) { + int i; + dprintk("W(%d):", reg & 0x7f); + for (i = 0; i < count; i++) + printk(KERN_CONT " %02x", src[i]); + printk("\n"); + } + + buf[0] = reg; + memcpy(&buf[1], src, count); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = count + 1; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) { + dprintk("%s: ret == %d\n", __func__, ret); + return -EREMOTEIO; + } + + return 0; +} + +static inline int mt312_readreg(struct mt312_state *state, + const enum mt312_reg_addr reg, u8 *val) +{ + return mt312_read(state, reg, val, 1); +} + +static inline int mt312_writereg(struct mt312_state *state, + const enum mt312_reg_addr reg, const u8 val) +{ + u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + + return mt312_write(state, reg, &tmp, 1); +} + +static int mt312_reset(struct mt312_state *state, const u8 full) +{ + return mt312_writereg(state, RESET, full ? 0x80 : 0x40); +} + +static int mt312_get_inversion(struct mt312_state *state, + enum fe_spectral_inversion *i) +{ + int ret; + u8 vit_mode; + + ret = mt312_readreg(state, VIT_MODE, &vit_mode); + if (ret < 0) + return ret; + + if (vit_mode & 0x80) /* auto inversion was used */ + *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; + + return 0; +} + +static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr) +{ + int ret; + u8 sym_rate_h; + u8 dec_ratio; + u16 sym_rat_op; + u16 monitor; + u8 buf[2]; + + ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h); + if (ret < 0) + return ret; + + if (sym_rate_h & 0x80) { + /* symbol rate search was used */ + ret = mt312_writereg(state, MON_CTRL, 0x03); + if (ret < 0) + return ret; + + ret = mt312_read(state, MONITOR_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + monitor = (buf[0] << 8) | buf[1]; + + dprintk("sr(auto) = %u\n", + DIV_ROUND_CLOSEST(monitor * 15625, 4)); + } else { + ret = mt312_writereg(state, MON_CTRL, 0x05); + if (ret < 0) + return ret; + + ret = mt312_read(state, MONITOR_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + dec_ratio = ((buf[0] >> 5) & 0x07) * 32; + + ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + sym_rat_op = (buf[0] << 8) | buf[1]; + + dprintk("sym_rat_op=%d dec_ratio=%d\n", + sym_rat_op, dec_ratio); + dprintk("*sr(manual) = %lu\n", + (((state->xtal * 8192) / (sym_rat_op + 8192)) * + 2) - dec_ratio); + } + + return 0; +} + +static int mt312_get_code_rate(struct mt312_state *state, enum fe_code_rate *cr) +{ + const enum fe_code_rate fec_tab[8] = + { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, + FEC_AUTO, FEC_AUTO }; + + int ret; + u8 fec_status; + + ret = mt312_readreg(state, FEC_STATUS, &fec_status); + if (ret < 0) + return ret; + + *cr = fec_tab[(fec_status >> 4) & 0x07]; + + return 0; +} + +static int mt312_initfe(struct dvb_frontend *fe) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 buf[2]; + + /* wake up */ + ret = mt312_writereg(state, CONFIG, + (state->freq_mult == 6 ? 0x88 : 0x8c)); + if (ret < 0) + return ret; + + /* wait at least 150 usec */ + udelay(150); + + /* full reset */ + ret = mt312_reset(state, 1); + if (ret < 0) + return ret; + +/* Per datasheet, write correct values. 09/28/03 ACCJr. + * If we don't do this, we won't get FE_HAS_VITERBI in the VP310. */ + { + u8 buf_def[8] = { 0x14, 0x12, 0x03, 0x02, + 0x01, 0x00, 0x00, 0x00 }; + + ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def)); + if (ret < 0) + return ret; + } + + switch (state->id) { + case ID_ZL10313: + /* enable ADC */ + ret = mt312_writereg(state, GPP_CTRL, 0x80); + if (ret < 0) + return ret; + + /* configure ZL10313 for optimal ADC performance */ + buf[0] = 0x80; + buf[1] = 0xB0; + ret = mt312_write(state, HW_CTRL, buf, 2); + if (ret < 0) + return ret; + + /* enable MPEG output and ADCs */ + ret = mt312_writereg(state, HW_CTRL, 0x00); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, MPEG_CTRL, 0x00); + if (ret < 0) + return ret; + + break; + } + + /* SYS_CLK */ + buf[0] = DIV_ROUND_CLOSEST(state->xtal * state->freq_mult * 2, 1000000); + + /* DISEQC_RATIO */ + buf[1] = DIV_ROUND_CLOSEST(state->xtal, 22000 * 4); + + ret = mt312_write(state, SYS_CLK, buf, sizeof(buf)); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, SNR_THS_HIGH, 0x32); + if (ret < 0) + return ret; + + /* different MOCLK polarity */ + switch (state->id) { + case ID_ZL10313: + buf[0] = 0x33; + break; + default: + buf[0] = 0x53; + break; + } + + ret = mt312_writereg(state, OP_CTRL, buf[0]); + if (ret < 0) + return ret; + + /* TS_SW_LIM */ + buf[0] = 0x8c; + buf[1] = 0x98; + + ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf)); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, CS_SW_LIM, 0x69); + if (ret < 0) + return ret; + + return 0; +} + +static int mt312_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *c) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 diseqc_mode; + + if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg))) + return -EINVAL; + + ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); + if (ret < 0) + return ret; + + ret = mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, DISEQC_MODE, + (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3) + | 0x04); + if (ret < 0) + return ret; + + /* is there a better way to wait for message to be transmitted */ + msleep(100); + + /* set DISEQC_MODE[2:0] to zero if a return message is expected */ + if (c->msg[0] & 0x02) { + ret = mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40)); + if (ret < 0) + return ret; + } + + return 0; +} + +static int mt312_send_burst(struct dvb_frontend *fe, + const enum fe_sec_mini_cmd c) +{ + struct mt312_state *state = fe->demodulator_priv; + const u8 mini_tab[2] = { 0x02, 0x03 }; + + int ret; + u8 diseqc_mode; + + if (c > SEC_MINI_B) + return -EINVAL; + + ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, DISEQC_MODE, + (diseqc_mode & 0x40) | mini_tab[c]); + if (ret < 0) + return ret; + + return 0; +} + +static int mt312_set_tone(struct dvb_frontend *fe, + const enum fe_sec_tone_mode t) +{ + struct mt312_state *state = fe->demodulator_priv; + const u8 tone_tab[2] = { 0x01, 0x00 }; + + int ret; + u8 diseqc_mode; + + if (t > SEC_TONE_OFF) + return -EINVAL; + + ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, DISEQC_MODE, + (diseqc_mode & 0x40) | tone_tab[t]); + if (ret < 0) + return ret; + + return 0; +} + +static int mt312_set_voltage(struct dvb_frontend *fe, + const enum fe_sec_voltage v) +{ + struct mt312_state *state = fe->demodulator_priv; + const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; + u8 val; + + if (v > SEC_VOLTAGE_OFF) + return -EINVAL; + + val = volt_tab[v]; + if (state->config->voltage_inverted) + val ^= 0x40; + + return mt312_writereg(state, DISEQC_MODE, val); +} + +static int mt312_read_status(struct dvb_frontend *fe, enum fe_status *s) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 status[3]; + + *s = 0; + + ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status)); + if (ret < 0) + return ret; + + dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", + status[0], status[1], status[2]); + + if (status[0] & 0xc0) + *s |= FE_HAS_SIGNAL; /* signal noise ratio */ + if (status[0] & 0x04) + *s |= FE_HAS_CARRIER; /* qpsk carrier lock */ + if (status[2] & 0x02) + *s |= FE_HAS_VITERBI; /* viterbi lock */ + if (status[2] & 0x04) + *s |= FE_HAS_SYNC; /* byte align lock */ + if (status[0] & 0x01) + *s |= FE_HAS_LOCK; /* qpsk lock */ + + return 0; +} + +static int mt312_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 buf[3]; + + ret = mt312_read(state, RS_BERCNT_H, buf, 3); + if (ret < 0) + return ret; + + *ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64; + + return 0; +} + +static int mt312_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 buf[3]; + u16 agc; + s16 err_db; + + ret = mt312_read(state, AGC_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + agc = (buf[0] << 6) | (buf[1] >> 2); + err_db = (s16) (((buf[1] & 0x03) << 14) | buf[2] << 6) >> 6; + + *signal_strength = agc; + + dprintk("agc=%08x err_db=%hd\n", agc, err_db); + + return 0; +} + +static int mt312_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 buf[2]; + + ret = mt312_read(state, M_SNR_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + *snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1); + + return 0; +} + +static int mt312_read_ucblocks(struct dvb_frontend *fe, u32 *ubc) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 buf[2]; + + ret = mt312_read(state, RS_UBC_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + *ubc = (buf[0] << 8) | buf[1]; + + return 0; +} + +static int mt312_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 buf[5], config_val; + u16 sr; + + const u8 fec_tab[10] = + { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f }; + const u8 inv_tab[3] = { 0x00, 0x40, 0x80 }; + + dprintk("%s: Freq %d\n", __func__, p->frequency); + + if ((p->frequency < fe->ops.info.frequency_min_hz / kHz) + || (p->frequency > fe->ops.info.frequency_max_hz / kHz)) + return -EINVAL; + + if (((int)p->inversion < INVERSION_OFF) + || (p->inversion > INVERSION_ON)) + return -EINVAL; + + if ((p->symbol_rate < fe->ops.info.symbol_rate_min) + || (p->symbol_rate > fe->ops.info.symbol_rate_max)) + return -EINVAL; + + if (((int)p->fec_inner < FEC_NONE) + || (p->fec_inner > FEC_AUTO)) + return -EINVAL; + + if ((p->fec_inner == FEC_4_5) + || (p->fec_inner == FEC_8_9)) + return -EINVAL; + + switch (state->id) { + case ID_VP310: + /* For now we will do this only for the VP310. + * It should be better for the mt312 as well, + * but tuning will be slower. ACCJr 09/29/03 + */ + ret = mt312_readreg(state, CONFIG, &config_val); + if (ret < 0) + return ret; + if (p->symbol_rate >= 30000000) { + /* Note that 30MS/s should use 90MHz */ + if (state->freq_mult == 6) { + /* We are running 60MHz */ + state->freq_mult = 9; + ret = mt312_initfe(fe); + if (ret < 0) + return ret; + } + } else { + if (state->freq_mult == 9) { + /* We are running 90MHz */ + state->freq_mult = 6; + ret = mt312_initfe(fe); + if (ret < 0) + return ret; + } + } + break; + + case ID_MT312: + case ID_ZL10313: + break; + + default: + return -EINVAL; + } + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* sr = (u16)(sr * 256.0 / 1000000.0) */ + sr = DIV_ROUND_CLOSEST(p->symbol_rate * 4, 15625); + + /* SYM_RATE */ + buf[0] = (sr >> 8) & 0x3f; + buf[1] = (sr >> 0) & 0xff; + + /* VIT_MODE */ + buf[2] = inv_tab[p->inversion] | fec_tab[p->fec_inner]; + + /* QPSK_CTRL */ + buf[3] = 0x40; /* swap I and Q before QPSK demodulation */ + + if (p->symbol_rate < 10000000) + buf[3] |= 0x04; /* use afc mode */ + + /* GO */ + buf[4] = 0x01; + + ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf)); + if (ret < 0) + return ret; + + ret = mt312_reset(state, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int mt312_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + + ret = mt312_get_inversion(state, &p->inversion); + if (ret < 0) + return ret; + + ret = mt312_get_symbol_rate(state, &p->symbol_rate); + if (ret < 0) + return ret; + + ret = mt312_get_code_rate(state, &p->fec_inner); + if (ret < 0) + return ret; + + return 0; +} + +static int mt312_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct mt312_state *state = fe->demodulator_priv; + + u8 val = 0x00; + int ret; + + switch (state->id) { + case ID_ZL10313: + ret = mt312_readreg(state, GPP_CTRL, &val); + if (ret < 0) + goto error; + + /* preserve this bit to not accidentally shutdown ADC */ + val &= 0x80; + break; + } + + if (enable) + val |= 0x40; + else + val &= ~0x40; + + ret = mt312_writereg(state, GPP_CTRL, val); + +error: + return ret; +} + +static int mt312_sleep(struct dvb_frontend *fe) +{ + struct mt312_state *state = fe->demodulator_priv; + int ret; + u8 config; + + /* reset all registers to defaults */ + ret = mt312_reset(state, 1); + if (ret < 0) + return ret; + + if (state->id == ID_ZL10313) { + /* reset ADC */ + ret = mt312_writereg(state, GPP_CTRL, 0x00); + if (ret < 0) + return ret; + + /* full shutdown of ADCs, mpeg bus tristated */ + ret = mt312_writereg(state, HW_CTRL, 0x0d); + if (ret < 0) + return ret; + } + + ret = mt312_readreg(state, CONFIG, &config); + if (ret < 0) + return ret; + + /* enter standby */ + ret = mt312_writereg(state, CONFIG, config & 0x7f); + if (ret < 0) + return ret; + + return 0; +} + +static int mt312_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 50; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void mt312_release(struct dvb_frontend *fe) +{ + struct mt312_state *state = fe->demodulator_priv; + kfree(state); +} + +#define MT312_SYS_CLK 90000000UL /* 90 MHz */ +static const struct dvb_frontend_ops mt312_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Zarlink ???? DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + /* FIXME: adjust freq to real used xtal */ + .frequency_stepsize_hz = MT312_PLL_CLK / 128, + .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ + .symbol_rate_max = MT312_SYS_CLK / 2, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | + FE_CAN_RECOVER + }, + + .release = mt312_release, + + .init = mt312_initfe, + .sleep = mt312_sleep, + .i2c_gate_ctrl = mt312_i2c_gate_ctrl, + + .set_frontend = mt312_set_frontend, + .get_frontend = mt312_get_frontend, + .get_tune_settings = mt312_get_tune_settings, + + .read_status = mt312_read_status, + .read_ber = mt312_read_ber, + .read_signal_strength = mt312_read_signal_strength, + .read_snr = mt312_read_snr, + .read_ucblocks = mt312_read_ucblocks, + + .diseqc_send_master_cmd = mt312_send_master_cmd, + .diseqc_send_burst = mt312_send_burst, + .set_tone = mt312_set_tone, + .set_voltage = mt312_set_voltage, +}; + +struct dvb_frontend *mt312_attach(const struct mt312_config *config, + struct i2c_adapter *i2c) +{ + struct mt312_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if (mt312_readreg(state, ID, &state->id) < 0) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &mt312_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + switch (state->id) { + case ID_VP310: + strscpy(state->frontend.ops.info.name, "Zarlink VP310 DVB-S", + sizeof(state->frontend.ops.info.name)); + state->xtal = MT312_PLL_CLK; + state->freq_mult = 9; + break; + case ID_MT312: + strscpy(state->frontend.ops.info.name, "Zarlink MT312 DVB-S", + sizeof(state->frontend.ops.info.name)); + state->xtal = MT312_PLL_CLK; + state->freq_mult = 6; + break; + case ID_ZL10313: + strscpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S", + sizeof(state->frontend.ops.info.name)); + state->xtal = MT312_PLL_CLK_10_111; + state->freq_mult = 9; + break; + default: + printk(KERN_WARNING "Only Zarlink VP310/MT312/ZL10313 are supported chips.\n"); + goto error; + } + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(mt312_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Zarlink VP310/MT312/ZL10313 DVB-S Demodulator driver"); +MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>"); +MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/dvb-frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h new file mode 100644 index 0000000000..21828513f8 --- /dev/null +++ b/drivers/media/dvb-frontends/mt312.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for Zarlink MT312 Satellite Channel Decoder + + Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> + + + References: + http://products.zarlink.com/product_profiles/MT312.htm + http://products.zarlink.com/product_profiles/SL1935.htm +*/ + +#ifndef MT312_H +#define MT312_H + +#include <linux/dvb/frontend.h> + +struct mt312_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* inverted voltage setting */ + unsigned int voltage_inverted:1; +}; + +#if IS_REACHABLE(CONFIG_DVB_MT312) +struct dvb_frontend *mt312_attach(const struct mt312_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *mt312_attach( + const struct mt312_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_MT312 */ + +#endif /* MT312_H */ diff --git a/drivers/media/dvb-frontends/mt312_priv.h b/drivers/media/dvb-frontends/mt312_priv.h new file mode 100644 index 0000000000..4582b15c99 --- /dev/null +++ b/drivers/media/dvb-frontends/mt312_priv.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for Zarlink MT312 QPSK Frontend + + Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> + + +*/ + +#ifndef _DVB_FRONTENDS_MT312_PRIV +#define _DVB_FRONTENDS_MT312_PRIV + +enum mt312_reg_addr { + QPSK_INT_H = 0, + QPSK_INT_M = 1, + QPSK_INT_L = 2, + FEC_INT = 3, + QPSK_STAT_H = 4, + QPSK_STAT_L = 5, + FEC_STATUS = 6, + LNB_FREQ_H = 7, + LNB_FREQ_L = 8, + M_SNR_H = 9, + M_SNR_L = 10, + VIT_ERRCNT_H = 11, + VIT_ERRCNT_M = 12, + VIT_ERRCNT_L = 13, + RS_BERCNT_H = 14, + RS_BERCNT_M = 15, + RS_BERCNT_L = 16, + RS_UBC_H = 17, + RS_UBC_L = 18, + SIG_LEVEL = 19, + GPP_CTRL = 20, + RESET = 21, + DISEQC_MODE = 22, + SYM_RATE_H = 23, + SYM_RATE_L = 24, + VIT_MODE = 25, + QPSK_CTRL = 26, + GO = 27, + IE_QPSK_H = 28, + IE_QPSK_M = 29, + IE_QPSK_L = 30, + IE_FEC = 31, + QPSK_STAT_EN = 32, + FEC_STAT_EN = 33, + SYS_CLK = 34, + DISEQC_RATIO = 35, + DISEQC_INSTR = 36, + FR_LIM = 37, + FR_OFF = 38, + AGC_CTRL = 39, + AGC_INIT = 40, + AGC_REF = 41, + AGC_MAX = 42, + AGC_MIN = 43, + AGC_LK_TH = 44, + TS_AGC_LK_TH = 45, + AGC_PWR_SET = 46, + QPSK_MISC = 47, + SNR_THS_LOW = 48, + SNR_THS_HIGH = 49, + TS_SW_RATE = 50, + TS_SW_LIM_L = 51, + TS_SW_LIM_H = 52, + CS_SW_RATE_1 = 53, + CS_SW_RATE_2 = 54, + CS_SW_RATE_3 = 55, + CS_SW_RATE_4 = 56, + CS_SW_LIM = 57, + TS_LPK = 58, + TS_LPK_M = 59, + TS_LPK_L = 60, + CS_KPROP_H = 61, + CS_KPROP_L = 62, + CS_KINT_H = 63, + CS_KINT_L = 64, + QPSK_SCALE = 65, + TLD_OUTCLK_TH = 66, + TLD_INCLK_TH = 67, + FLD_TH = 68, + PLD_OUTLK3 = 69, + PLD_OUTLK2 = 70, + PLD_OUTLK1 = 71, + PLD_OUTLK0 = 72, + PLD_INLK3 = 73, + PLD_INLK2 = 74, + PLD_INLK1 = 75, + PLD_INLK0 = 76, + PLD_ACC_TIME = 77, + SWEEP_PAR = 78, + STARTUP_TIME = 79, + LOSSLOCK_TH = 80, + FEC_LOCK_TM = 81, + LOSSLOCK_TM = 82, + VIT_ERRPER_H = 83, + VIT_ERRPER_M = 84, + VIT_ERRPER_L = 85, + HW_CTRL = 84, /* ZL10313 only */ + MPEG_CTRL = 85, /* ZL10313 only */ + VIT_SETUP = 86, + VIT_REF0 = 87, + VIT_REF1 = 88, + VIT_REF2 = 89, + VIT_REF3 = 90, + VIT_REF4 = 91, + VIT_REF5 = 92, + VIT_REF6 = 93, + VIT_MAXERR = 94, + BA_SETUPT = 95, + OP_CTRL = 96, + FEC_SETUP = 97, + PROG_SYNC = 98, + AFC_SEAR_TH = 99, + CSACC_DIF_TH = 100, + QPSK_LK_CT = 101, + QPSK_ST_CT = 102, + MON_CTRL = 103, + QPSK_RESET = 104, + QPSK_TST_CT = 105, + QPSK_TST_ST = 106, + TEST_R = 107, + AGC_H = 108, + AGC_M = 109, + AGC_L = 110, + FREQ_ERR1_H = 111, + FREQ_ERR1_M = 112, + FREQ_ERR1_L = 113, + FREQ_ERR2_H = 114, + FREQ_ERR2_L = 115, + SYM_RAT_OP_H = 116, + SYM_RAT_OP_L = 117, + DESEQC2_INT = 118, + DISEQC2_STAT = 119, + DISEQC2_FIFO = 120, + DISEQC2_CTRL1 = 121, + DISEQC2_CTRL2 = 122, + MONITOR_H = 123, + MONITOR_L = 124, + TEST_MODE = 125, + ID = 126, + CONFIG = 127 +}; + +enum mt312_model_id { + ID_VP310 = 1, + ID_MT312 = 3, + ID_ZL10313 = 5, +}; + +#endif /* DVB_FRONTENDS_MT312_PRIV */ diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c new file mode 100644 index 0000000000..1b2889f5cf --- /dev/null +++ b/drivers/media/dvb-frontends/mt352.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Zarlink DVB-T MT352 demodulator + * + * Written by Holger Waechtler <holger@qanu.de> + * and Daniel Mack <daniel@qanu.de> + * + * AVerMedia AVerTV DVB-T 771 support by + * Wolfram Joost <dbox2@frokaschwei.de> + * + * Support for Samsung TDTC9251DH01C(M) tuner + * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it> + * Amauri Celani <acelani@essegi.net> + * + * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by + * Christopher Pascoe <c.pascoe@itee.uq.edu.au> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "mt352_priv.h" +#include "mt352.h" + +struct mt352_state { + struct i2c_adapter* i2c; + struct dvb_frontend frontend; + + /* configuration settings */ + struct mt352_config config; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "mt352: " args); \ + } while (0) + +static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val) +{ + struct mt352_state* state = fe->demodulator_priv; + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, + .buf = buf, .len = 2 }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk("mt352_write() to reg %x failed (err = %d)!\n", reg, err); + return err; + } + return 0; +} + +static int _mt352_write(struct dvb_frontend* fe, const u8 ibuf[], int ilen) +{ + int err,i; + for (i=0; i < ilen-1; i++) + if ((err = mt352_single_write(fe,ibuf[0]+i,ibuf[i+1]))) + return err; + + return 0; +} + +static int mt352_read_register(struct mt352_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config.demod_address, + .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config.demod_address, + .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk("%s: readreg error (reg=%d, ret==%i)\n", + __func__, reg, ret); + return ret; + } + + return b1[0]; +} + +static int mt352_sleep(struct dvb_frontend* fe) +{ + static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; + + _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); + return 0; +} + +static void mt352_calc_nominal_rate(struct mt352_state* state, + u32 bandwidth, + unsigned char *buf) +{ + u32 adc_clock = 20480; /* 20.340 MHz */ + u32 bw,value; + + switch (bandwidth) { + case 6000000: + bw = 6; + break; + case 7000000: + bw = 7; + break; + case 8000000: + default: + bw = 8; + break; + } + if (state->config.adc_clock) + adc_clock = state->config.adc_clock; + + value = 64 * bw * (1<<16) / (7 * 8); + value = value * 1000 / adc_clock; + dprintk("%s: bw %d, adc_clock %d => 0x%x\n", + __func__, bw, adc_clock, value); + buf[0] = msb(value); + buf[1] = lsb(value); +} + +static void mt352_calc_input_freq(struct mt352_state* state, + unsigned char *buf) +{ + int adc_clock = 20480; /* 20.480000 MHz */ + int if2 = 36167; /* 36.166667 MHz */ + int ife,value; + + if (state->config.adc_clock) + adc_clock = state->config.adc_clock; + if (state->config.if2) + if2 = state->config.if2; + + if (adc_clock >= if2 * 2) + ife = if2; + else { + ife = adc_clock - (if2 % adc_clock); + if (ife > adc_clock / 2) + ife = adc_clock - ife; + } + value = -16374 * ife / adc_clock; + dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", + __func__, if2, ife, adc_clock, value, value & 0x3fff); + buf[0] = msb(value); + buf[1] = lsb(value); +} + +static int mt352_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *op = &fe->dtv_property_cache; + struct mt352_state* state = fe->demodulator_priv; + unsigned char buf[13]; + static unsigned char tuner_go[] = { 0x5d, 0x01 }; + static unsigned char fsm_go[] = { 0x5e, 0x01 }; + unsigned int tps = 0; + + switch (op->code_rate_HP) { + case FEC_2_3: + tps |= (1 << 7); + break; + case FEC_3_4: + tps |= (2 << 7); + break; + case FEC_5_6: + tps |= (3 << 7); + break; + case FEC_7_8: + tps |= (4 << 7); + break; + case FEC_1_2: + case FEC_AUTO: + break; + default: + return -EINVAL; + } + + switch (op->code_rate_LP) { + case FEC_2_3: + tps |= (1 << 4); + break; + case FEC_3_4: + tps |= (2 << 4); + break; + case FEC_5_6: + tps |= (3 << 4); + break; + case FEC_7_8: + tps |= (4 << 4); + break; + case FEC_1_2: + case FEC_AUTO: + break; + case FEC_NONE: + if (op->hierarchy == HIERARCHY_AUTO || + op->hierarchy == HIERARCHY_NONE) + break; + fallthrough; + default: + return -EINVAL; + } + + switch (op->modulation) { + case QPSK: + break; + case QAM_AUTO: + case QAM_16: + tps |= (1 << 13); + break; + case QAM_64: + tps |= (2 << 13); + break; + default: + return -EINVAL; + } + + switch (op->transmission_mode) { + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + break; + case TRANSMISSION_MODE_8K: + tps |= (1 << 0); + break; + default: + return -EINVAL; + } + + switch (op->guard_interval) { + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + break; + case GUARD_INTERVAL_1_16: + tps |= (1 << 2); + break; + case GUARD_INTERVAL_1_8: + tps |= (2 << 2); + break; + case GUARD_INTERVAL_1_4: + tps |= (3 << 2); + break; + default: + return -EINVAL; + } + + switch (op->hierarchy) { + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + tps |= (1 << 10); + break; + case HIERARCHY_2: + tps |= (2 << 10); + break; + case HIERARCHY_4: + tps |= (3 << 10); + break; + default: + return -EINVAL; + } + + + buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */ + + buf[1] = msb(tps); /* TPS_GIVEN_(1|0) */ + buf[2] = lsb(tps); + + buf[3] = 0x50; // old +// buf[3] = 0xf4; // pinnacle + + mt352_calc_nominal_rate(state, op->bandwidth_hz, buf+4); + mt352_calc_input_freq(state, buf+6); + + if (state->config.no_tuner) { + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + _mt352_write(fe, buf, 8); + _mt352_write(fe, fsm_go, 2); + } else { + if (fe->ops.tuner_ops.calc_regs) { + fe->ops.tuner_ops.calc_regs(fe, buf+8, 5); + buf[8] <<= 1; + _mt352_write(fe, buf, sizeof(buf)); + _mt352_write(fe, tuner_go, 2); + } + } + + return 0; +} + +static int mt352_get_parameters(struct dvb_frontend* fe, + struct dtv_frontend_properties *op) +{ + struct mt352_state* state = fe->demodulator_priv; + u16 tps; + u16 div; + u8 trl; + static const u8 tps_fec_to_api[8] = + { + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_5_6, + FEC_7_8, + FEC_AUTO, + FEC_AUTO, + FEC_AUTO + }; + + if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 ) + return -EINVAL; + + /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because + * the mt352 sometimes works with the wrong parameters + */ + tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0); + div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0); + trl = mt352_read_register(state, TRL_NOMINAL_RATE_1); + + op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; + op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; + + switch ( (tps >> 13) & 3) + { + case 0: + op->modulation = QPSK; + break; + case 1: + op->modulation = QAM_16; + break; + case 2: + op->modulation = QAM_64; + break; + default: + op->modulation = QAM_AUTO; + break; + } + + op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K; + + switch ( (tps >> 2) & 3) + { + case 0: + op->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + op->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + op->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + op->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + op->guard_interval = GUARD_INTERVAL_AUTO; + break; + } + + switch ( (tps >> 10) & 7) + { + case 0: + op->hierarchy = HIERARCHY_NONE; + break; + case 1: + op->hierarchy = HIERARCHY_1; + break; + case 2: + op->hierarchy = HIERARCHY_2; + break; + case 3: + op->hierarchy = HIERARCHY_4; + break; + default: + op->hierarchy = HIERARCHY_AUTO; + break; + } + + op->frequency = (500 * (div - IF_FREQUENCYx6)) / 3 * 1000; + + if (trl == 0x72) + op->bandwidth_hz = 8000000; + else if (trl == 0x64) + op->bandwidth_hz = 7000000; + else + op->bandwidth_hz = 6000000; + + + if (mt352_read_register(state, STATUS_2) & 0x02) + op->inversion = INVERSION_OFF; + else + op->inversion = INVERSION_ON; + + return 0; +} + +static int mt352_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct mt352_state* state = fe->demodulator_priv; + int s0, s1, s3; + + /* FIXME: + * + * The MT352 design manual from Zarlink states (page 46-47): + * + * Notes about the TUNER_GO register: + * + * If the Read_Tuner_Byte (bit-1) is activated, then the tuner status + * byte is copied from the tuner to the STATUS_3 register and + * completion of the read operation is indicated by bit-5 of the + * INTERRUPT_3 register. + */ + + if ((s0 = mt352_read_register(state, STATUS_0)) < 0) + return -EREMOTEIO; + if ((s1 = mt352_read_register(state, STATUS_1)) < 0) + return -EREMOTEIO; + if ((s3 = mt352_read_register(state, STATUS_3)) < 0) + return -EREMOTEIO; + + *status = 0; + if (s0 & (1 << 4)) + *status |= FE_HAS_CARRIER; + if (s0 & (1 << 1)) + *status |= FE_HAS_VITERBI; + if (s0 & (1 << 5)) + *status |= FE_HAS_LOCK; + if (s1 & (1 << 1)) + *status |= FE_HAS_SYNC; + if (s3 & (1 << 6)) + *status |= FE_HAS_SIGNAL; + + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} + +static int mt352_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct mt352_state* state = fe->demodulator_priv; + + *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) | + (mt352_read_register (state, RS_ERR_CNT_1) << 8) | + (mt352_read_register (state, RS_ERR_CNT_0)); + + return 0; +} + +static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct mt352_state* state = fe->demodulator_priv; + + /* align the 12 bit AGC gain with the most significant bits */ + u16 signal = ((mt352_read_register(state, AGC_GAIN_1) & 0x0f) << 12) | + (mt352_read_register(state, AGC_GAIN_0) << 4); + + /* inverse of gain is signal strength */ + *strength = ~signal; + return 0; +} + +static int mt352_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct mt352_state* state = fe->demodulator_priv; + + u8 _snr = mt352_read_register (state, SNR); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct mt352_state* state = fe->demodulator_priv; + + *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) | + (mt352_read_register (state, RS_UBC_0)); + + return 0; +} + +static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 800; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + + return 0; +} + +static int mt352_init(struct dvb_frontend* fe) +{ + struct mt352_state* state = fe->demodulator_priv; + + static u8 mt352_reset_attach [] = { RESET, 0xC0 }; + + dprintk("%s: hello\n",__func__); + + if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 || + (mt352_read_register(state, CONFIG) & 0x20) == 0) { + + /* Do a "hard" reset */ + _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); + return state->config.demod_init(fe); + } + + return 0; +} + +static void mt352_release(struct dvb_frontend* fe) +{ + struct mt352_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops mt352_ops; + +struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c) +{ + struct mt352_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct mt352_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config,config,sizeof(struct mt352_config)); + + /* check if the demod is there */ + if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &mt352_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops mt352_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Zarlink MT352 DVB-T", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = mt352_release, + + .init = mt352_init, + .sleep = mt352_sleep, + .write = _mt352_write, + + .set_frontend = mt352_set_parameters, + .get_frontend = mt352_get_parameters, + .get_tune_settings = mt352_get_tune_settings, + + .read_status = mt352_read_status, + .read_ber = mt352_read_ber, + .read_signal_strength = mt352_read_signal_strength, + .read_snr = mt352_read_snr, + .read_ucblocks = mt352_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(mt352_attach); diff --git a/drivers/media/dvb-frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h new file mode 100644 index 0000000000..acb1c33748 --- /dev/null +++ b/drivers/media/dvb-frontends/mt352.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Zarlink DVB-T MT352 demodulator + * + * Written by Holger Waechtler <holger@qanu.de> + * and Daniel Mack <daniel@qanu.de> + * + * AVerMedia AVerTV DVB-T 771 support by + * Wolfram Joost <dbox2@frokaschwei.de> + * + * Support for Samsung TDTC9251DH01C(M) tuner + * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it> + * Amauri Celani <acelani@essegi.net> + * + * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by + * Christopher Pascoe <c.pascoe@itee.uq.edu.au> + */ + +#ifndef MT352_H +#define MT352_H + +#include <linux/dvb/frontend.h> + +struct mt352_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* frequencies in kHz */ + int adc_clock; // default: 20480 + int if2; // default: 36166 + + /* set if no pll is connected to the secondary i2c bus */ + int no_tuner; + + /* Initialise the demodulator and PLL. Cannot be NULL */ + int (*demod_init)(struct dvb_frontend* fe); +}; + +#if IS_REACHABLE(CONFIG_DVB_MT352) +extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_MT352 + +static inline int mt352_write(struct dvb_frontend *fe, const u8 buf[], int len) { + int r = 0; + if (fe->ops.write) + r = fe->ops.write(fe, buf, len); + return r; +} + +#endif // MT352_H diff --git a/drivers/media/dvb-frontends/mt352_priv.h b/drivers/media/dvb-frontends/mt352_priv.h new file mode 100644 index 0000000000..3ec4552efa --- /dev/null +++ b/drivers/media/dvb-frontends/mt352_priv.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Zarlink DVB-T MT352 demodulator + * + * Written by Holger Waechtler <holger@qanu.de> + * and Daniel Mack <daniel@qanu.de> + * + * AVerMedia AVerTV DVB-T 771 support by + * Wolfram Joost <dbox2@frokaschwei.de> + * + * Support for Samsung TDTC9251DH01C(M) tuner + * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it> + * Amauri Celani <acelani@essegi.net> + * + * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by + * Christopher Pascoe <c.pascoe@itee.uq.edu.au> + */ + +#ifndef _MT352_PRIV_ +#define _MT352_PRIV_ + +#define ID_MT352 0x13 + +#define msb(x) (((x) >> 8) & 0xff) +#define lsb(x) ((x) & 0xff) + +enum mt352_reg_addr { + STATUS_0 = 0x00, + STATUS_1 = 0x01, + STATUS_2 = 0x02, + STATUS_3 = 0x03, + STATUS_4 = 0x04, + INTERRUPT_0 = 0x05, + INTERRUPT_1 = 0x06, + INTERRUPT_2 = 0x07, + INTERRUPT_3 = 0x08, + SNR = 0x09, + VIT_ERR_CNT_2 = 0x0A, + VIT_ERR_CNT_1 = 0x0B, + VIT_ERR_CNT_0 = 0x0C, + RS_ERR_CNT_2 = 0x0D, + RS_ERR_CNT_1 = 0x0E, + RS_ERR_CNT_0 = 0x0F, + RS_UBC_1 = 0x10, + RS_UBC_0 = 0x11, + AGC_GAIN_3 = 0x12, + AGC_GAIN_2 = 0x13, + AGC_GAIN_1 = 0x14, + AGC_GAIN_0 = 0x15, + FREQ_OFFSET_2 = 0x17, + FREQ_OFFSET_1 = 0x18, + FREQ_OFFSET_0 = 0x19, + TIMING_OFFSET_1 = 0x1A, + TIMING_OFFSET_0 = 0x1B, + CHAN_FREQ_1 = 0x1C, + CHAN_FREQ_0 = 0x1D, + TPS_RECEIVED_1 = 0x1E, + TPS_RECEIVED_0 = 0x1F, + TPS_CURRENT_1 = 0x20, + TPS_CURRENT_0 = 0x21, + TPS_CELL_ID_1 = 0x22, + TPS_CELL_ID_0 = 0x23, + TPS_MISC_DATA_2 = 0x24, + TPS_MISC_DATA_1 = 0x25, + TPS_MISC_DATA_0 = 0x26, + RESET = 0x50, + TPS_GIVEN_1 = 0x51, + TPS_GIVEN_0 = 0x52, + ACQ_CTL = 0x53, + TRL_NOMINAL_RATE_1 = 0x54, + TRL_NOMINAL_RATE_0 = 0x55, + INPUT_FREQ_1 = 0x56, + INPUT_FREQ_0 = 0x57, + TUNER_ADDR = 0x58, + CHAN_START_1 = 0x59, + CHAN_START_0 = 0x5A, + CONT_1 = 0x5B, + CONT_0 = 0x5C, + TUNER_GO = 0x5D, + STATUS_EN_0 = 0x5F, + STATUS_EN_1 = 0x60, + INTERRUPT_EN_0 = 0x61, + INTERRUPT_EN_1 = 0x62, + INTERRUPT_EN_2 = 0x63, + INTERRUPT_EN_3 = 0x64, + AGC_TARGET = 0x67, + AGC_CTL = 0x68, + CAPT_RANGE = 0x75, + SNR_SELECT_1 = 0x79, + SNR_SELECT_0 = 0x7A, + RS_ERR_PER_1 = 0x7C, + RS_ERR_PER_0 = 0x7D, + CHIP_ID = 0x7F, + CHAN_STOP_1 = 0x80, + CHAN_STOP_0 = 0x81, + CHAN_STEP_1 = 0x82, + CHAN_STEP_0 = 0x83, + FEC_LOCK_TIME = 0x85, + OFDM_LOCK_TIME = 0x86, + ACQ_DELAY = 0x87, + SCAN_CTL = 0x88, + CLOCK_CTL = 0x89, + CONFIG = 0x8A, + MCLK_RATIO = 0x8B, + GPP_CTL = 0x8C, + ADC_CTL_1 = 0x8E, + ADC_CTL_0 = 0x8F +}; + +/* here we assume 1/6MHz == 166.66kHz stepsize */ +#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ + +#endif /* _MT352_PRIV_ */ diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c new file mode 100644 index 0000000000..4ebbcf05cc --- /dev/null +++ b/drivers/media/dvb-frontends/mxl5xx.c @@ -0,0 +1,1885 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the MaxLinear MxL5xx family of tuners/demods + * + * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de> + * Marcus Metzler <mocm@metzlerbros.de> + * developed for Digital Devices GmbH + * + * based on code: + * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/vmalloc.h> +#include <asm/div64.h> +#include <asm/unaligned.h> + +#include <media/dvb_frontend.h> +#include "mxl5xx.h" +#include "mxl5xx_regs.h" +#include "mxl5xx_defs.h" + +#define BYTE0(v) ((v >> 0) & 0xff) +#define BYTE1(v) ((v >> 8) & 0xff) +#define BYTE2(v) ((v >> 16) & 0xff) +#define BYTE3(v) ((v >> 24) & 0xff) + +static LIST_HEAD(mxllist); + +struct mxl_base { + struct list_head mxllist; + struct list_head mxls; + + u8 adr; + struct i2c_adapter *i2c; + + u32 count; + u32 type; + u32 sku_type; + u32 chipversion; + u32 clock; + u32 fwversion; + + u8 *ts_map; + u8 can_clkout; + u8 chan_bond; + u8 demod_num; + u8 tuner_num; + + unsigned long next_tune; + + struct mutex i2c_lock; + struct mutex status_lock; + struct mutex tune_lock; + + u8 buf[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN]; + + u32 cmd_size; + u8 cmd_data[MAX_CMD_DATA]; +}; + +struct mxl { + struct list_head mxl; + + struct mxl_base *base; + struct dvb_frontend fe; + struct device *i2cdev; + u32 demod; + u32 tuner; + u32 tuner_in_use; + u8 xbar[3]; + + unsigned long tune_time; +}; + +static void convert_endian(u8 flag, u32 size, u8 *d) +{ + u32 i; + + if (!flag) + return; + for (i = 0; i < (size & ~3); i += 4) { + d[i + 0] ^= d[i + 3]; + d[i + 3] ^= d[i + 0]; + d[i + 0] ^= d[i + 3]; + + d[i + 1] ^= d[i + 2]; + d[i + 2] ^= d[i + 1]; + d[i + 1] ^= d[i + 2]; + } + + switch (size & 3) { + case 0: + case 1: + /* do nothing */ + break; + case 2: + d[i + 0] ^= d[i + 1]; + d[i + 1] ^= d[i + 0]; + d[i + 0] ^= d[i + 1]; + break; + + case 3: + d[i + 0] ^= d[i + 2]; + d[i + 2] ^= d[i + 0]; + d[i + 0] ^= d[i + 2]; + break; + } + +} + +static int i2c_write(struct i2c_adapter *adap, u8 adr, + u8 *data, u32 len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, + .buf = data, .len = len}; + + return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1; +} + +static int i2c_read(struct i2c_adapter *adap, u8 adr, + u8 *data, u32 len) +{ + struct i2c_msg msg = {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = len}; + + return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1; +} + +static int i2cread(struct mxl *state, u8 *data, int len) +{ + return i2c_read(state->base->i2c, state->base->adr, data, len); +} + +static int i2cwrite(struct mxl *state, u8 *data, int len) +{ + return i2c_write(state->base->i2c, state->base->adr, data, len); +} + +static int read_register_unlocked(struct mxl *state, u32 reg, u32 *val) +{ + int stat; + u8 data[MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE] = { + MXL_HYDRA_PLID_REG_READ, 0x04, + GET_BYTE(reg, 0), GET_BYTE(reg, 1), + GET_BYTE(reg, 2), GET_BYTE(reg, 3), + }; + + stat = i2cwrite(state, data, + MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE); + if (stat) + dev_err(state->i2cdev, "i2c read error 1\n"); + if (!stat) + stat = i2cread(state, (u8 *) val, + MXL_HYDRA_REG_SIZE_IN_BYTES); + le32_to_cpus(val); + if (stat) + dev_err(state->i2cdev, "i2c read error 2\n"); + return stat; +} + +#define DMA_I2C_INTERRUPT_ADDR 0x8000011C +#define DMA_INTR_PROT_WR_CMP 0x08 + +static int send_command(struct mxl *state, u32 size, u8 *buf) +{ + int stat; + u32 val, count = 10; + + mutex_lock(&state->base->i2c_lock); + if (state->base->fwversion > 0x02010109) { + read_register_unlocked(state, DMA_I2C_INTERRUPT_ADDR, &val); + if (DMA_INTR_PROT_WR_CMP & val) + dev_info(state->i2cdev, "%s busy\n", __func__); + while ((DMA_INTR_PROT_WR_CMP & val) && --count) { + mutex_unlock(&state->base->i2c_lock); + usleep_range(1000, 2000); + mutex_lock(&state->base->i2c_lock); + read_register_unlocked(state, DMA_I2C_INTERRUPT_ADDR, + &val); + } + if (!count) { + dev_info(state->i2cdev, "%s busy\n", __func__); + mutex_unlock(&state->base->i2c_lock); + return -EBUSY; + } + } + stat = i2cwrite(state, buf, size); + mutex_unlock(&state->base->i2c_lock); + return stat; +} + +static int write_register(struct mxl *state, u32 reg, u32 val) +{ + int stat; + u8 data[MXL_HYDRA_REG_WRITE_LEN] = { + MXL_HYDRA_PLID_REG_WRITE, 0x08, + BYTE0(reg), BYTE1(reg), BYTE2(reg), BYTE3(reg), + BYTE0(val), BYTE1(val), BYTE2(val), BYTE3(val), + }; + mutex_lock(&state->base->i2c_lock); + stat = i2cwrite(state, data, sizeof(data)); + mutex_unlock(&state->base->i2c_lock); + if (stat) + dev_err(state->i2cdev, "i2c write error\n"); + return stat; +} + +static int write_firmware_block(struct mxl *state, + u32 reg, u32 size, u8 *reg_data_ptr) +{ + int stat; + u8 *buf = state->base->buf; + + mutex_lock(&state->base->i2c_lock); + buf[0] = MXL_HYDRA_PLID_REG_WRITE; + buf[1] = size + 4; + buf[2] = GET_BYTE(reg, 0); + buf[3] = GET_BYTE(reg, 1); + buf[4] = GET_BYTE(reg, 2); + buf[5] = GET_BYTE(reg, 3); + memcpy(&buf[6], reg_data_ptr, size); + stat = i2cwrite(state, buf, + MXL_HYDRA_I2C_HDR_SIZE + + MXL_HYDRA_REG_SIZE_IN_BYTES + size); + mutex_unlock(&state->base->i2c_lock); + if (stat) + dev_err(state->i2cdev, "fw block write failed\n"); + return stat; +} + +static int read_register(struct mxl *state, u32 reg, u32 *val) +{ + int stat; + u8 data[MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE] = { + MXL_HYDRA_PLID_REG_READ, 0x04, + GET_BYTE(reg, 0), GET_BYTE(reg, 1), + GET_BYTE(reg, 2), GET_BYTE(reg, 3), + }; + + mutex_lock(&state->base->i2c_lock); + stat = i2cwrite(state, data, + MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE); + if (stat) + dev_err(state->i2cdev, "i2c read error 1\n"); + if (!stat) + stat = i2cread(state, (u8 *) val, + MXL_HYDRA_REG_SIZE_IN_BYTES); + mutex_unlock(&state->base->i2c_lock); + le32_to_cpus(val); + if (stat) + dev_err(state->i2cdev, "i2c read error 2\n"); + return stat; +} + +static int read_register_block(struct mxl *state, u32 reg, u32 size, u8 *data) +{ + int stat; + u8 *buf = state->base->buf; + + mutex_lock(&state->base->i2c_lock); + + buf[0] = MXL_HYDRA_PLID_REG_READ; + buf[1] = size + 4; + buf[2] = GET_BYTE(reg, 0); + buf[3] = GET_BYTE(reg, 1); + buf[4] = GET_BYTE(reg, 2); + buf[5] = GET_BYTE(reg, 3); + stat = i2cwrite(state, buf, + MXL_HYDRA_I2C_HDR_SIZE + MXL_HYDRA_REG_SIZE_IN_BYTES); + if (!stat) { + stat = i2cread(state, data, size); + convert_endian(MXL_ENABLE_BIG_ENDIAN, size, data); + } + mutex_unlock(&state->base->i2c_lock); + return stat; +} + +static int read_by_mnemonic(struct mxl *state, + u32 reg, u8 lsbloc, u8 numofbits, u32 *val) +{ + u32 data = 0, mask = 0; + int stat; + + stat = read_register(state, reg, &data); + if (stat) + return stat; + mask = MXL_GET_REG_MASK_32(lsbloc, numofbits); + data &= mask; + data >>= lsbloc; + *val = data; + return 0; +} + + +static int update_by_mnemonic(struct mxl *state, + u32 reg, u8 lsbloc, u8 numofbits, u32 val) +{ + u32 data, mask; + int stat; + + stat = read_register(state, reg, &data); + if (stat) + return stat; + mask = MXL_GET_REG_MASK_32(lsbloc, numofbits); + data = (data & ~mask) | ((val << lsbloc) & mask); + stat = write_register(state, reg, data); + return stat; +} + +static int firmware_is_alive(struct mxl *state) +{ + u32 hb0, hb1; + + if (read_register(state, HYDRA_HEAR_BEAT, &hb0)) + return 0; + msleep(20); + if (read_register(state, HYDRA_HEAR_BEAT, &hb1)) + return 0; + if (hb1 == hb0) + return 0; + return 1; +} + +static int init(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + /* init fe stats */ + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.len = 1; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.len = 1; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.len = 1; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +} + +static void release(struct dvb_frontend *fe) +{ + struct mxl *state = fe->demodulator_priv; + + list_del(&state->mxl); + /* Release one frontend, two more shall take its place! */ + state->base->count--; + if (state->base->count == 0) { + list_del(&state->base->mxllist); + kfree(state->base); + } + kfree(state); +} + +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static u32 gold2root(u32 gold) +{ + u32 x, g, tmp = gold; + + if (tmp >= 0x3ffff) + tmp = 0; + for (g = 0, x = 1; g < tmp; g++) + x = (((x ^ (x >> 7)) & 1) << 17) | (x >> 1); + return x; +} + +static int cfg_scrambler(struct mxl *state, u32 gold) +{ + u32 root; + u8 buf[26] = { + MXL_HYDRA_PLID_CMD_WRITE, 24, + 0, MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD, 0, 0, + state->demod, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + }; + + root = gold2root(gold); + + buf[25] = (root >> 24) & 0xff; + buf[24] = (root >> 16) & 0xff; + buf[23] = (root >> 8) & 0xff; + buf[22] = root & 0xff; + + return send_command(state, sizeof(buf), buf); +} + +static int cfg_demod_abort_tune(struct mxl *state) +{ + struct MXL_HYDRA_DEMOD_ABORT_TUNE_T abort_tune_cmd; + u8 cmd_size = sizeof(abort_tune_cmd); + u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN]; + + abort_tune_cmd.demod_id = state->demod; + BUILD_HYDRA_CMD(MXL_HYDRA_ABORT_TUNE_CMD, MXL_CMD_WRITE, + cmd_size, &abort_tune_cmd, cmd_buff); + return send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE, + &cmd_buff[0]); +} + +static int send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + /*struct mxl *state = fe->demodulator_priv;*/ + + return 0; /*CfgDemodAbortTune(state);*/ +} + +static int set_parameters(struct dvb_frontend *fe) +{ + struct mxl *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct MXL_HYDRA_DEMOD_PARAM_T demod_chan_cfg; + u8 cmd_size = sizeof(demod_chan_cfg); + u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN]; + u32 srange = 10; + int stat; + + if (p->frequency < 950000 || p->frequency > 2150000) + return -EINVAL; + if (p->symbol_rate < 1000000 || p->symbol_rate > 45000000) + return -EINVAL; + + /* CfgDemodAbortTune(state); */ + + switch (p->delivery_system) { + case SYS_DSS: + demod_chan_cfg.standard = MXL_HYDRA_DSS; + demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO; + break; + case SYS_DVBS: + srange = p->symbol_rate / 1000000; + if (srange > 10) + srange = 10; + demod_chan_cfg.standard = MXL_HYDRA_DVBS; + demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_0_35; + demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_QPSK; + demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_OFF; + break; + case SYS_DVBS2: + demod_chan_cfg.standard = MXL_HYDRA_DVBS2; + demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO; + demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_AUTO; + demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_AUTO; + cfg_scrambler(state, p->scrambling_sequence_index); + break; + default: + return -EINVAL; + } + demod_chan_cfg.tuner_index = state->tuner; + demod_chan_cfg.demod_index = state->demod; + demod_chan_cfg.frequency_in_hz = p->frequency * 1000; + demod_chan_cfg.symbol_rate_in_hz = p->symbol_rate; + demod_chan_cfg.max_carrier_offset_in_mhz = srange; + demod_chan_cfg.spectrum_inversion = MXL_HYDRA_SPECTRUM_AUTO; + demod_chan_cfg.fec_code_rate = MXL_HYDRA_FEC_AUTO; + + mutex_lock(&state->base->tune_lock); + if (time_after(jiffies + msecs_to_jiffies(200), + state->base->next_tune)) + while (time_before(jiffies, state->base->next_tune)) + usleep_range(10000, 11000); + state->base->next_tune = jiffies + msecs_to_jiffies(100); + state->tuner_in_use = state->tuner; + BUILD_HYDRA_CMD(MXL_HYDRA_DEMOD_SET_PARAM_CMD, MXL_CMD_WRITE, + cmd_size, &demod_chan_cfg, cmd_buff); + stat = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE, + &cmd_buff[0]); + mutex_unlock(&state->base->tune_lock); + return stat; +} + +static int enable_tuner(struct mxl *state, u32 tuner, u32 enable); + +static int sleep(struct dvb_frontend *fe) +{ + struct mxl *state = fe->demodulator_priv; + struct mxl *p; + + cfg_demod_abort_tune(state); + if (state->tuner_in_use != 0xffffffff) { + mutex_lock(&state->base->tune_lock); + state->tuner_in_use = 0xffffffff; + list_for_each_entry(p, &state->base->mxls, mxl) { + if (p->tuner_in_use == state->tuner) + break; + } + if (&p->mxl == &state->base->mxls) + enable_tuner(state, state->tuner, 0); + mutex_unlock(&state->base->tune_lock); + } + return 0; +} + +static int read_snr(struct dvb_frontend *fe) +{ + struct mxl *state = fe->demodulator_priv; + int stat; + u32 reg_data = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + mutex_lock(&state->base->status_lock); + HYDRA_DEMOD_STATUS_LOCK(state, state->demod); + stat = read_register(state, (HYDRA_DMD_SNR_ADDR_OFFSET + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + ®_data); + HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod); + mutex_unlock(&state->base->status_lock); + + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (s16)reg_data * 10; + + return stat; +} + +static int read_ber(struct dvb_frontend *fe) +{ + struct mxl *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 reg[8]; + + mutex_lock(&state->base->status_lock); + HYDRA_DEMOD_STATUS_LOCK(state, state->demod); + read_register_block(state, + (HYDRA_DMD_DVBS_1ST_CORR_RS_ERRORS_ADDR_OFFSET + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + (4 * sizeof(u32)), + (u8 *) ®[0]); + HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod); + + switch (p->delivery_system) { + case SYS_DSS: + case SYS_DVBS: + p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_error.stat[0].uvalue = reg[2]; + p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_count.stat[0].uvalue = reg[3]; + break; + default: + break; + } + + read_register_block(state, + (HYDRA_DMD_DVBS2_CRC_ERRORS_ADDR_OFFSET + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + (7 * sizeof(u32)), + (u8 *) ®[0]); + + switch (p->delivery_system) { + case SYS_DSS: + case SYS_DVBS: + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue = reg[5]; + p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_count.stat[0].uvalue = reg[6]; + break; + case SYS_DVBS2: + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue = reg[1]; + p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_count.stat[0].uvalue = reg[2]; + break; + default: + break; + } + + mutex_unlock(&state->base->status_lock); + + return 0; +} + +static int read_signal_strength(struct dvb_frontend *fe) +{ + struct mxl *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int stat; + u32 reg_data = 0; + + mutex_lock(&state->base->status_lock); + HYDRA_DEMOD_STATUS_LOCK(state, state->demod); + stat = read_register(state, (HYDRA_DMD_STATUS_INPUT_POWER_ADDR + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + ®_data); + HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod); + mutex_unlock(&state->base->status_lock); + + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].svalue = (s16) reg_data * 10; /* fix scale */ + + return stat; +} + +static int read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct mxl *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 reg_data = 0; + + mutex_lock(&state->base->status_lock); + HYDRA_DEMOD_STATUS_LOCK(state, state->demod); + read_register(state, (HYDRA_DMD_LOCK_STATUS_ADDR_OFFSET + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + ®_data); + HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod); + mutex_unlock(&state->base->status_lock); + + *status = (reg_data == 1) ? 0x1f : 0; + + /* signal statistics */ + + /* signal strength is always available */ + read_signal_strength(fe); + + if (*status & FE_HAS_CARRIER) + read_snr(fe); + else + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SYNC) + read_ber(fe); + else { + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +} + +static int tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, + unsigned int *delay, enum fe_status *status) +{ + struct mxl *state = fe->demodulator_priv; + int r = 0; + + *delay = HZ / 2; + if (re_tune) { + r = set_parameters(fe); + if (r) + return r; + state->tune_time = jiffies; + } + + return read_status(fe, status); +} + +static enum fe_code_rate conv_fec(enum MXL_HYDRA_FEC_E fec) +{ + enum fe_code_rate fec2fec[11] = { + FEC_NONE, FEC_1_2, FEC_3_5, FEC_2_3, + FEC_3_4, FEC_4_5, FEC_5_6, FEC_6_7, + FEC_7_8, FEC_8_9, FEC_9_10 + }; + + if (fec > MXL_HYDRA_FEC_9_10) + return FEC_NONE; + return fec2fec[fec]; +} + +static int get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct mxl *state = fe->demodulator_priv; + u32 reg_data[MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE]; + u32 freq; + + mutex_lock(&state->base->status_lock); + HYDRA_DEMOD_STATUS_LOCK(state, state->demod); + read_register_block(state, + (HYDRA_DMD_STANDARD_ADDR_OFFSET + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + (MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE * 4), /* 25 * 4 bytes */ + (u8 *) ®_data[0]); + /* read demod channel parameters */ + read_register_block(state, + (HYDRA_DMD_STATUS_CENTER_FREQ_IN_KHZ_ADDR + + HYDRA_DMD_STATUS_OFFSET(state->demod)), + (4), /* 4 bytes */ + (u8 *) &freq); + HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod); + mutex_unlock(&state->base->status_lock); + + dev_dbg(state->i2cdev, "freq=%u delsys=%u srate=%u\n", + freq * 1000, reg_data[DMD_STANDARD_ADDR], + reg_data[DMD_SYMBOL_RATE_ADDR]); + p->symbol_rate = reg_data[DMD_SYMBOL_RATE_ADDR]; + p->frequency = freq; + /* + * p->delivery_system = + * (MXL_HYDRA_BCAST_STD_E) regData[DMD_STANDARD_ADDR]; + * p->inversion = + * (MXL_HYDRA_SPECTRUM_E) regData[DMD_SPECTRUM_INVERSION_ADDR]; + * freqSearchRangeKHz = + * (regData[DMD_FREQ_SEARCH_RANGE_IN_KHZ_ADDR]); + */ + + p->fec_inner = conv_fec(reg_data[DMD_FEC_CODE_RATE_ADDR]); + switch (p->delivery_system) { + case SYS_DSS: + break; + case SYS_DVBS2: + switch ((enum MXL_HYDRA_PILOTS_E) + reg_data[DMD_DVBS2_PILOT_ON_OFF_ADDR]) { + case MXL_HYDRA_PILOTS_OFF: + p->pilot = PILOT_OFF; + break; + case MXL_HYDRA_PILOTS_ON: + p->pilot = PILOT_ON; + break; + default: + break; + } + fallthrough; + case SYS_DVBS: + switch ((enum MXL_HYDRA_MODULATION_E) + reg_data[DMD_MODULATION_SCHEME_ADDR]) { + case MXL_HYDRA_MOD_QPSK: + p->modulation = QPSK; + break; + case MXL_HYDRA_MOD_8PSK: + p->modulation = PSK_8; + break; + default: + break; + } + switch ((enum MXL_HYDRA_ROLLOFF_E) + reg_data[DMD_SPECTRUM_ROLL_OFF_ADDR]) { + case MXL_HYDRA_ROLLOFF_0_20: + p->rolloff = ROLLOFF_20; + break; + case MXL_HYDRA_ROLLOFF_0_35: + p->rolloff = ROLLOFF_35; + break; + case MXL_HYDRA_ROLLOFF_0_25: + p->rolloff = ROLLOFF_25; + break; + default: + break; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int set_input(struct dvb_frontend *fe, int input) +{ + struct mxl *state = fe->demodulator_priv; + + state->tuner = input; + return 0; +} + +static const struct dvb_frontend_ops mxl_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, + .info = { + .name = "MaxLinear MxL5xx DVB-S/S2 tuner-demodulator", + .frequency_min_hz = 300 * MHz, + .frequency_max_hz = 2350 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION + }, + .init = init, + .release = release, + .get_frontend_algo = get_algo, + .tune = tune, + .read_status = read_status, + .sleep = sleep, + .get_frontend = get_frontend, + .diseqc_send_master_cmd = send_master_cmd, +}; + +static struct mxl_base *match_base(struct i2c_adapter *i2c, u8 adr) +{ + struct mxl_base *p; + + list_for_each_entry(p, &mxllist, mxllist) + if (p->i2c == i2c && p->adr == adr) + return p; + return NULL; +} + +static void cfg_dev_xtal(struct mxl *state, u32 freq, u32 cap, u32 enable) +{ + if (state->base->can_clkout || !enable) + update_by_mnemonic(state, 0x90200054, 23, 1, enable); + + if (freq == 24000000) + write_register(state, HYDRA_CRYSTAL_SETTING, 0); + else + write_register(state, HYDRA_CRYSTAL_SETTING, 1); + + write_register(state, HYDRA_CRYSTAL_CAP, cap); +} + +static u32 get_big_endian(u8 num_of_bits, const u8 buf[]) +{ + u32 ret_value = 0; + + switch (num_of_bits) { + case 24: + ret_value = (((u32) buf[0]) << 16) | + (((u32) buf[1]) << 8) | buf[2]; + break; + case 32: + ret_value = (((u32) buf[0]) << 24) | + (((u32) buf[1]) << 16) | + (((u32) buf[2]) << 8) | buf[3]; + break; + default: + break; + } + + return ret_value; +} + +static int write_fw_segment(struct mxl *state, + u32 mem_addr, u32 total_size, u8 *data_ptr) +{ + int status; + u32 data_count = 0; + u32 size = 0; + u32 orig_size = 0; + u8 *w_buf_ptr = NULL; + u32 block_size = ((MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH - + (MXL_HYDRA_I2C_HDR_SIZE + + MXL_HYDRA_REG_SIZE_IN_BYTES)) / 4) * 4; + u8 w_msg_buffer[MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH - + (MXL_HYDRA_I2C_HDR_SIZE + MXL_HYDRA_REG_SIZE_IN_BYTES)]; + + do { + size = orig_size = (((u32)(data_count + block_size)) > total_size) ? + (total_size - data_count) : block_size; + + if (orig_size & 3) + size = (orig_size + 4) & ~3; + w_buf_ptr = &w_msg_buffer[0]; + memset((void *) w_buf_ptr, 0, size); + memcpy((void *) w_buf_ptr, (void *) data_ptr, orig_size); + convert_endian(1, size, w_buf_ptr); + status = write_firmware_block(state, mem_addr, size, w_buf_ptr); + if (status) + return status; + data_count += size; + mem_addr += size; + data_ptr += size; + } while (data_count < total_size); + + return status; +} + +static int do_firmware_download(struct mxl *state, u8 *mbin_buffer_ptr, + u32 mbin_buffer_size) + +{ + int status; + u32 index = 0; + u32 seg_length = 0; + u32 seg_address = 0; + struct MBIN_FILE_T *mbin_ptr = (struct MBIN_FILE_T *)mbin_buffer_ptr; + struct MBIN_SEGMENT_T *segment_ptr; + enum MXL_BOOL_E xcpu_fw_flag = MXL_FALSE; + + if (mbin_ptr->header.id != MBIN_FILE_HEADER_ID) { + dev_err(state->i2cdev, "%s: Invalid file header ID (%c)\n", + __func__, mbin_ptr->header.id); + return -EINVAL; + } + status = write_register(state, FW_DL_SIGN_ADDR, 0); + if (status) + return status; + segment_ptr = (struct MBIN_SEGMENT_T *) (&mbin_ptr->data[0]); + for (index = 0; index < mbin_ptr->header.num_segments; index++) { + if (segment_ptr->header.id != MBIN_SEGMENT_HEADER_ID) { + dev_err(state->i2cdev, "%s: Invalid segment header ID (%c)\n", + __func__, segment_ptr->header.id); + return -EINVAL; + } + seg_length = get_big_endian(24, + &(segment_ptr->header.len24[0])); + seg_address = get_big_endian(32, + &(segment_ptr->header.address[0])); + + if (state->base->type == MXL_HYDRA_DEVICE_568) { + if ((((seg_address & 0x90760000) == 0x90760000) || + ((seg_address & 0x90740000) == 0x90740000)) && + (xcpu_fw_flag == MXL_FALSE)) { + update_by_mnemonic(state, 0x8003003C, 0, 1, 1); + msleep(200); + write_register(state, 0x90720000, 0); + usleep_range(10000, 11000); + xcpu_fw_flag = MXL_TRUE; + } + status = write_fw_segment(state, seg_address, + seg_length, + (u8 *) segment_ptr->data); + } else { + if (((seg_address & 0x90760000) != 0x90760000) && + ((seg_address & 0x90740000) != 0x90740000)) + status = write_fw_segment(state, seg_address, + seg_length, (u8 *) segment_ptr->data); + } + if (status) + return status; + segment_ptr = (struct MBIN_SEGMENT_T *) + &(segment_ptr->data[((seg_length + 3) / 4) * 4]); + } + return status; +} + +static int check_fw(struct mxl *state, u8 *mbin, u32 mbin_len) +{ + struct MBIN_FILE_HEADER_T *fh = (struct MBIN_FILE_HEADER_T *) mbin; + u32 flen = (fh->image_size24[0] << 16) | + (fh->image_size24[1] << 8) | fh->image_size24[2]; + u8 *fw, cs = 0; + u32 i; + + if (fh->id != 'M' || fh->fmt_version != '1' || flen > 0x3FFF0) { + dev_info(state->i2cdev, "Invalid FW Header\n"); + return -1; + } + fw = mbin + sizeof(struct MBIN_FILE_HEADER_T); + for (i = 0; i < flen; i += 1) + cs += fw[i]; + if (cs != fh->image_checksum) { + dev_info(state->i2cdev, "Invalid FW Checksum\n"); + return -1; + } + return 0; +} + +static int firmware_download(struct mxl *state, u8 *mbin, u32 mbin_len) +{ + int status; + u32 reg_data = 0; + struct MXL_HYDRA_SKU_COMMAND_T dev_sku_cfg; + u8 cmd_size = sizeof(struct MXL_HYDRA_SKU_COMMAND_T); + u8 cmd_buff[sizeof(struct MXL_HYDRA_SKU_COMMAND_T) + 6]; + + if (check_fw(state, mbin, mbin_len)) + return -1; + + /* put CPU into reset */ + status = update_by_mnemonic(state, 0x8003003C, 0, 1, 0); + if (status) + return status; + usleep_range(1000, 2000); + + /* Reset TX FIFO's, BBAND, XBAR */ + status = write_register(state, HYDRA_RESET_TRANSPORT_FIFO_REG, + HYDRA_RESET_TRANSPORT_FIFO_DATA); + if (status) + return status; + status = write_register(state, HYDRA_RESET_BBAND_REG, + HYDRA_RESET_BBAND_DATA); + if (status) + return status; + status = write_register(state, HYDRA_RESET_XBAR_REG, + HYDRA_RESET_XBAR_DATA); + if (status) + return status; + + /* Disable clock to Baseband, Wideband, SerDes, + * Alias ext & Transport modules + */ + status = write_register(state, HYDRA_MODULES_CLK_2_REG, + HYDRA_DISABLE_CLK_2); + if (status) + return status; + /* Clear Software & Host interrupt status - (Clear on read) */ + status = read_register(state, HYDRA_PRCM_ROOT_CLK_REG, ®_data); + if (status) + return status; + status = do_firmware_download(state, mbin, mbin_len); + if (status) + return status; + + if (state->base->type == MXL_HYDRA_DEVICE_568) { + usleep_range(10000, 11000); + + /* bring XCPU out of reset */ + status = write_register(state, 0x90720000, 1); + if (status) + return status; + msleep(500); + + /* Enable XCPU UART message processing in MCPU */ + status = write_register(state, 0x9076B510, 1); + if (status) + return status; + } else { + /* Bring CPU out of reset */ + status = update_by_mnemonic(state, 0x8003003C, 0, 1, 1); + if (status) + return status; + /* Wait until FW boots */ + msleep(150); + } + + /* Initialize XPT XBAR */ + status = write_register(state, XPT_DMD0_BASEADDR, 0x76543210); + if (status) + return status; + + if (!firmware_is_alive(state)) + return -1; + + dev_info(state->i2cdev, "Hydra FW alive. Hail!\n"); + + /* sometimes register values are wrong shortly + * after first heart beats + */ + msleep(50); + + dev_sku_cfg.sku_type = state->base->sku_type; + BUILD_HYDRA_CMD(MXL_HYDRA_DEV_CFG_SKU_CMD, MXL_CMD_WRITE, + cmd_size, &dev_sku_cfg, cmd_buff); + status = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE, + &cmd_buff[0]); + + return status; +} + +static int cfg_ts_pad_mux(struct mxl *state, enum MXL_BOOL_E enable_serial_ts) +{ + int status = 0; + u32 pad_mux_value = 0; + + if (enable_serial_ts == MXL_TRUE) { + pad_mux_value = 0; + if ((state->base->type == MXL_HYDRA_DEVICE_541) || + (state->base->type == MXL_HYDRA_DEVICE_541S)) + pad_mux_value = 2; + } else { + if ((state->base->type == MXL_HYDRA_DEVICE_581) || + (state->base->type == MXL_HYDRA_DEVICE_581S)) + pad_mux_value = 2; + else + pad_mux_value = 3; + } + + switch (state->base->type) { + case MXL_HYDRA_DEVICE_561: + case MXL_HYDRA_DEVICE_581: + case MXL_HYDRA_DEVICE_541: + case MXL_HYDRA_DEVICE_541S: + case MXL_HYDRA_DEVICE_561S: + case MXL_HYDRA_DEVICE_581S: + status |= update_by_mnemonic(state, 0x90000170, 24, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000170, 28, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 0, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 4, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 8, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 12, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 16, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 20, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 24, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000174, 28, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000178, 0, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000178, 4, 3, + pad_mux_value); + status |= update_by_mnemonic(state, 0x90000178, 8, 3, + pad_mux_value); + break; + + case MXL_HYDRA_DEVICE_544: + case MXL_HYDRA_DEVICE_542: + status |= update_by_mnemonic(state, 0x9000016C, 4, 3, 1); + status |= update_by_mnemonic(state, 0x9000016C, 8, 3, 0); + status |= update_by_mnemonic(state, 0x9000016C, 12, 3, 0); + status |= update_by_mnemonic(state, 0x9000016C, 16, 3, 0); + status |= update_by_mnemonic(state, 0x90000170, 0, 3, 0); + status |= update_by_mnemonic(state, 0x90000178, 12, 3, 1); + status |= update_by_mnemonic(state, 0x90000178, 16, 3, 1); + status |= update_by_mnemonic(state, 0x90000178, 20, 3, 1); + status |= update_by_mnemonic(state, 0x90000178, 24, 3, 1); + status |= update_by_mnemonic(state, 0x9000017C, 0, 3, 1); + status |= update_by_mnemonic(state, 0x9000017C, 4, 3, 1); + if (enable_serial_ts == MXL_ENABLE) { + status |= update_by_mnemonic(state, + 0x90000170, 4, 3, 0); + status |= update_by_mnemonic(state, + 0x90000170, 8, 3, 0); + status |= update_by_mnemonic(state, + 0x90000170, 12, 3, 0); + status |= update_by_mnemonic(state, + 0x90000170, 16, 3, 0); + status |= update_by_mnemonic(state, + 0x90000170, 20, 3, 1); + status |= update_by_mnemonic(state, + 0x90000170, 24, 3, 1); + status |= update_by_mnemonic(state, + 0x90000170, 28, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 0, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 4, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 8, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 12, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 16, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 20, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 24, 3, 2); + status |= update_by_mnemonic(state, + 0x90000174, 28, 3, 2); + status |= update_by_mnemonic(state, + 0x90000178, 0, 3, 2); + status |= update_by_mnemonic(state, + 0x90000178, 4, 3, 2); + status |= update_by_mnemonic(state, + 0x90000178, 8, 3, 2); + } else { + status |= update_by_mnemonic(state, + 0x90000170, 4, 3, 3); + status |= update_by_mnemonic(state, + 0x90000170, 8, 3, 3); + status |= update_by_mnemonic(state, + 0x90000170, 12, 3, 3); + status |= update_by_mnemonic(state, + 0x90000170, 16, 3, 3); + status |= update_by_mnemonic(state, + 0x90000170, 20, 3, 3); + status |= update_by_mnemonic(state, + 0x90000170, 24, 3, 3); + status |= update_by_mnemonic(state, + 0x90000170, 28, 3, 3); + status |= update_by_mnemonic(state, + 0x90000174, 0, 3, 3); + status |= update_by_mnemonic(state, + 0x90000174, 4, 3, 3); + status |= update_by_mnemonic(state, + 0x90000174, 8, 3, 3); + status |= update_by_mnemonic(state, + 0x90000174, 12, 3, 3); + status |= update_by_mnemonic(state, + 0x90000174, 16, 3, 3); + status |= update_by_mnemonic(state, + 0x90000174, 20, 3, 1); + status |= update_by_mnemonic(state, + 0x90000174, 24, 3, 1); + status |= update_by_mnemonic(state, + 0x90000174, 28, 3, 1); + status |= update_by_mnemonic(state, + 0x90000178, 0, 3, 1); + status |= update_by_mnemonic(state, + 0x90000178, 4, 3, 1); + status |= update_by_mnemonic(state, + 0x90000178, 8, 3, 1); + } + break; + + case MXL_HYDRA_DEVICE_568: + if (enable_serial_ts == MXL_FALSE) { + status |= update_by_mnemonic(state, + 0x9000016C, 8, 3, 5); + status |= update_by_mnemonic(state, + 0x9000016C, 12, 3, 5); + status |= update_by_mnemonic(state, + 0x9000016C, 16, 3, 5); + status |= update_by_mnemonic(state, + 0x9000016C, 20, 3, 5); + status |= update_by_mnemonic(state, + 0x9000016C, 24, 3, 5); + status |= update_by_mnemonic(state, + 0x9000016C, 28, 3, 5); + status |= update_by_mnemonic(state, + 0x90000170, 0, 3, 5); + status |= update_by_mnemonic(state, + 0x90000170, 4, 3, 5); + status |= update_by_mnemonic(state, + 0x90000170, 8, 3, 5); + status |= update_by_mnemonic(state, + 0x90000170, 12, 3, 5); + status |= update_by_mnemonic(state, + 0x90000170, 16, 3, 5); + status |= update_by_mnemonic(state, + 0x90000170, 20, 3, 5); + + status |= update_by_mnemonic(state, + 0x90000170, 24, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 0, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 4, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 8, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 12, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 16, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 20, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 24, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 28, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000178, 0, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000178, 4, 3, pad_mux_value); + + status |= update_by_mnemonic(state, + 0x90000178, 8, 3, 5); + status |= update_by_mnemonic(state, + 0x90000178, 12, 3, 5); + status |= update_by_mnemonic(state, + 0x90000178, 16, 3, 5); + status |= update_by_mnemonic(state, + 0x90000178, 20, 3, 5); + status |= update_by_mnemonic(state, + 0x90000178, 24, 3, 5); + status |= update_by_mnemonic(state, + 0x90000178, 28, 3, 5); + status |= update_by_mnemonic(state, + 0x9000017C, 0, 3, 5); + status |= update_by_mnemonic(state, + 0x9000017C, 4, 3, 5); + } else { + status |= update_by_mnemonic(state, + 0x90000170, 4, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 8, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 12, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 16, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 20, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 24, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 28, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 0, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 4, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 8, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 12, 3, pad_mux_value); + } + break; + + + case MXL_HYDRA_DEVICE_584: + default: + status |= update_by_mnemonic(state, + 0x90000170, 4, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 8, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 12, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 16, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 20, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 24, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000170, 28, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 0, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 4, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 8, 3, pad_mux_value); + status |= update_by_mnemonic(state, + 0x90000174, 12, 3, pad_mux_value); + break; + } + return status; +} + +static int set_drive_strength(struct mxl *state, + enum MXL_HYDRA_TS_DRIVE_STRENGTH_E ts_drive_strength) +{ + int stat = 0; + u32 val; + + read_register(state, 0x90000194, &val); + dev_info(state->i2cdev, "DIGIO = %08x\n", val); + dev_info(state->i2cdev, "set drive_strength = %u\n", ts_drive_strength); + + + stat |= update_by_mnemonic(state, 0x90000194, 0, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x90000194, 20, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x90000194, 24, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x90000198, 12, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x90000198, 16, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x90000198, 20, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x90000198, 24, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x9000019C, 0, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x9000019C, 4, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x9000019C, 8, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x9000019C, 24, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x9000019C, 28, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x900001A0, 0, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x900001A0, 4, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x900001A0, 20, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x900001A0, 24, 3, ts_drive_strength); + stat |= update_by_mnemonic(state, 0x900001A0, 28, 3, ts_drive_strength); + + return stat; +} + +static int enable_tuner(struct mxl *state, u32 tuner, u32 enable) +{ + int stat = 0; + struct MXL_HYDRA_TUNER_CMD ctrl_tuner_cmd; + u8 cmd_size = sizeof(ctrl_tuner_cmd); + u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN]; + u32 val, count = 10; + + ctrl_tuner_cmd.tuner_id = tuner; + ctrl_tuner_cmd.enable = enable; + BUILD_HYDRA_CMD(MXL_HYDRA_TUNER_ACTIVATE_CMD, MXL_CMD_WRITE, + cmd_size, &ctrl_tuner_cmd, cmd_buff); + stat = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE, + &cmd_buff[0]); + if (stat) + return stat; + read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val); + while (--count && ((val >> tuner) & 1) != enable) { + msleep(20); + read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val); + } + if (!count) + return -1; + read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val); + dev_dbg(state->i2cdev, "tuner %u ready = %u\n", + tuner, (val >> tuner) & 1); + + return 0; +} + + +static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id, + struct MXL_HYDRA_MPEGOUT_PARAM_T *mpeg_out_param_ptr) +{ + int status = 0; + u32 nco_count_min = 0; + u32 clk_type = 0; + + struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = { + {0x90700010, 8, 1}, {0x90700010, 9, 1}, + {0x90700010, 10, 1}, {0x90700010, 11, 1}, + {0x90700010, 12, 1}, {0x90700010, 13, 1}, + {0x90700010, 14, 1}, {0x90700010, 15, 1} }; + struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = { + {0x90700010, 16, 1}, {0x90700010, 17, 1}, + {0x90700010, 18, 1}, {0x90700010, 19, 1}, + {0x90700010, 20, 1}, {0x90700010, 21, 1}, + {0x90700010, 22, 1}, {0x90700010, 23, 1} }; + struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = { + {0x90700014, 0, 1}, {0x90700014, 1, 1}, + {0x90700014, 2, 1}, {0x90700014, 3, 1}, + {0x90700014, 4, 1}, {0x90700014, 5, 1}, + {0x90700014, 6, 1}, {0x90700014, 7, 1} }; + struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = { + {0x90700018, 0, 3}, {0x90700018, 4, 3}, + {0x90700018, 8, 3}, {0x90700018, 12, 3}, + {0x90700018, 16, 3}, {0x90700018, 20, 3}, + {0x90700018, 24, 3}, {0x90700018, 28, 3} }; + struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = { + {0x9070000C, 16, 1}, {0x9070000C, 17, 1}, + {0x9070000C, 18, 1}, {0x9070000C, 19, 1}, + {0x9070000C, 20, 1}, {0x9070000C, 21, 1}, + {0x9070000C, 22, 1}, {0x9070000C, 23, 1} }; + struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = { + {0x90700010, 0, 1}, {0x90700010, 1, 1}, + {0x90700010, 2, 1}, {0x90700010, 3, 1}, + {0x90700010, 4, 1}, {0x90700010, 5, 1}, + {0x90700010, 6, 1}, {0x90700010, 7, 1} }; + struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = { + {0x9070000C, 0, 1}, {0x9070000C, 1, 1}, + {0x9070000C, 2, 1}, {0x9070000C, 3, 1}, + {0x9070000C, 4, 1}, {0x9070000C, 5, 1}, + {0x9070000C, 6, 1}, {0x9070000C, 7, 1} }; + struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = { + {0x9070000C, 24, 1}, {0x9070000C, 25, 1}, + {0x9070000C, 26, 1}, {0x9070000C, 27, 1}, + {0x9070000C, 28, 1}, {0x9070000C, 29, 1}, + {0x9070000C, 30, 1}, {0x9070000C, 31, 1} }; + struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = { + {0x90700014, 8, 1}, {0x90700014, 9, 1}, + {0x90700014, 10, 1}, {0x90700014, 11, 1}, + {0x90700014, 12, 1}, {0x90700014, 13, 1}, + {0x90700014, 14, 1}, {0x90700014, 15, 1} }; + struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = { + {0x907001D4, 0, 1}, {0x907001D4, 1, 1}, + {0x907001D4, 2, 1}, {0x907001D4, 3, 1}, + {0x907001D4, 4, 1}, {0x907001D4, 5, 1}, + {0x907001D4, 6, 1}, {0x907001D4, 7, 1} }; + struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = { + {0x90700044, 16, 80}, {0x90700044, 16, 81}, + {0x90700044, 16, 82}, {0x90700044, 16, 83}, + {0x90700044, 16, 84}, {0x90700044, 16, 85}, + {0x90700044, 16, 86}, {0x90700044, 16, 87} }; + + demod_id = state->base->ts_map[demod_id]; + + if (mpeg_out_param_ptr->enable == MXL_ENABLE) { + if (mpeg_out_param_ptr->mpeg_mode == + MXL_HYDRA_MPEG_MODE_PARALLEL) { + } else { + cfg_ts_pad_mux(state, MXL_TRUE); + update_by_mnemonic(state, + 0x90700010, 27, 1, MXL_FALSE); + } + } + + nco_count_min = + (u32)(MXL_HYDRA_NCO_CLK / mpeg_out_param_ptr->max_mpeg_clk_rate); + + if (state->base->chipversion >= 2) { + status |= update_by_mnemonic(state, + xpt_nco_clock_rate[demod_id].reg_addr, /* Reg Addr */ + xpt_nco_clock_rate[demod_id].lsb_pos, /* LSB pos */ + xpt_nco_clock_rate[demod_id].num_of_bits, /* Num of bits */ + nco_count_min); /* Data */ + } else + update_by_mnemonic(state, 0x90700044, 16, 8, nco_count_min); + + if (mpeg_out_param_ptr->mpeg_clk_type == MXL_HYDRA_MPEG_CLK_CONTINUOUS) + clk_type = 1; + + if (mpeg_out_param_ptr->mpeg_mode < MXL_HYDRA_MPEG_MODE_PARALLEL) { + status |= update_by_mnemonic(state, + xpt_continuous_clock[demod_id].reg_addr, + xpt_continuous_clock[demod_id].lsb_pos, + xpt_continuous_clock[demod_id].num_of_bits, + clk_type); + } else + update_by_mnemonic(state, 0x907001D4, 8, 1, clk_type); + + status |= update_by_mnemonic(state, + xpt_sync_polarity[demod_id].reg_addr, + xpt_sync_polarity[demod_id].lsb_pos, + xpt_sync_polarity[demod_id].num_of_bits, + mpeg_out_param_ptr->mpeg_sync_pol); + + status |= update_by_mnemonic(state, + xpt_valid_polarity[demod_id].reg_addr, + xpt_valid_polarity[demod_id].lsb_pos, + xpt_valid_polarity[demod_id].num_of_bits, + mpeg_out_param_ptr->mpeg_valid_pol); + + status |= update_by_mnemonic(state, + xpt_clock_polarity[demod_id].reg_addr, + xpt_clock_polarity[demod_id].lsb_pos, + xpt_clock_polarity[demod_id].num_of_bits, + mpeg_out_param_ptr->mpeg_clk_pol); + + status |= update_by_mnemonic(state, + xpt_sync_byte[demod_id].reg_addr, + xpt_sync_byte[demod_id].lsb_pos, + xpt_sync_byte[demod_id].num_of_bits, + mpeg_out_param_ptr->mpeg_sync_pulse_width); + + status |= update_by_mnemonic(state, + xpt_ts_clock_phase[demod_id].reg_addr, + xpt_ts_clock_phase[demod_id].lsb_pos, + xpt_ts_clock_phase[demod_id].num_of_bits, + mpeg_out_param_ptr->mpeg_clk_phase); + + status |= update_by_mnemonic(state, + xpt_lsb_first[demod_id].reg_addr, + xpt_lsb_first[demod_id].lsb_pos, + xpt_lsb_first[demod_id].num_of_bits, + mpeg_out_param_ptr->lsb_or_msb_first); + + switch (mpeg_out_param_ptr->mpeg_error_indication) { + case MXL_HYDRA_MPEG_ERR_REPLACE_SYNC: + status |= update_by_mnemonic(state, + xpt_err_replace_sync[demod_id].reg_addr, + xpt_err_replace_sync[demod_id].lsb_pos, + xpt_err_replace_sync[demod_id].num_of_bits, + MXL_TRUE); + status |= update_by_mnemonic(state, + xpt_err_replace_valid[demod_id].reg_addr, + xpt_err_replace_valid[demod_id].lsb_pos, + xpt_err_replace_valid[demod_id].num_of_bits, + MXL_FALSE); + break; + + case MXL_HYDRA_MPEG_ERR_REPLACE_VALID: + status |= update_by_mnemonic(state, + xpt_err_replace_sync[demod_id].reg_addr, + xpt_err_replace_sync[demod_id].lsb_pos, + xpt_err_replace_sync[demod_id].num_of_bits, + MXL_FALSE); + + status |= update_by_mnemonic(state, + xpt_err_replace_valid[demod_id].reg_addr, + xpt_err_replace_valid[demod_id].lsb_pos, + xpt_err_replace_valid[demod_id].num_of_bits, + MXL_TRUE); + break; + + case MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED: + default: + status |= update_by_mnemonic(state, + xpt_err_replace_sync[demod_id].reg_addr, + xpt_err_replace_sync[demod_id].lsb_pos, + xpt_err_replace_sync[demod_id].num_of_bits, + MXL_FALSE); + + status |= update_by_mnemonic(state, + xpt_err_replace_valid[demod_id].reg_addr, + xpt_err_replace_valid[demod_id].lsb_pos, + xpt_err_replace_valid[demod_id].num_of_bits, + MXL_FALSE); + + break; + + } + + if (mpeg_out_param_ptr->mpeg_mode != MXL_HYDRA_MPEG_MODE_PARALLEL) { + status |= update_by_mnemonic(state, + xpt_enable_output[demod_id].reg_addr, + xpt_enable_output[demod_id].lsb_pos, + xpt_enable_output[demod_id].num_of_bits, + mpeg_out_param_ptr->enable); + } + return status; +} + +static int config_mux(struct mxl *state) +{ + update_by_mnemonic(state, 0x9070000C, 0, 1, 0); + update_by_mnemonic(state, 0x9070000C, 1, 1, 0); + update_by_mnemonic(state, 0x9070000C, 2, 1, 0); + update_by_mnemonic(state, 0x9070000C, 3, 1, 0); + update_by_mnemonic(state, 0x9070000C, 4, 1, 0); + update_by_mnemonic(state, 0x9070000C, 5, 1, 0); + update_by_mnemonic(state, 0x9070000C, 6, 1, 0); + update_by_mnemonic(state, 0x9070000C, 7, 1, 0); + update_by_mnemonic(state, 0x90700008, 0, 2, 1); + update_by_mnemonic(state, 0x90700008, 2, 2, 1); + return 0; +} + +static int load_fw(struct mxl *state, struct mxl5xx_cfg *cfg) +{ + int stat = 0; + u8 *buf; + + if (cfg->fw) + return firmware_download(state, cfg->fw, cfg->fw_len); + + if (!cfg->fw_read) + return -1; + + buf = vmalloc(0x40000); + if (!buf) + return -ENOMEM; + + cfg->fw_read(cfg->fw_priv, buf, 0x40000); + stat = firmware_download(state, buf, 0x40000); + vfree(buf); + + return stat; +} + +static int validate_sku(struct mxl *state) +{ + u32 pad_mux_bond = 0, prcm_chip_id = 0, prcm_so_cid = 0; + int status; + u32 type = state->base->type; + + status = read_by_mnemonic(state, 0x90000190, 0, 3, &pad_mux_bond); + status |= read_by_mnemonic(state, 0x80030000, 0, 12, &prcm_chip_id); + status |= read_by_mnemonic(state, 0x80030004, 24, 8, &prcm_so_cid); + if (status) + return -1; + + dev_info(state->i2cdev, "padMuxBond=%08x, prcmChipId=%08x, prcmSoCId=%08x\n", + pad_mux_bond, prcm_chip_id, prcm_so_cid); + + if (prcm_chip_id != 0x560) { + switch (pad_mux_bond) { + case MXL_HYDRA_SKU_ID_581: + if (type == MXL_HYDRA_DEVICE_581) + return 0; + if (type == MXL_HYDRA_DEVICE_581S) { + state->base->type = MXL_HYDRA_DEVICE_581; + return 0; + } + break; + case MXL_HYDRA_SKU_ID_584: + if (type == MXL_HYDRA_DEVICE_584) + return 0; + break; + case MXL_HYDRA_SKU_ID_544: + if (type == MXL_HYDRA_DEVICE_544) + return 0; + if (type == MXL_HYDRA_DEVICE_542) + return 0; + break; + case MXL_HYDRA_SKU_ID_582: + if (type == MXL_HYDRA_DEVICE_582) + return 0; + break; + default: + return -1; + } + } + return -1; +} + +static int get_fwinfo(struct mxl *state) +{ + int status; + u32 val = 0; + + status = read_by_mnemonic(state, 0x90000190, 0, 3, &val); + if (status) + return status; + dev_info(state->i2cdev, "chipID=%08x\n", val); + + status = read_by_mnemonic(state, 0x80030004, 8, 8, &val); + if (status) + return status; + dev_info(state->i2cdev, "chipVer=%08x\n", val); + + status = read_register(state, HYDRA_FIRMWARE_VERSION, &val); + if (status) + return status; + dev_info(state->i2cdev, "FWVer=%08x\n", val); + + state->base->fwversion = val; + return status; +} + + +static u8 ts_map1_to_1[MXL_HYDRA_DEMOD_MAX] = { + MXL_HYDRA_DEMOD_ID_0, + MXL_HYDRA_DEMOD_ID_1, + MXL_HYDRA_DEMOD_ID_2, + MXL_HYDRA_DEMOD_ID_3, + MXL_HYDRA_DEMOD_ID_4, + MXL_HYDRA_DEMOD_ID_5, + MXL_HYDRA_DEMOD_ID_6, + MXL_HYDRA_DEMOD_ID_7, +}; + +static u8 ts_map54x[MXL_HYDRA_DEMOD_MAX] = { + MXL_HYDRA_DEMOD_ID_2, + MXL_HYDRA_DEMOD_ID_3, + MXL_HYDRA_DEMOD_ID_4, + MXL_HYDRA_DEMOD_ID_5, + MXL_HYDRA_DEMOD_MAX, + MXL_HYDRA_DEMOD_MAX, + MXL_HYDRA_DEMOD_MAX, + MXL_HYDRA_DEMOD_MAX, +}; + +static int probe(struct mxl *state, struct mxl5xx_cfg *cfg) +{ + u32 chipver; + int fw, status, j; + struct MXL_HYDRA_MPEGOUT_PARAM_T mpeg_interface_cfg; + + state->base->ts_map = ts_map1_to_1; + + switch (state->base->type) { + case MXL_HYDRA_DEVICE_581: + case MXL_HYDRA_DEVICE_581S: + state->base->can_clkout = 1; + state->base->demod_num = 8; + state->base->tuner_num = 1; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_581; + break; + case MXL_HYDRA_DEVICE_582: + state->base->can_clkout = 1; + state->base->demod_num = 8; + state->base->tuner_num = 3; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_582; + break; + case MXL_HYDRA_DEVICE_585: + state->base->can_clkout = 0; + state->base->demod_num = 8; + state->base->tuner_num = 4; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_585; + break; + case MXL_HYDRA_DEVICE_544: + state->base->can_clkout = 0; + state->base->demod_num = 4; + state->base->tuner_num = 4; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_544; + state->base->ts_map = ts_map54x; + break; + case MXL_HYDRA_DEVICE_541: + case MXL_HYDRA_DEVICE_541S: + state->base->can_clkout = 0; + state->base->demod_num = 4; + state->base->tuner_num = 1; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_541; + state->base->ts_map = ts_map54x; + break; + case MXL_HYDRA_DEVICE_561: + case MXL_HYDRA_DEVICE_561S: + state->base->can_clkout = 0; + state->base->demod_num = 6; + state->base->tuner_num = 1; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_561; + break; + case MXL_HYDRA_DEVICE_568: + state->base->can_clkout = 0; + state->base->demod_num = 8; + state->base->tuner_num = 1; + state->base->chan_bond = 1; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_568; + break; + case MXL_HYDRA_DEVICE_542: + state->base->can_clkout = 1; + state->base->demod_num = 4; + state->base->tuner_num = 3; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_542; + state->base->ts_map = ts_map54x; + break; + case MXL_HYDRA_DEVICE_TEST: + case MXL_HYDRA_DEVICE_584: + default: + state->base->can_clkout = 0; + state->base->demod_num = 8; + state->base->tuner_num = 4; + state->base->sku_type = MXL_HYDRA_SKU_TYPE_584; + break; + } + + status = validate_sku(state); + if (status) + return status; + + update_by_mnemonic(state, 0x80030014, 9, 1, 1); + update_by_mnemonic(state, 0x8003003C, 12, 1, 1); + status = read_by_mnemonic(state, 0x80030000, 12, 4, &chipver); + if (status) + state->base->chipversion = 0; + else + state->base->chipversion = (chipver == 2) ? 2 : 1; + dev_info(state->i2cdev, "Hydra chip version %u\n", + state->base->chipversion); + + cfg_dev_xtal(state, cfg->clk, cfg->cap, 0); + + fw = firmware_is_alive(state); + if (!fw) { + status = load_fw(state, cfg); + if (status) + return status; + } + get_fwinfo(state); + + config_mux(state); + mpeg_interface_cfg.enable = MXL_ENABLE; + mpeg_interface_cfg.lsb_or_msb_first = MXL_HYDRA_MPEG_SERIAL_MSB_1ST; + /* supports only (0-104&139)MHz */ + if (cfg->ts_clk) + mpeg_interface_cfg.max_mpeg_clk_rate = cfg->ts_clk; + else + mpeg_interface_cfg.max_mpeg_clk_rate = 69; /* 139; */ + mpeg_interface_cfg.mpeg_clk_phase = MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_0_DEG; + mpeg_interface_cfg.mpeg_clk_pol = MXL_HYDRA_MPEG_CLK_IN_PHASE; + /* MXL_HYDRA_MPEG_CLK_GAPPED; */ + mpeg_interface_cfg.mpeg_clk_type = MXL_HYDRA_MPEG_CLK_CONTINUOUS; + mpeg_interface_cfg.mpeg_error_indication = + MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED; + mpeg_interface_cfg.mpeg_mode = MXL_HYDRA_MPEG_MODE_SERIAL_3_WIRE; + mpeg_interface_cfg.mpeg_sync_pol = MXL_HYDRA_MPEG_ACTIVE_HIGH; + mpeg_interface_cfg.mpeg_sync_pulse_width = MXL_HYDRA_MPEG_SYNC_WIDTH_BIT; + mpeg_interface_cfg.mpeg_valid_pol = MXL_HYDRA_MPEG_ACTIVE_HIGH; + + for (j = 0; j < state->base->demod_num; j++) { + status = config_ts(state, (enum MXL_HYDRA_DEMOD_ID_E) j, + &mpeg_interface_cfg); + if (status) + return status; + } + set_drive_strength(state, 1); + return 0; +} + +struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c, + struct mxl5xx_cfg *cfg, u32 demod, u32 tuner, + int (**fn_set_input)(struct dvb_frontend *, int)) +{ + struct mxl *state; + struct mxl_base *base; + + state = kzalloc(sizeof(struct mxl), GFP_KERNEL); + if (!state) + return NULL; + + state->demod = demod; + state->tuner = tuner; + state->tuner_in_use = 0xffffffff; + state->i2cdev = &i2c->dev; + + base = match_base(i2c, cfg->adr); + if (base) { + base->count++; + if (base->count > base->demod_num) + goto fail; + state->base = base; + } else { + base = kzalloc(sizeof(struct mxl_base), GFP_KERNEL); + if (!base) + goto fail; + base->i2c = i2c; + base->adr = cfg->adr; + base->type = cfg->type; + base->count = 1; + mutex_init(&base->i2c_lock); + mutex_init(&base->status_lock); + mutex_init(&base->tune_lock); + INIT_LIST_HEAD(&base->mxls); + + state->base = base; + if (probe(state, cfg) < 0) { + kfree(base); + goto fail; + } + list_add(&base->mxllist, &mxllist); + } + state->fe.ops = mxl_ops; + state->xbar[0] = 4; + state->xbar[1] = demod; + state->xbar[2] = 8; + state->fe.demodulator_priv = state; + *fn_set_input = set_input; + + list_add(&state->mxl, &base->mxls); + return &state->fe; + +fail: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(mxl5xx_attach); + +MODULE_DESCRIPTION("MaxLinear MxL5xx DVB-S/S2 tuner-demodulator driver"); +MODULE_AUTHOR("Ralph and Marcus Metzler, Metzler Brothers Systementwicklung GbR"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/mxl5xx.h b/drivers/media/dvb-frontends/mxl5xx.h new file mode 100644 index 0000000000..139e16b2ec --- /dev/null +++ b/drivers/media/dvb-frontends/mxl5xx.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL5xx family of tuners/demods + * + * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de> + * Marcus Metzler <mocm@metzlerbros.de> + * developed for Digital Devices GmbH + * + * based on code: + * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + */ + +#ifndef _MXL5XX_H_ +#define _MXL5XX_H_ + +#include <linux/types.h> +#include <linux/i2c.h> + +#include <media/dvb_frontend.h> + +struct mxl5xx_cfg { + u8 adr; + u8 type; + u32 cap; + u32 clk; + u32 ts_clk; + + u8 *fw; + u32 fw_len; + + int (*fw_read)(void *priv, u8 *buf, u32 len); + void *fw_priv; +}; + +#if IS_REACHABLE(CONFIG_DVB_MXL5XX) + +extern struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c, + struct mxl5xx_cfg *cfg, u32 demod, u32 tuner, + int (**fn_set_input)(struct dvb_frontend *, int)); + +#else + +static inline struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c, + struct mxl5xx_cfg *cfg, u32 demod, u32 tuner, + int (**fn_set_input)(struct dvb_frontend *, int)) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_MXL5XX */ + +#endif /* _MXL5XX_H_ */ diff --git a/drivers/media/dvb-frontends/mxl5xx_defs.h b/drivers/media/dvb-frontends/mxl5xx_defs.h new file mode 100644 index 0000000000..097271f737 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl5xx_defs.h @@ -0,0 +1,728 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Defines for the Maxlinear MX58x family of tuners/demods + * + * Copyright (C) 2014 Digital Devices GmbH + * + * based on code: + * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + */ + +enum MXL_BOOL_E { + MXL_DISABLE = 0, + MXL_ENABLE = 1, + + MXL_FALSE = 0, + MXL_TRUE = 1, + + MXL_INVALID = 0, + MXL_VALID = 1, + + MXL_NO = 0, + MXL_YES = 1, + + MXL_OFF = 0, + MXL_ON = 1 +}; + +/* Firmware-Host Command IDs */ +enum MXL_HYDRA_HOST_CMD_ID_E { + /* --Device command IDs-- */ + MXL_HYDRA_DEV_NO_OP_CMD = 0, /* No OP */ + + MXL_HYDRA_DEV_SET_POWER_MODE_CMD = 1, + MXL_HYDRA_DEV_SET_OVERWRITE_DEF_CMD = 2, + + /* Host-used CMD, not used by firmware */ + MXL_HYDRA_DEV_FIRMWARE_DOWNLOAD_CMD = 3, + + /* Additional CONTROL types from DTV */ + MXL_HYDRA_DEV_SET_BROADCAST_PID_STB_ID_CMD = 4, + MXL_HYDRA_DEV_GET_PMM_SLEEP_CMD = 5, + + /* --Tuner command IDs-- */ + MXL_HYDRA_TUNER_TUNE_CMD = 6, + MXL_HYDRA_TUNER_GET_STATUS_CMD = 7, + + /* --Demod command IDs-- */ + MXL_HYDRA_DEMOD_SET_PARAM_CMD = 8, + MXL_HYDRA_DEMOD_GET_STATUS_CMD = 9, + + MXL_HYDRA_DEMOD_RESET_FEC_COUNTER_CMD = 10, + + MXL_HYDRA_DEMOD_SET_PKT_NUM_CMD = 11, + + MXL_HYDRA_DEMOD_SET_IQ_SOURCE_CMD = 12, + MXL_HYDRA_DEMOD_GET_IQ_DATA_CMD = 13, + + MXL_HYDRA_DEMOD_GET_M68HC05_VER_CMD = 14, + + MXL_HYDRA_DEMOD_SET_ERROR_COUNTER_MODE_CMD = 15, + + /* --- ABORT channel tune */ + MXL_HYDRA_ABORT_TUNE_CMD = 16, /* Abort current tune command. */ + + /* --SWM/FSK command IDs-- */ + MXL_HYDRA_FSK_RESET_CMD = 17, + MXL_HYDRA_FSK_MSG_CMD = 18, + MXL_HYDRA_FSK_SET_OP_MODE_CMD = 19, + + /* --DiSeqC command IDs-- */ + MXL_HYDRA_DISEQC_MSG_CMD = 20, + MXL_HYDRA_DISEQC_COPY_MSG_TO_MAILBOX = 21, + MXL_HYDRA_DISEQC_CFG_MSG_CMD = 22, + + /* --- FFT Debug Command IDs-- */ + MXL_HYDRA_REQ_FFT_SPECTRUM_CMD = 23, + + /* -- Demod scramblle code */ + MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD = 24, + + /* ---For host to know how many commands in total */ + MXL_HYDRA_LAST_HOST_CMD = 25, + + MXL_HYDRA_DEMOD_INTR_TYPE_CMD = 47, + MXL_HYDRA_DEV_INTR_CLEAR_CMD = 48, + MXL_HYDRA_TUNER_SPECTRUM_REQ_CMD = 53, + MXL_HYDRA_TUNER_ACTIVATE_CMD = 55, + MXL_HYDRA_DEV_CFG_POWER_MODE_CMD = 56, + MXL_HYDRA_DEV_XTAL_CAP_CMD = 57, + MXL_HYDRA_DEV_CFG_SKU_CMD = 58, + MXL_HYDRA_TUNER_SPECTRUM_MIN_GAIN_CMD = 59, + MXL_HYDRA_DISEQC_CONT_TONE_CFG = 60, + MXL_HYDRA_DEV_RF_WAKE_UP_CMD = 61, + MXL_HYDRA_DEMOD_CFG_EQ_CTRL_PARAM_CMD = 62, + MXL_HYDRA_DEMOD_FREQ_OFFSET_SEARCH_RANGE_CMD = 63, + MXL_HYDRA_DEV_REQ_PWR_FROM_ADCRSSI_CMD = 64, + + MXL_XCPU_PID_FLT_CFG_CMD = 65, + MXL_XCPU_SHMEM_TEST_CMD = 66, + MXL_XCPU_ABORT_TUNE_CMD = 67, + MXL_XCPU_CHAN_TUNE_CMD = 68, + MXL_XCPU_FLT_BOND_HDRS_CMD = 69, + + MXL_HYDRA_DEV_BROADCAST_WAKE_UP_CMD = 70, + MXL_HYDRA_FSK_CFG_FSK_FREQ_CMD = 71, + MXL_HYDRA_FSK_POWER_DOWN_CMD = 72, + MXL_XCPU_CLEAR_CB_STATS_CMD = 73, + MXL_XCPU_CHAN_BOND_RESTART_CMD = 74 +}; + +#define MXL_ENABLE_BIG_ENDIAN (0) + +#define MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH 248 + +#define MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN (248) + +#define MXL_HYDRA_CAP_MIN 10 +#define MXL_HYDRA_CAP_MAX 33 + +#define MXL_HYDRA_PLID_REG_READ 0xFB /* Read register PLID */ +#define MXL_HYDRA_PLID_REG_WRITE 0xFC /* Write register PLID */ + +#define MXL_HYDRA_PLID_CMD_READ 0xFD /* Command Read PLID */ +#define MXL_HYDRA_PLID_CMD_WRITE 0xFE /* Command Write PLID */ + +#define MXL_HYDRA_REG_SIZE_IN_BYTES 4 /* Hydra register size in bytes */ +#define MXL_HYDRA_I2C_HDR_SIZE (2 * sizeof(u8)) /* PLID + LEN(0xFF) */ +#define MXL_HYDRA_CMD_HEADER_SIZE (MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE) + +#define MXL_HYDRA_SKU_ID_581 0 +#define MXL_HYDRA_SKU_ID_584 1 +#define MXL_HYDRA_SKU_ID_585 2 +#define MXL_HYDRA_SKU_ID_544 3 +#define MXL_HYDRA_SKU_ID_561 4 +#define MXL_HYDRA_SKU_ID_582 5 +#define MXL_HYDRA_SKU_ID_568 6 + +/* macro for register write data buffer size + * (PLID + LEN (0xFF) + RegAddr + RegData) + */ +#define MXL_HYDRA_REG_WRITE_LEN (MXL_HYDRA_I2C_HDR_SIZE + (2 * MXL_HYDRA_REG_SIZE_IN_BYTES)) + +/* macro to extract a single byte from 4-byte(32-bit) data */ +#define GET_BYTE(x, n) (((x) >> (8*(n))) & 0xFF) + +#define MAX_CMD_DATA 512 + +#define MXL_GET_REG_MASK_32(lsb_loc, num_of_bits) ((0xFFFFFFFF >> (32 - (num_of_bits))) << (lsb_loc)) + +#define FW_DL_SIGN (0xDEADBEEF) + +#define MBIN_FORMAT_VERSION '1' +#define MBIN_FILE_HEADER_ID 'M' +#define MBIN_SEGMENT_HEADER_ID 'S' +#define MBIN_MAX_FILE_LENGTH (1<<23) + +struct MBIN_FILE_HEADER_T { + u8 id; + u8 fmt_version; + u8 header_len; + u8 num_segments; + u8 entry_address[4]; + u8 image_size24[3]; + u8 image_checksum; + u8 reserved[4]; +}; + +struct MBIN_FILE_T { + struct MBIN_FILE_HEADER_T header; + u8 data[1]; +}; + +struct MBIN_SEGMENT_HEADER_T { + u8 id; + u8 len24[3]; + u8 address[4]; +}; + +struct MBIN_SEGMENT_T { + struct MBIN_SEGMENT_HEADER_T header; + u8 data[1]; +}; + +enum MXL_CMD_TYPE_E { MXL_CMD_WRITE = 0, MXL_CMD_READ }; + +#define BUILD_HYDRA_CMD(cmd_id, req_type, size, data_ptr, cmd_buff) \ + do { \ + cmd_buff[0] = ((req_type == MXL_CMD_WRITE) ? MXL_HYDRA_PLID_CMD_WRITE : MXL_HYDRA_PLID_CMD_READ); \ + cmd_buff[1] = (size > 251) ? 0xff : (u8) (size + 4); \ + cmd_buff[2] = size; \ + cmd_buff[3] = cmd_id; \ + cmd_buff[4] = 0x00; \ + cmd_buff[5] = 0x00; \ + convert_endian(MXL_ENABLE_BIG_ENDIAN, size, (u8 *)data_ptr); \ + memcpy((void *)&cmd_buff[6], data_ptr, size); \ + } while (0) + +struct MXL_REG_FIELD_T { + u32 reg_addr; + u8 lsb_pos; + u8 num_of_bits; +}; + +struct MXL_DEV_CMD_DATA_T { + u32 data_size; + u8 data[MAX_CMD_DATA]; +}; + +enum MXL_HYDRA_SKU_TYPE_E { + MXL_HYDRA_SKU_TYPE_MIN = 0x00, + MXL_HYDRA_SKU_TYPE_581 = 0x00, + MXL_HYDRA_SKU_TYPE_584 = 0x01, + MXL_HYDRA_SKU_TYPE_585 = 0x02, + MXL_HYDRA_SKU_TYPE_544 = 0x03, + MXL_HYDRA_SKU_TYPE_561 = 0x04, + MXL_HYDRA_SKU_TYPE_5XX = 0x05, + MXL_HYDRA_SKU_TYPE_5YY = 0x06, + MXL_HYDRA_SKU_TYPE_511 = 0x07, + MXL_HYDRA_SKU_TYPE_561_DE = 0x08, + MXL_HYDRA_SKU_TYPE_582 = 0x09, + MXL_HYDRA_SKU_TYPE_541 = 0x0A, + MXL_HYDRA_SKU_TYPE_568 = 0x0B, + MXL_HYDRA_SKU_TYPE_542 = 0x0C, + MXL_HYDRA_SKU_TYPE_MAX = 0x0D, +}; + +struct MXL_HYDRA_SKU_COMMAND_T { + enum MXL_HYDRA_SKU_TYPE_E sku_type; +}; + +enum MXL_HYDRA_DEMOD_ID_E { + MXL_HYDRA_DEMOD_ID_0 = 0, + MXL_HYDRA_DEMOD_ID_1, + MXL_HYDRA_DEMOD_ID_2, + MXL_HYDRA_DEMOD_ID_3, + MXL_HYDRA_DEMOD_ID_4, + MXL_HYDRA_DEMOD_ID_5, + MXL_HYDRA_DEMOD_ID_6, + MXL_HYDRA_DEMOD_ID_7, + MXL_HYDRA_DEMOD_MAX +}; + +#define MXL_DEMOD_SCRAMBLE_SEQ_LEN 12 + +#define MAX_STEP_SIZE_24_XTAL_102_05_KHZ 195 +#define MAX_STEP_SIZE_24_XTAL_204_10_KHZ 215 +#define MAX_STEP_SIZE_24_XTAL_306_15_KHZ 203 +#define MAX_STEP_SIZE_24_XTAL_408_20_KHZ 177 + +#define MAX_STEP_SIZE_27_XTAL_102_05_KHZ 195 +#define MAX_STEP_SIZE_27_XTAL_204_10_KHZ 215 +#define MAX_STEP_SIZE_27_XTAL_306_15_KHZ 203 +#define MAX_STEP_SIZE_27_XTAL_408_20_KHZ 177 + +#define MXL_HYDRA_SPECTRUM_MIN_FREQ_KHZ 300000 +#define MXL_HYDRA_SPECTRUM_MAX_FREQ_KHZ 2350000 + +enum MXL_DEMOD_CHAN_PARAMS_OFFSET_E { + DMD_STANDARD_ADDR = 0, + DMD_SPECTRUM_INVERSION_ADDR, + DMD_SPECTRUM_ROLL_OFF_ADDR, + DMD_SYMBOL_RATE_ADDR, + DMD_MODULATION_SCHEME_ADDR, + DMD_FEC_CODE_RATE_ADDR, + DMD_SNR_ADDR, + DMD_FREQ_OFFSET_ADDR, + DMD_CTL_FREQ_OFFSET_ADDR, + DMD_STR_FREQ_OFFSET_ADDR, + DMD_FTL_FREQ_OFFSET_ADDR, + DMD_STR_NBC_SYNC_LOCK_ADDR, + DMD_CYCLE_SLIP_COUNT_ADDR, + DMD_DISPLAY_IQ_ADDR, + DMD_DVBS2_CRC_ERRORS_ADDR, + DMD_DVBS2_PER_COUNT_ADDR, + DMD_DVBS2_PER_WINDOW_ADDR, + DMD_DVBS_CORR_RS_ERRORS_ADDR, + DMD_DVBS_UNCORR_RS_ERRORS_ADDR, + DMD_DVBS_BER_COUNT_ADDR, + DMD_DVBS_BER_WINDOW_ADDR, + DMD_TUNER_ID_ADDR, + DMD_DVBS2_PILOT_ON_OFF_ADDR, + DMD_FREQ_SEARCH_RANGE_IN_KHZ_ADDR, + + MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE, +}; + +enum MXL_HYDRA_TUNER_ID_E { + MXL_HYDRA_TUNER_ID_0 = 0, + MXL_HYDRA_TUNER_ID_1, + MXL_HYDRA_TUNER_ID_2, + MXL_HYDRA_TUNER_ID_3, + MXL_HYDRA_TUNER_MAX +}; + +enum MXL_HYDRA_BCAST_STD_E { + MXL_HYDRA_DSS = 0, + MXL_HYDRA_DVBS, + MXL_HYDRA_DVBS2, +}; + +enum MXL_HYDRA_FEC_E { + MXL_HYDRA_FEC_AUTO = 0, + MXL_HYDRA_FEC_1_2, + MXL_HYDRA_FEC_3_5, + MXL_HYDRA_FEC_2_3, + MXL_HYDRA_FEC_3_4, + MXL_HYDRA_FEC_4_5, + MXL_HYDRA_FEC_5_6, + MXL_HYDRA_FEC_6_7, + MXL_HYDRA_FEC_7_8, + MXL_HYDRA_FEC_8_9, + MXL_HYDRA_FEC_9_10, +}; + +enum MXL_HYDRA_MODULATION_E { + MXL_HYDRA_MOD_AUTO = 0, + MXL_HYDRA_MOD_QPSK, + MXL_HYDRA_MOD_8PSK +}; + +enum MXL_HYDRA_SPECTRUM_E { + MXL_HYDRA_SPECTRUM_AUTO = 0, + MXL_HYDRA_SPECTRUM_INVERTED, + MXL_HYDRA_SPECTRUM_NON_INVERTED, +}; + +enum MXL_HYDRA_ROLLOFF_E { + MXL_HYDRA_ROLLOFF_AUTO = 0, + MXL_HYDRA_ROLLOFF_0_20, + MXL_HYDRA_ROLLOFF_0_25, + MXL_HYDRA_ROLLOFF_0_35 +}; + +enum MXL_HYDRA_PILOTS_E { + MXL_HYDRA_PILOTS_OFF = 0, + MXL_HYDRA_PILOTS_ON, + MXL_HYDRA_PILOTS_AUTO +}; + +enum MXL_HYDRA_CONSTELLATION_SRC_E { + MXL_HYDRA_FORMATTER = 0, + MXL_HYDRA_LEGACY_FEC, + MXL_HYDRA_FREQ_RECOVERY, + MXL_HYDRA_NBC, + MXL_HYDRA_CTL, + MXL_HYDRA_EQ, +}; + +struct MXL_HYDRA_DEMOD_LOCK_T { + int agc_lock; /* AGC lock info */ + int fec_lock; /* Demod FEC block lock info */ +}; + +struct MXL_HYDRA_DEMOD_STATUS_DVBS_T { + u32 rs_errors; /* RS decoder err counter */ + u32 ber_window; /* Ber Windows */ + u32 ber_count; /* BER count */ + u32 ber_window_iter1; /* Ber Windows - post viterbi */ + u32 ber_count_iter1; /* BER count - post viterbi */ +}; + +struct MXL_HYDRA_DEMOD_STATUS_DSS_T { + u32 rs_errors; /* RS decoder err counter */ + u32 ber_window; /* Ber Windows */ + u32 ber_count; /* BER count */ +}; + +struct MXL_HYDRA_DEMOD_STATUS_DVBS2_T { + u32 crc_errors; /* CRC error counter */ + u32 packet_error_count; /* Number of packet errors */ + u32 total_packets; /* Total packets */ +}; + +struct MXL_HYDRA_DEMOD_STATUS_T { + enum MXL_HYDRA_BCAST_STD_E standard_mask; /* Standard DVB-S, DVB-S2 or DSS */ + + union { + struct MXL_HYDRA_DEMOD_STATUS_DVBS_T demod_status_dvbs; /* DVB-S demod status */ + struct MXL_HYDRA_DEMOD_STATUS_DVBS2_T demod_status_dvbs2; /* DVB-S2 demod status */ + struct MXL_HYDRA_DEMOD_STATUS_DSS_T demod_status_dss; /* DSS demod status */ + } u; +}; + +struct MXL_HYDRA_DEMOD_SIG_OFFSET_INFO_T { + s32 carrier_offset_in_hz; /* CRL offset info */ + s32 symbol_offset_in_symbol; /* SRL offset info */ +}; + +struct MXL_HYDRA_DEMOD_SCRAMBLE_INFO_T { + u8 scramble_sequence[MXL_DEMOD_SCRAMBLE_SEQ_LEN]; /* scramble sequence */ + u32 scramble_code; /* scramble gold code */ +}; + +enum MXL_HYDRA_SPECTRUM_STEP_SIZE_E { + MXL_HYDRA_STEP_SIZE_24_XTAL_102_05KHZ, /* 102.05 KHz for 24 MHz XTAL */ + MXL_HYDRA_STEP_SIZE_24_XTAL_204_10KHZ, /* 204.10 KHz for 24 MHz XTAL */ + MXL_HYDRA_STEP_SIZE_24_XTAL_306_15KHZ, /* 306.15 KHz for 24 MHz XTAL */ + MXL_HYDRA_STEP_SIZE_24_XTAL_408_20KHZ, /* 408.20 KHz for 24 MHz XTAL */ + + MXL_HYDRA_STEP_SIZE_27_XTAL_102_05KHZ, /* 102.05 KHz for 27 MHz XTAL */ + MXL_HYDRA_STEP_SIZE_27_XTAL_204_35KHZ, /* 204.35 KHz for 27 MHz XTAL */ + MXL_HYDRA_STEP_SIZE_27_XTAL_306_52KHZ, /* 306.52 KHz for 27 MHz XTAL */ + MXL_HYDRA_STEP_SIZE_27_XTAL_408_69KHZ, /* 408.69 KHz for 27 MHz XTAL */ +}; + +enum MXL_HYDRA_SPECTRUM_RESOLUTION_E { + MXL_HYDRA_SPECTRUM_RESOLUTION_00_1_DB, /* 0.1 dB */ + MXL_HYDRA_SPECTRUM_RESOLUTION_01_0_DB, /* 1.0 dB */ + MXL_HYDRA_SPECTRUM_RESOLUTION_05_0_DB, /* 5.0 dB */ + MXL_HYDRA_SPECTRUM_RESOLUTION_10_0_DB, /* 10 dB */ +}; + +enum MXL_HYDRA_SPECTRUM_ERROR_CODE_E { + MXL_SPECTRUM_NO_ERROR, + MXL_SPECTRUM_INVALID_PARAMETER, + MXL_SPECTRUM_INVALID_STEP_SIZE, + MXL_SPECTRUM_BW_CANNOT_BE_COVERED, + MXL_SPECTRUM_DEMOD_BUSY, + MXL_SPECTRUM_TUNER_NOT_ENABLED, +}; + +struct MXL_HYDRA_SPECTRUM_REQ_T { + u32 tuner_index; /* TUNER Ctrl: one of MXL58x_TUNER_ID_E */ + u32 demod_index; /* DEMOD Ctrl: one of MXL58x_DEMOD_ID_E */ + enum MXL_HYDRA_SPECTRUM_STEP_SIZE_E step_size_in_khz; + u32 starting_freq_ink_hz; + u32 total_steps; + enum MXL_HYDRA_SPECTRUM_RESOLUTION_E spectrum_division; +}; + +enum MXL_HYDRA_SEARCH_FREQ_OFFSET_TYPE_E { + MXL_HYDRA_SEARCH_MAX_OFFSET = 0, /* DMD searches for max freq offset (i.e. 5MHz) */ + MXL_HYDRA_SEARCH_BW_PLUS_ROLLOFF, /* DMD searches for BW + ROLLOFF/2 */ +}; + +struct MXL58X_CFG_FREQ_OFF_SEARCH_RANGE_T { + u32 demod_index; + enum MXL_HYDRA_SEARCH_FREQ_OFFSET_TYPE_E search_type; +}; + +/* there are two slices + * slice0 - TS0, TS1, TS2 & TS3 + * slice1 - TS4, TS5, TS6 & TS7 + */ +#define MXL_HYDRA_TS_SLICE_MAX 2 + +#define MAX_FIXED_PID_NUM 32 + +#define MXL_HYDRA_NCO_CLK 418 /* 418 MHz */ + +#define MXL_HYDRA_MAX_TS_CLOCK 139 /* 139 MHz */ + +#define MXL_HYDRA_TS_FIXED_PID_FILT_SIZE 32 + +#define MXL_HYDRA_SHARED_PID_FILT_SIZE_DEFAULT 33 /* Shared PID filter size in 1-1 mux mode */ +#define MXL_HYDRA_SHARED_PID_FILT_SIZE_2_TO_1 66 /* Shared PID filter size in 2-1 mux mode */ +#define MXL_HYDRA_SHARED_PID_FILT_SIZE_4_TO_1 132 /* Shared PID filter size in 4-1 mux mode */ + +enum MXL_HYDRA_PID_BANK_TYPE_E { + MXL_HYDRA_SOFTWARE_PID_BANK = 0, + MXL_HYDRA_HARDWARE_PID_BANK, +}; + +enum MXL_HYDRA_TS_MUX_MODE_E { + MXL_HYDRA_TS_MUX_PID_REMAP = 0, + MXL_HYDRA_TS_MUX_PREFIX_EXTRA_HEADER = 1, +}; + +enum MXL_HYDRA_TS_MUX_TYPE_E { + MXL_HYDRA_TS_MUX_DISABLE = 0, /* No Mux ( 1 TSIF to 1 TSIF) */ + MXL_HYDRA_TS_MUX_2_TO_1, /* Mux 2 TSIF to 1 TSIF */ + MXL_HYDRA_TS_MUX_4_TO_1, /* Mux 4 TSIF to 1 TSIF */ +}; + +enum MXL_HYDRA_TS_GROUP_E { + MXL_HYDRA_TS_GROUP_0_3 = 0, /* TS group 0 to 3 (TS0, TS1, TS2 & TS3) */ + MXL_HYDRA_TS_GROUP_4_7, /* TS group 0 to 3 (TS4, TS5, TS6 & TS7) */ +}; + +enum MXL_HYDRA_TS_PID_FLT_CTRL_E { + MXL_HYDRA_TS_PIDS_ALLOW_ALL = 0, /* Allow all pids */ + MXL_HYDRA_TS_PIDS_DROP_ALL, /* Drop all pids */ + MXL_HYDRA_TS_INVALIDATE_PID_FILTER, /* Delete current PD filter in the device */ +}; + +enum MXL_HYDRA_TS_PID_TYPE_E { + MXL_HYDRA_TS_PID_FIXED = 0, + MXL_HYDRA_TS_PID_REGULAR, +}; + +struct MXL_HYDRA_TS_PID_T { + u16 original_pid; /* pid from TS */ + u16 remapped_pid; /* remapped pid */ + enum MXL_BOOL_E enable; /* enable or disable pid */ + enum MXL_BOOL_E allow_or_drop; /* allow or drop pid */ + enum MXL_BOOL_E enable_pid_remap; /* enable or disable pid remap */ + u8 bond_id; /* Bond ID in A0 always 0 - Only for 568 Sku */ + u8 dest_id; /* Output port ID for the PID - Only for 568 Sku */ +}; + +struct MXL_HYDRA_TS_MUX_PREFIX_HEADER_T { + enum MXL_BOOL_E enable; + u8 num_byte; + u8 header[12]; +}; + +enum MXL_HYDRA_PID_FILTER_BANK_E { + MXL_HYDRA_PID_BANK_A = 0, + MXL_HYDRA_PID_BANK_B, +}; + +enum MXL_HYDRA_MPEG_DATA_FMT_E { + MXL_HYDRA_MPEG_SERIAL_MSB_1ST = 0, + MXL_HYDRA_MPEG_SERIAL_LSB_1ST, + + MXL_HYDRA_MPEG_SYNC_WIDTH_BIT = 0, + MXL_HYDRA_MPEG_SYNC_WIDTH_BYTE +}; + +enum MXL_HYDRA_MPEG_MODE_E { + MXL_HYDRA_MPEG_MODE_SERIAL_4_WIRE = 0, /* MPEG 4 Wire serial mode */ + MXL_HYDRA_MPEG_MODE_SERIAL_3_WIRE, /* MPEG 3 Wire serial mode */ + MXL_HYDRA_MPEG_MODE_SERIAL_2_WIRE, /* MPEG 2 Wire serial mode */ + MXL_HYDRA_MPEG_MODE_PARALLEL /* MPEG parallel mode - valid only for MxL581 */ +}; + +enum MXL_HYDRA_MPEG_CLK_TYPE_E { + MXL_HYDRA_MPEG_CLK_CONTINUOUS = 0, /* Continuous MPEG clock */ + MXL_HYDRA_MPEG_CLK_GAPPED, /* Gapped (gated) MPEG clock */ +}; + +enum MXL_HYDRA_MPEG_CLK_FMT_E { + MXL_HYDRA_MPEG_ACTIVE_LOW = 0, + MXL_HYDRA_MPEG_ACTIVE_HIGH, + + MXL_HYDRA_MPEG_CLK_NEGATIVE = 0, + MXL_HYDRA_MPEG_CLK_POSITIVE, + + MXL_HYDRA_MPEG_CLK_IN_PHASE = 0, + MXL_HYDRA_MPEG_CLK_INVERTED, +}; + +enum MXL_HYDRA_MPEG_CLK_PHASE_E { + MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_0_DEG = 0, + MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_90_DEG, + MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_180_DEG, + MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_270_DEG +}; + +enum MXL_HYDRA_MPEG_ERR_INDICATION_E { + MXL_HYDRA_MPEG_ERR_REPLACE_SYNC = 0, + MXL_HYDRA_MPEG_ERR_REPLACE_VALID, + MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED +}; + +struct MXL_HYDRA_MPEGOUT_PARAM_T { + int enable; /* Enable or Disable MPEG OUT */ + enum MXL_HYDRA_MPEG_CLK_TYPE_E mpeg_clk_type; /* Continuous or gapped */ + enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_clk_pol; /* MPEG Clk polarity */ + u8 max_mpeg_clk_rate; /* Max MPEG Clk rate (0 - 104 MHz, 139 MHz) */ + enum MXL_HYDRA_MPEG_CLK_PHASE_E mpeg_clk_phase; /* MPEG Clk phase */ + enum MXL_HYDRA_MPEG_DATA_FMT_E lsb_or_msb_first; /* LSB first or MSB first in TS transmission */ + enum MXL_HYDRA_MPEG_DATA_FMT_E mpeg_sync_pulse_width; /* MPEG SYNC pulse width (1-bit or 1-byte) */ + enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_valid_pol; /* MPEG VALID polarity */ + enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_sync_pol; /* MPEG SYNC polarity */ + enum MXL_HYDRA_MPEG_MODE_E mpeg_mode; /* config 4/3/2-wire serial or parallel TS out */ + enum MXL_HYDRA_MPEG_ERR_INDICATION_E mpeg_error_indication; /* Enable or Disable MPEG error indication */ +}; + +enum MXL_HYDRA_EXT_TS_IN_ID_E { + MXL_HYDRA_EXT_TS_IN_0 = 0, + MXL_HYDRA_EXT_TS_IN_1, + MXL_HYDRA_EXT_TS_IN_2, + MXL_HYDRA_EXT_TS_IN_3, + MXL_HYDRA_EXT_TS_IN_MAX +}; + +enum MXL_HYDRA_TS_OUT_ID_E { + MXL_HYDRA_TS_OUT_0 = 0, + MXL_HYDRA_TS_OUT_1, + MXL_HYDRA_TS_OUT_2, + MXL_HYDRA_TS_OUT_3, + MXL_HYDRA_TS_OUT_4, + MXL_HYDRA_TS_OUT_5, + MXL_HYDRA_TS_OUT_6, + MXL_HYDRA_TS_OUT_7, + MXL_HYDRA_TS_OUT_MAX +}; + +enum MXL_HYDRA_TS_DRIVE_STRENGTH_E { + MXL_HYDRA_TS_DRIVE_STRENGTH_1X = 0, + MXL_HYDRA_TS_DRIVE_STRENGTH_2X, + MXL_HYDRA_TS_DRIVE_STRENGTH_3X, + MXL_HYDRA_TS_DRIVE_STRENGTH_4X, + MXL_HYDRA_TS_DRIVE_STRENGTH_5X, + MXL_HYDRA_TS_DRIVE_STRENGTH_6X, + MXL_HYDRA_TS_DRIVE_STRENGTH_7X, + MXL_HYDRA_TS_DRIVE_STRENGTH_8X +}; + +enum MXL_HYDRA_DEVICE_E { + MXL_HYDRA_DEVICE_581 = 0, + MXL_HYDRA_DEVICE_584, + MXL_HYDRA_DEVICE_585, + MXL_HYDRA_DEVICE_544, + MXL_HYDRA_DEVICE_561, + MXL_HYDRA_DEVICE_TEST, + MXL_HYDRA_DEVICE_582, + MXL_HYDRA_DEVICE_541, + MXL_HYDRA_DEVICE_568, + MXL_HYDRA_DEVICE_542, + MXL_HYDRA_DEVICE_541S, + MXL_HYDRA_DEVICE_561S, + MXL_HYDRA_DEVICE_581S, + MXL_HYDRA_DEVICE_MAX +}; + +/* Demod IQ data */ +struct MXL_HYDRA_DEMOD_IQ_SRC_T { + u32 demod_id; + u32 source_of_iq; /* == 0, it means I/Q comes from Formatter + * == 1, Legacy FEC + * == 2, Frequency Recovery + * == 3, NBC + * == 4, CTL + * == 5, EQ + * == 6, FPGA + */ +}; + +struct MXL_HYDRA_DEMOD_ABORT_TUNE_T { + u32 demod_id; +}; + +struct MXL_HYDRA_TUNER_CMD { + u8 tuner_id; + u8 enable; +}; + +/* Demod Para for Channel Tune */ +struct MXL_HYDRA_DEMOD_PARAM_T { + u32 tuner_index; + u32 demod_index; + u32 frequency_in_hz; /* Frequency */ + u32 standard; /* one of MXL_HYDRA_BCAST_STD_E */ + u32 spectrum_inversion; /* Input : Spectrum inversion. */ + u32 roll_off; /* rollOff (alpha) factor */ + u32 symbol_rate_in_hz; /* Symbol rate */ + u32 pilots; /* TRUE = pilots enabled */ + u32 modulation_scheme; /* Input : Modulation Scheme is one of MXL_HYDRA_MODULATION_E */ + u32 fec_code_rate; /* Input : Forward error correction rate. Is one of MXL_HYDRA_FEC_E */ + u32 max_carrier_offset_in_mhz; /* Maximum carrier freq offset in MHz. Same as freqSearchRangeKHz, but in unit of MHz. */ +}; + +struct MXL_HYDRA_DEMOD_SCRAMBLE_CODE_T { + u32 demod_index; + u8 scramble_sequence[12]; /* scramble sequence */ + u32 scramble_code; /* scramble gold code */ +}; + +struct MXL_INTR_CFG_T { + u32 intr_type; + u32 intr_duration_in_nano_secs; + u32 intr_mask; +}; + +struct MXL_HYDRA_POWER_MODE_CMD { + u8 power_mode; /* enumeration values are defined in MXL_HYDRA_PWR_MODE_E (device API.h) */ +}; + +struct MXL_HYDRA_RF_WAKEUP_PARAM_T { + u32 time_interval_in_seconds; /* in seconds */ + u32 tuner_index; + s32 rssi_threshold; +}; + +struct MXL_HYDRA_RF_WAKEUP_CFG_T { + u32 tuner_count; + struct MXL_HYDRA_RF_WAKEUP_PARAM_T params; +}; + +enum MXL_HYDRA_AUX_CTRL_MODE_E { + MXL_HYDRA_AUX_CTRL_MODE_FSK = 0, /* Select FSK controller */ + MXL_HYDRA_AUX_CTRL_MODE_DISEQC, /* Select DiSEqC controller */ +}; + +enum MXL_HYDRA_DISEQC_OPMODE_E { + MXL_HYDRA_DISEQC_ENVELOPE_MODE = 0, + MXL_HYDRA_DISEQC_TONE_MODE, +}; + +enum MXL_HYDRA_DISEQC_VER_E { + MXL_HYDRA_DISEQC_1_X = 0, /* Config DiSEqC 1.x mode */ + MXL_HYDRA_DISEQC_2_X, /* Config DiSEqC 2.x mode */ + MXL_HYDRA_DISEQC_DISABLE /* Disable DiSEqC */ +}; + +enum MXL_HYDRA_DISEQC_CARRIER_FREQ_E { + MXL_HYDRA_DISEQC_CARRIER_FREQ_22KHZ = 0, /* DiSEqC signal frequency of 22 KHz */ + MXL_HYDRA_DISEQC_CARRIER_FREQ_33KHZ, /* DiSEqC signal frequency of 33 KHz */ + MXL_HYDRA_DISEQC_CARRIER_FREQ_44KHZ /* DiSEqC signal frequency of 44 KHz */ +}; + +enum MXL_HYDRA_DISEQC_ID_E { + MXL_HYDRA_DISEQC_ID_0 = 0, + MXL_HYDRA_DISEQC_ID_1, + MXL_HYDRA_DISEQC_ID_2, + MXL_HYDRA_DISEQC_ID_3 +}; + +enum MXL_HYDRA_FSK_OP_MODE_E { + MXL_HYDRA_FSK_CFG_TYPE_39KPBS = 0, /* 39.0kbps */ + MXL_HYDRA_FSK_CFG_TYPE_39_017KPBS, /* 39.017kbps */ + MXL_HYDRA_FSK_CFG_TYPE_115_2KPBS /* 115.2kbps */ +}; + +struct MXL58X_DSQ_OP_MODE_T { + u32 diseqc_id; /* DSQ 0, 1, 2 or 3 */ + u32 op_mode; /* Envelope mode (0) or internal tone mode (1) */ + u32 version; /* 0: 1.0, 1: 1.1, 2: Disable */ + u32 center_freq; /* 0: 22KHz, 1: 33KHz and 2: 44 KHz */ +}; + +struct MXL_HYDRA_DISEQC_CFG_CONT_TONE_T { + u32 diseqc_id; + u32 cont_tone_flag; /* 1: Enable , 0: Disable */ +}; diff --git a/drivers/media/dvb-frontends/mxl5xx_regs.h b/drivers/media/dvb-frontends/mxl5xx_regs.h new file mode 100644 index 0000000000..b38a138470 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl5xx_regs.h @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved + * + * This program may alternatively be licensed under a proprietary license from + * MaxLinear, Inc. + * + */ + +#ifndef __MXL58X_REGISTERS_H__ +#define __MXL58X_REGISTERS_H__ + +#define HYDRA_INTR_STATUS_REG 0x80030008 +#define HYDRA_INTR_MASK_REG 0x8003000C + +#define HYDRA_CRYSTAL_SETTING 0x3FFFC5F0 /* 0 - 24 MHz & 1 - 27 MHz */ +#define HYDRA_CRYSTAL_CAP 0x3FFFEDA4 /* 0 - 24 MHz & 1 - 27 MHz */ + +#define HYDRA_CPU_RESET_REG 0x8003003C +#define HYDRA_CPU_RESET_DATA 0x00000400 + +#define HYDRA_RESET_TRANSPORT_FIFO_REG 0x80030028 +#define HYDRA_RESET_TRANSPORT_FIFO_DATA 0x00000000 + +#define HYDRA_RESET_BBAND_REG 0x80030024 +#define HYDRA_RESET_BBAND_DATA 0x00000000 + +#define HYDRA_RESET_XBAR_REG 0x80030020 +#define HYDRA_RESET_XBAR_DATA 0x00000000 + +#define HYDRA_MODULES_CLK_1_REG 0x80030014 +#define HYDRA_DISABLE_CLK_1 0x00000000 + +#define HYDRA_MODULES_CLK_2_REG 0x8003001C +#define HYDRA_DISABLE_CLK_2 0x0000000B + +#define HYDRA_PRCM_ROOT_CLK_REG 0x80030018 +#define HYDRA_PRCM_ROOT_CLK_DISABLE 0x00000000 + +#define HYDRA_CPU_RESET_CHECK_REG 0x80030008 +#define HYDRA_CPU_RESET_CHECK_OFFSET 0x40000000 /* <bit 30> */ + +#define HYDRA_SKU_ID_REG 0x90000190 + +#define FW_DL_SIGN_ADDR 0x3FFFEAE0 + +/* Register to check if FW is running or not */ +#define HYDRA_HEAR_BEAT 0x3FFFEDDC + +/* Firmware version */ +#define HYDRA_FIRMWARE_VERSION 0x3FFFEDB8 +#define HYDRA_FW_RC_VERSION 0x3FFFCFAC + +/* Firmware patch version */ +#define HYDRA_FIRMWARE_PATCH_VERSION 0x3FFFEDC2 + +/* SOC operating temperature in C */ +#define HYDRA_TEMPARATURE 0x3FFFEDB4 + +/* Demod & Tuner status registers */ +/* Demod 0 status base address */ +#define HYDRA_DEMOD_0_BASE_ADDR 0x3FFFC64C + +/* Tuner 0 status base address */ +#define HYDRA_TUNER_0_BASE_ADDR 0x3FFFCE4C + +#define POWER_FROM_ADCRSSI_READBACK 0x3FFFEB6C + +/* Macros to determine base address of respective demod or tuner */ +#define HYDRA_DMD_STATUS_OFFSET(demodID) ((demodID) * 0x100) +#define HYDRA_TUNER_STATUS_OFFSET(tunerID) ((tunerID) * 0x40) + +/* Demod status address offset from respective demod's base address */ +#define HYDRA_DMD_AGC_DIG_LEVEL_ADDR_OFFSET 0x3FFFC64C +#define HYDRA_DMD_LOCK_STATUS_ADDR_OFFSET 0x3FFFC650 +#define HYDRA_DMD_ACQ_STATUS_ADDR_OFFSET 0x3FFFC654 + +#define HYDRA_DMD_STANDARD_ADDR_OFFSET 0x3FFFC658 +#define HYDRA_DMD_SPECTRUM_INVERSION_ADDR_OFFSET 0x3FFFC65C +#define HYDRA_DMD_SPECTRUM_ROLL_OFF_ADDR_OFFSET 0x3FFFC660 +#define HYDRA_DMD_SYMBOL_RATE_ADDR_OFFSET 0x3FFFC664 +#define HYDRA_DMD_MODULATION_SCHEME_ADDR_OFFSET 0x3FFFC668 +#define HYDRA_DMD_FEC_CODE_RATE_ADDR_OFFSET 0x3FFFC66C + +#define HYDRA_DMD_SNR_ADDR_OFFSET 0x3FFFC670 +#define HYDRA_DMD_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC674 +#define HYDRA_DMD_CTL_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC678 +#define HYDRA_DMD_STR_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC67C +#define HYDRA_DMD_FTL_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC680 +#define HYDRA_DMD_STR_NBC_SYNC_LOCK_ADDR_OFFSET 0x3FFFC684 +#define HYDRA_DMD_CYCLE_SLIP_COUNT_ADDR_OFFSET 0x3FFFC688 + +#define HYDRA_DMD_DISPLAY_I_ADDR_OFFSET 0x3FFFC68C +#define HYDRA_DMD_DISPLAY_Q_ADDR_OFFSET 0x3FFFC68E + +#define HYDRA_DMD_DVBS2_CRC_ERRORS_ADDR_OFFSET 0x3FFFC690 +#define HYDRA_DMD_DVBS2_PER_COUNT_ADDR_OFFSET 0x3FFFC694 +#define HYDRA_DMD_DVBS2_PER_WINDOW_ADDR_OFFSET 0x3FFFC698 + +#define HYDRA_DMD_DVBS_CORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC69C +#define HYDRA_DMD_DVBS_UNCORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC6A0 +#define HYDRA_DMD_DVBS_BER_COUNT_ADDR_OFFSET 0x3FFFC6A4 +#define HYDRA_DMD_DVBS_BER_WINDOW_ADDR_OFFSET 0x3FFFC6A8 + +/* Debug-purpose DVB-S DMD 0 */ +#define HYDRA_DMD_DVBS_1ST_CORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC6C8 /* corrected RS Errors: 1st iteration */ +#define HYDRA_DMD_DVBS_1ST_UNCORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC6CC /* uncorrected RS Errors: 1st iteration */ +#define HYDRA_DMD_DVBS_BER_COUNT_1ST_ADDR_OFFSET 0x3FFFC6D0 +#define HYDRA_DMD_DVBS_BER_WINDOW_1ST_ADDR_OFFSET 0x3FFFC6D4 + +#define HYDRA_DMD_TUNER_ID_ADDR_OFFSET 0x3FFFC6AC +#define HYDRA_DMD_DVBS2_PILOT_ON_OFF_ADDR_OFFSET 0x3FFFC6B0 +#define HYDRA_DMD_FREQ_SEARCH_RANGE_KHZ_ADDR_OFFSET 0x3FFFC6B4 +#define HYDRA_DMD_STATUS_LOCK_ADDR_OFFSET 0x3FFFC6B8 +#define HYDRA_DMD_STATUS_CENTER_FREQ_IN_KHZ_ADDR 0x3FFFC704 +#define HYDRA_DMD_STATUS_INPUT_POWER_ADDR 0x3FFFC708 + +/* DVB-S new scaled_BER_count for a new BER API, see HYDRA-1343 "DVB-S post viterbi information" */ +#define DMD0_STATUS_DVBS_1ST_SCALED_BER_COUNT_ADDR 0x3FFFC710 /* DMD 0: 1st iteration BER count scaled by HYDRA_BER_COUNT_SCALING_FACTOR */ +#define DMD0_STATUS_DVBS_SCALED_BER_COUNT_ADDR 0x3FFFC714 /* DMD 0: 2nd iteration BER count scaled by HYDRA_BER_COUNT_SCALING_FACTOR */ + +#define DMD0_SPECTRUM_MIN_GAIN_STATUS 0x3FFFC73C +#define DMD0_SPECTRUM_MIN_GAIN_WB_SAGC_VALUE 0x3FFFC740 +#define DMD0_SPECTRUM_MIN_GAIN_NB_SAGC_VALUE 0x3FFFC744 + +#define HYDRA_DMD_STATUS_END_ADDR_OFFSET 0x3FFFC748 + +/* Tuner status address offset from respective tuners's base address */ +#define HYDRA_TUNER_DEMOD_ID_ADDR_OFFSET 0x3FFFCE4C +#define HYDRA_TUNER_AGC_LOCK_OFFSET 0x3FFFCE50 +#define HYDRA_TUNER_SPECTRUM_STATUS_OFFSET 0x3FFFCE54 +#define HYDRA_TUNER_SPECTRUM_BIN_SIZE_OFFSET 0x3FFFCE58 +#define HYDRA_TUNER_SPECTRUM_ADDRESS_OFFSET 0x3FFFCE5C +#define HYDRA_TUNER_ENABLE_COMPLETE 0x3FFFEB78 + +#define HYDRA_DEMOD_STATUS_LOCK(devId, demodId) write_register(devId, (HYDRA_DMD_STATUS_LOCK_ADDR_OFFSET + HYDRA_DMD_STATUS_OFFSET(demodId)), MXL_YES) +#define HYDRA_DEMOD_STATUS_UNLOCK(devId, demodId) write_register(devId, (HYDRA_DMD_STATUS_LOCK_ADDR_OFFSET + HYDRA_DMD_STATUS_OFFSET(demodId)), MXL_NO) + +#define HYDRA_VERSION 0x3FFFEDB8 +#define HYDRA_DEMOD0_VERSION 0x3FFFEDBC +#define HYDRA_DEMOD1_VERSION 0x3FFFEDC0 +#define HYDRA_DEMOD2_VERSION 0x3FFFEDC4 +#define HYDRA_DEMOD3_VERSION 0x3FFFEDC8 +#define HYDRA_DEMOD4_VERSION 0x3FFFEDCC +#define HYDRA_DEMOD5_VERSION 0x3FFFEDD0 +#define HYDRA_DEMOD6_VERSION 0x3FFFEDD4 +#define HYDRA_DEMOD7_VERSION 0x3FFFEDD8 +#define HYDRA_HEAR_BEAT 0x3FFFEDDC +#define HYDRA_SKU_MGMT 0x3FFFEBC0 + +#define MXL_HYDRA_FPGA_A_ADDRESS 0x91C00000 +#define MXL_HYDRA_FPGA_B_ADDRESS 0x91D00000 + +/* TS control base address */ +#define HYDRA_TS_CTRL_BASE_ADDR 0x90700000 + +#define MPEG_MUX_MODE_SLICE0_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x08) + +#define MPEG_MUX_MODE_SLICE1_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x08) + +#define PID_BANK_SEL_SLICE0_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x190) +#define PID_BANK_SEL_SLICE1_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1B0) + +#define MPEG_CLK_GATED_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x20) + +#define MPEG_CLK_ALWAYS_ON_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1D4) + +#define HYDRA_REGULAR_PID_BANK_A_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x190) + +#define HYDRA_FIXED_PID_BANK_A_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x190) + +#define HYDRA_REGULAR_PID_BANK_B_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1B0) + +#define HYDRA_FIXED_PID_BANK_B_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1B0) + +#define FIXED_PID_TBL_REG_ADDRESS_0 (HYDRA_TS_CTRL_BASE_ADDR + 0x9000) +#define FIXED_PID_TBL_REG_ADDRESS_1 (HYDRA_TS_CTRL_BASE_ADDR + 0x9100) +#define FIXED_PID_TBL_REG_ADDRESS_2 (HYDRA_TS_CTRL_BASE_ADDR + 0x9200) +#define FIXED_PID_TBL_REG_ADDRESS_3 (HYDRA_TS_CTRL_BASE_ADDR + 0x9300) + +#define FIXED_PID_TBL_REG_ADDRESS_4 (HYDRA_TS_CTRL_BASE_ADDR + 0xB000) +#define FIXED_PID_TBL_REG_ADDRESS_5 (HYDRA_TS_CTRL_BASE_ADDR + 0xB100) +#define FIXED_PID_TBL_REG_ADDRESS_6 (HYDRA_TS_CTRL_BASE_ADDR + 0xB200) +#define FIXED_PID_TBL_REG_ADDRESS_7 (HYDRA_TS_CTRL_BASE_ADDR + 0xB300) + +#define REGULAR_PID_TBL_REG_ADDRESS_0 (HYDRA_TS_CTRL_BASE_ADDR + 0x8000) +#define REGULAR_PID_TBL_REG_ADDRESS_1 (HYDRA_TS_CTRL_BASE_ADDR + 0x8200) +#define REGULAR_PID_TBL_REG_ADDRESS_2 (HYDRA_TS_CTRL_BASE_ADDR + 0x8400) +#define REGULAR_PID_TBL_REG_ADDRESS_3 (HYDRA_TS_CTRL_BASE_ADDR + 0x8600) + +#define REGULAR_PID_TBL_REG_ADDRESS_4 (HYDRA_TS_CTRL_BASE_ADDR + 0xA000) +#define REGULAR_PID_TBL_REG_ADDRESS_5 (HYDRA_TS_CTRL_BASE_ADDR + 0xA200) +#define REGULAR_PID_TBL_REG_ADDRESS_6 (HYDRA_TS_CTRL_BASE_ADDR + 0xA400) +#define REGULAR_PID_TBL_REG_ADDRESS_7 (HYDRA_TS_CTRL_BASE_ADDR + 0xA600) + +/***************************************************************************/ + +#define PAD_MUX_GPIO_00_SYNC_BASEADDR 0x90000188 + + +#define PAD_MUX_UART_RX_C_PINMUX_BASEADDR 0x9000001C + +#define XPT_PACKET_GAP_MIN_BASEADDR 0x90700044 +#define XPT_NCO_COUNT_BASEADDR 0x90700238 + +#define XPT_NCO_COUNT_BASEADDR1 0x9070023C + +/* V2 DigRF status register */ + +#define XPT_PID_BASEADDR 0x90708000 + +#define XPT_PID_REMAP_BASEADDR 0x90708004 + +#define XPT_KNOWN_PID_BASEADDR 0x90709000 + +#define XPT_PID_BASEADDR1 0x9070A000 + +#define XPT_PID_REMAP_BASEADDR1 0x9070A004 + +#define XPT_KNOWN_PID_BASEADDR1 0x9070B000 + +#define XPT_BERT_LOCK_BASEADDR 0x907000B8 + +#define XPT_BERT_BASEADDR 0x907000BC + +#define XPT_BERT_INVERT_BASEADDR 0x907000C0 + +#define XPT_BERT_HEADER_BASEADDR 0x907000C4 + +#define XPT_BERT_BASEADDR1 0x907000C8 + +#define XPT_BERT_BIT_COUNT0_BASEADDR 0x907000CC + +#define XPT_BERT_BIT_COUNT0_BASEADDR1 0x907000D0 + +#define XPT_BERT_BIT_COUNT1_BASEADDR 0x907000D4 + +#define XPT_BERT_BIT_COUNT1_BASEADDR1 0x907000D8 + +#define XPT_BERT_BIT_COUNT2_BASEADDR 0x907000DC + +#define XPT_BERT_BIT_COUNT2_BASEADDR1 0x907000E0 + +#define XPT_BERT_BIT_COUNT3_BASEADDR 0x907000E4 + +#define XPT_BERT_BIT_COUNT3_BASEADDR1 0x907000E8 + +#define XPT_BERT_BIT_COUNT4_BASEADDR 0x907000EC + +#define XPT_BERT_BIT_COUNT4_BASEADDR1 0x907000F0 + +#define XPT_BERT_BIT_COUNT5_BASEADDR 0x907000F4 + +#define XPT_BERT_BIT_COUNT5_BASEADDR1 0x907000F8 + +#define XPT_BERT_BIT_COUNT6_BASEADDR 0x907000FC + +#define XPT_BERT_BIT_COUNT6_BASEADDR1 0x90700100 + +#define XPT_BERT_BIT_COUNT7_BASEADDR 0x90700104 + +#define XPT_BERT_BIT_COUNT7_BASEADDR1 0x90700108 + +#define XPT_BERT_ERR_COUNT0_BASEADDR 0x9070010C + +#define XPT_BERT_ERR_COUNT0_BASEADDR1 0x90700110 + +#define XPT_BERT_ERR_COUNT1_BASEADDR 0x90700114 + +#define XPT_BERT_ERR_COUNT1_BASEADDR1 0x90700118 + +#define XPT_BERT_ERR_COUNT2_BASEADDR 0x9070011C + +#define XPT_BERT_ERR_COUNT2_BASEADDR1 0x90700120 + +#define XPT_BERT_ERR_COUNT3_BASEADDR 0x90700124 + +#define XPT_BERT_ERR_COUNT3_BASEADDR1 0x90700128 + +#define XPT_BERT_ERR_COUNT4_BASEADDR 0x9070012C + +#define XPT_BERT_ERR_COUNT4_BASEADDR1 0x90700130 + +#define XPT_BERT_ERR_COUNT5_BASEADDR 0x90700134 + +#define XPT_BERT_ERR_COUNT5_BASEADDR1 0x90700138 + +#define XPT_BERT_ERR_COUNT6_BASEADDR 0x9070013C + +#define XPT_BERT_ERR_COUNT6_BASEADDR1 0x90700140 + +#define XPT_BERT_ERR_COUNT7_BASEADDR 0x90700144 + +#define XPT_BERT_ERR_COUNT7_BASEADDR1 0x90700148 + +#define XPT_BERT_ERROR_BASEADDR 0x9070014C + +#define XPT_BERT_ANALYZER_BASEADDR 0x90700150 + +#define XPT_BERT_ANALYZER_BASEADDR1 0x90700154 + +#define XPT_BERT_ANALYZER_BASEADDR2 0x90700158 + +#define XPT_BERT_ANALYZER_BASEADDR3 0x9070015C + +#define XPT_BERT_ANALYZER_BASEADDR4 0x90700160 + +#define XPT_BERT_ANALYZER_BASEADDR5 0x90700164 + +#define XPT_BERT_ANALYZER_BASEADDR6 0x90700168 + +#define XPT_BERT_ANALYZER_BASEADDR7 0x9070016C + +#define XPT_BERT_ANALYZER_BASEADDR8 0x90700170 + +#define XPT_BERT_ANALYZER_BASEADDR9 0x90700174 + +#define XPT_DMD0_BASEADDR 0x9070024C + +/* V2 AGC Gain Freeze & step */ +#define DBG_ENABLE_DISABLE_AGC (0x3FFFCF60) /* 1: DISABLE, 0:ENABLE */ +#define WB_DFE0_DFE_FB_RF1_BASEADDR 0x903004A4 + +#define WB_DFE1_DFE_FB_RF1_BASEADDR 0x904004A4 + +#define WB_DFE2_DFE_FB_RF1_BASEADDR 0x905004A4 + +#define WB_DFE3_DFE_FB_RF1_BASEADDR 0x906004A4 + +#define AFE_REG_D2A_TA_RFFE_LNA_BO_1P8_BASEADDR 0x90200104 + +#define AFE_REG_AFE_REG_SPARE_BASEADDR 0x902000A0 + +#define AFE_REG_AFE_REG_SPARE_BASEADDR1 0x902000B4 + +#define AFE_REG_AFE_REG_SPARE_BASEADDR2 0x902000C4 + +#define AFE_REG_AFE_REG_SPARE_BASEADDR3 0x902000D4 + +#define WB_DFE0_DFE_FB_AGC_BASEADDR 0x90300498 + +#define WB_DFE1_DFE_FB_AGC_BASEADDR 0x90400498 + +#define WB_DFE2_DFE_FB_AGC_BASEADDR 0x90500498 + +#define WB_DFE3_DFE_FB_AGC_BASEADDR 0x90600498 + +#define WDT_WD_INT_BASEADDR 0x8002000C + +#define FSK_TX_FTM_BASEADDR 0x80090000 + +#define FSK_TX_FTM_TX_CNT_BASEADDR 0x80090018 + +#define AFE_REG_D2A_FSK_BIAS_BASEADDR 0x90200040 + +#define DMD_TEI_BASEADDR 0x3FFFEBE0 + +#endif /* __MXL58X_REGISTERS_H__ */ diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c new file mode 100644 index 0000000000..2a31bde263 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.c @@ -0,0 +1,1368 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + */ + +#include <linux/mutex.h> +#include <linux/i2c-mux.h> +#include <linux/string.h> +#include <linux/firmware.h> + +#include "mxl692.h" +#include "mxl692_defs.h" + +static const struct dvb_frontend_ops mxl692_ops; + +struct mxl692_dev { + struct dvb_frontend fe; + struct i2c_client *i2c_client; + struct mutex i2c_lock; /* i2c command mutex */ + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + enum MXL_EAGLE_POWER_MODE_E power_mode; + u32 current_frequency; + int device_type; + int seqnum; + int init_done; +}; + +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = 0, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_dbg(&dev->i2c_client->dev, "i2c write error!\n"); + + return ret; +} + +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = I2C_M_RD, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_dbg(&dev->i2c_client->dev, "i2c read error!\n"); + + return ret; +} + +static int convert_endian(u32 size, u8 *d) +{ + u32 i; + + for (i = 0; i < (size & ~3); i += 4) { + d[i + 0] ^= d[i + 3]; + d[i + 3] ^= d[i + 0]; + d[i + 0] ^= d[i + 3]; + + d[i + 1] ^= d[i + 2]; + d[i + 2] ^= d[i + 1]; + d[i + 1] ^= d[i + 2]; + } + + switch (size & 3) { + case 0: + case 1: + /* do nothing */ + break; + case 2: + d[i + 0] ^= d[i + 1]; + d[i + 1] ^= d[i + 0]; + d[i + 0] ^= d[i + 1]; + break; + + case 3: + d[i + 0] ^= d[i + 2]; + d[i + 2] ^= d[i + 0]; + d[i + 0] ^= d[i + 2]; + break; + } + return size; +} + +static int convert_endian_n(int n, u32 size, u8 *d) +{ + int i, count = 0; + + for (i = 0; i < n; i += size) + count += convert_endian(size, d + i); + return count; +} + +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ +#ifdef __BIG_ENDIAN + return; +#endif + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: + buffer += 5; + buffer += convert_endian(2 * sizeof(u32), buffer); + break; + default: + /* no swapping - all get opcodes */ + /* ATSC/OOB no swapping */ + break; + } +} + +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ +#ifdef __BIG_ENDIAN + return; +#endif + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: + buffer++; + buffer += convert_endian(2 * sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: + buffer += 8; + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: + buffer += convert_endian(7 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: + buffer += convert_endian_n(8, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: + buffer += convert_endian_n(17, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: + buffer += convert_endian(sizeof(u32), buffer); + break; + default: + /* no swapping - all set opcodes */ + break; + } +} + +static u32 mxl692_checksum(u8 *buffer, u32 size) +{ + u32 ix, div_size; + u32 cur_cksum = 0; + __be32 *buf; + + div_size = DIV_ROUND_UP(size, 4); + + buf = (__be32 *)buffer; + for (ix = 0; ix < div_size; ix++) + cur_cksum += be32_to_cpu(buf[ix]); + + cur_cksum ^= 0xDEADBEEF; + + return cur_cksum; +} + +static int mxl692_validate_fw_header(struct mxl692_dev *dev, + const u8 *buffer, u32 buf_len) +{ + int status = 0; + u32 ix, temp; + __be32 *local_buf = NULL; + u8 temp_cksum = 0; + static const u8 fw_hdr[] = { + 0x4D, 0x31, 0x10, 0x02, 0x40, 0x00, 0x00, 0x80 + }; + + if (memcmp(buffer, fw_hdr, 8) != 0) { + status = -EINVAL; + goto err_finish; + } + + local_buf = (__be32 *)(buffer + 8); + temp = be32_to_cpu(*local_buf); + + if ((buf_len - 16) != temp >> 8) { + status = -EINVAL; + goto err_finish; + } + + for (ix = 16; ix < buf_len; ix++) + temp_cksum += buffer[ix]; + + if (temp_cksum != buffer[11]) + status = -EINVAL; + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "failed\n"); + return status; +} + +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, + u32 buf_len, u32 *index) +{ + int status = 0; + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; + + ix = *index; + + if (buffer[ix] == 0x53) { + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; + total_len = (total_len + 3) & ~3; + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | + buffer[ix + 6] << 8 | buffer[ix + 7]; + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; + + while ((total_len > 0) && (status == 0)) { + plocal_buf = local_buf; + chunk_len = (total_len < payload_max) ? total_len : payload_max; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = chunk_len + sizeof(u32); + + *(u32 *)plocal_buf = addr + prevchunk_len; +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32), plocal_buf); +#endif + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, &buffer[ix], chunk_len); + convert_endian(chunk_len, plocal_buf); + if (mxl692_i2c_write(dev, local_buf, + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + break; + } + + prevchunk_len += chunk_len; + total_len -= chunk_len; + ix += chunk_len; + } + *index = ix; + } else { + status = -EINVAL; + } + + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = total_len + sizeof(u32); + + *(u32 *)plocal_buf = addr; + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, buffer, total_len); +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32) + total_len, local_buf + 2); +#endif + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } + + return status; +err_finish: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0; + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFB; + *plocal_buf++ = sizeof(u32); + *(u32 *)plocal_buf = addr; +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32), plocal_buf); +#endif + mutex_lock(&dev->i2c_lock); + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? -EREMOTEIO : 0; +#ifdef __BIG_ENDIAN + if (status == 0) + convert_endian(size, buffer); +#endif + } else { + status = -EREMOTEIO; + } + + mutex_unlock(&dev->i2c_lock); + + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static const char *mxl692_opcode_string(u8 opcode) +{ + if (opcode <= MXL_EAGLE_OPCODE_INTERNAL) + return MXL_EAGLE_OPCODE_STRING[opcode]; + + return "invalid opcode"; +} + +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_hdr = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)buffer; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFE; + *plocal_buf++ = (u8)total_len; + + memcpy(plocal_buf, buffer, total_len); + convert_endian(total_len, plocal_buf); + + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "opcode %s err %d\n", + mxl692_opcode_string(tx_hdr->opcode), status); + return status; +} + +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0; + u32 ix = 0; + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; + + local_buf[0] = 0xFD; + local_buf[1] = 0; + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + + /* Read in 4 byte chunks */ + for (ix = 0; ix < size; ix += 4) { + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { + dev_dbg(&dev->i2c_client->dev, "ix=%d size=%d\n", ix, size); + status = -EREMOTEIO; + goto err_finish; + } + } + convert_endian(size, buffer); + } else { + status = -EREMOTEIO; + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_i2c_writeread(struct mxl692_dev *dev, + u8 opcode, + u8 *tx_payload, + u8 tx_payload_size, + u8 *rx_payload, + u8 rx_payload_expected) +{ + int status = 0, timeout = 40; + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u32 resp_checksum = 0, resp_checksum_tmp = 0; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; + + mutex_lock(&dev->i2c_lock); + + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { + status = -EINVAL; + goto err_finish; + } + + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; + tx_header->opcode = opcode; + tx_header->seqnum = dev->seqnum++; + tx_header->payload_size = tx_payload_size; + tx_header->checksum = 0; + + if (dev->seqnum == 0) + dev->seqnum = 1; + + if (tx_payload && tx_payload_size > 0) + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], tx_payload, tx_payload_size); + + mxl692_tx_swap(opcode, tx_buf); + + tx_header->checksum = 0; + tx_header->checksum = mxl692_checksum(tx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); +#ifdef __LITTLE_ENDIAN + convert_endian(4, (u8 *)&tx_header->checksum); /* cksum is big endian */ +#endif + /* send Tx message */ + status = mxl692_opwrite(dev, tx_buf, + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + if (status) { + status = -EREMOTEIO; + goto err_finish; + } + + /* receive Rx message (polling) */ + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; + + do { + status = mxl692_opread(dev, rx_buf, + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + usleep_range(1000, 2000); + timeout--; + } while ((timeout > 0) && (status == 0) && + (rx_header->seqnum == 0) && + (rx_header->checksum == 0)); + + if (timeout == 0 || status) { + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", + timeout, status); + status = -ETIMEDOUT; + goto err_finish; + } + + if (rx_header->status) { + dev_dbg(&dev->i2c_client->dev, "rx header status code: %d\n", rx_header->status); + status = -EREMOTEIO; + goto err_finish; + } + + if (rx_header->seqnum != tx_header->seqnum || + rx_header->opcode != tx_header->opcode || + rx_header->payload_size != rx_payload_expected) { + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", + rx_header->seqnum != tx_header->seqnum ? "X" : "0", + rx_header->opcode != tx_header->opcode ? "X" : "0", + rx_header->payload_size != rx_payload_expected ? "X" : "0"); + if (rx_header->payload_size != rx_payload_expected) + dev_dbg(&dev->i2c_client->dev, + "rx_header->payloadSize=%d rx_payload_expected=%d\n", + rx_header->payload_size, rx_payload_expected); + status = -EREMOTEIO; + goto err_finish; + } + + resp_checksum = rx_header->checksum; + rx_header->checksum = 0; + + resp_checksum_tmp = mxl692_checksum(rx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); +#ifdef __LITTLE_ENDIAN + convert_endian(4, (u8 *)&resp_checksum_tmp); /* cksum is big endian */ +#endif + if (resp_checksum != resp_checksum_tmp) { + dev_dbg(&dev->i2c_client->dev, "rx checksum failure\n"); + status = -EREMOTEIO; + goto err_finish; + } + + mxl692_rx_swap(rx_header->opcode, rx_buf); + + if (rx_header->payload_size > 0) { + if (!rx_payload) { + dev_dbg(&dev->i2c_client->dev, "no rx payload?!?\n"); + status = -EREMOTEIO; + goto err_finish; + } + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, + rx_header->payload_size); + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + mutex_unlock(&dev->i2c_lock); + return status; +} + +static int mxl692_fwdownload(struct mxl692_dev *dev, + const u8 *firmware_buf, u32 buf_len) +{ + int status = 0; + u32 ix, reg_val = 0x1; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_DEV_STATUS_T *dev_status; + + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) + return -EINVAL; + + mutex_lock(&dev->i2c_lock); + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_validate_fw_header(dev, firmware_buf, buf_len); + if (status) + goto err_finish; + + ix = 16; + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ + if (status) + goto err_finish; + + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ + if (status) + goto err_finish; + + /* release CPU from reset */ + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + mutex_unlock(&dev->i2c_lock); + + if (status == 0) { + /* verify FW is alive */ + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + NULL, + 0, + (u8 *)dev_status, + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); + } + + return status; +err_finish: + mutex_unlock(&dev->i2c_lock); + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_versions(struct mxl692_dev *dev) +{ + int status = 0; + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; + + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + NULL, + 0, + (u8 *)&dev_ver, + sizeof(struct MXL_EAGLE_DEV_VER_T)); + if (status) + return status; + + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", + chip_id[dev_ver.chip_id]); + + dev_info(&dev->i2c_client->dev, + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", + dev_ver.firmware_ver[0], + dev_ver.firmware_ver[1], + dev_ver.firmware_ver[2], + dev_ver.firmware_ver[3], + dev_ver.firmware_ver[4]); + + return status; +} + +static int mxl692_reset(struct mxl692_dev *dev) +{ + int status = 0; + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* legacy i2c override */ + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* verify sku */ + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_type != dev->device_type) + goto err_finish; + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_regulators(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) +{ + int status = 0; + u32 reg_val; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* configure main regulator according to the power supply source */ + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0x00FFFFFF; + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? + 0x14000000 : 0x10000000; + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* configure digital regulator to high current mode */ + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x800; + + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_xtal(struct mxl692_dev *dev, + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) +{ + int status = 0; + u32 reg_val, reg_val1; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL capacitance */ + reg_val &= 0xFFFFFFE0; + reg_val |= dev_xtal->xtal_cap; + + /* set CLK OUT */ + reg_val = dev_xtal->clk_out_enable ? (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set CLK OUT divider */ + reg_val = dev_xtal->clk_out_div_enable ? (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL sharing */ + reg_val = dev_xtal->xtal_sharing_enable ? (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* enable/disable XTAL calibration, based on master/slave device */ + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_xtal->xtal_calibration_enable) { + /* enable XTAL calibration and set XTAL amplitude to a higher value */ + reg_val1 &= 0xFFFFFFFD; + reg_val1 |= 0x30; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + } else { + /* disable XTAL calibration */ + reg_val1 |= 0x2; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL bias value */ + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xC0FFFFFF; + reg_val |= 0xA000000; + + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + } + + /* start XTAL calibration */ + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x8; + + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x10; + + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xFFFFEFFF; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x1000; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + usleep_range(45000, 55000); + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_powermode(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_MODE_E power_mode) +{ + int status = 0; + u8 mode = power_mode; + + dev_dbg(&dev->i2c_client->dev, "%s\n", + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? "sleep" : "active"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + &mode, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; + } + + dev->power_mode = power_mode; + + return status; +} + +static int mxl692_init(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->i2c_client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int status = 0; + const struct firmware *firmware; + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + if (dev->init_done) + goto warm; + + dev->seqnum = 1; + + status = mxl692_reset(dev); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ + + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); + if (status) + goto err; + + xtal_config.xtal_cap = 26; + xtal_config.clk_out_div_enable = 0; + xtal_config.clk_out_enable = 0; + xtal_config.xtal_calibration_enable = 0; + xtal_config.xtal_sharing_enable = 1; + status = mxl692_config_xtal(dev, &xtal_config); + if (status) + goto err; + + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); + if (status) { + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", + MXL692_FIRMWARE); + goto err; + } + + status = mxl692_fwdownload(dev, firmware->data, firmware->size); + if (status) + goto err_release_firmware; + + release_firmware(firmware); + + status = mxl692_get_versions(dev); + if (status) + goto err; + + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; +warm: + /* Config Device Power Mode */ + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { + status = mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_ACTIVE); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ + } + + /* Init stats here to indicate which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dev->init_done = 1; + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_sleep(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); + + return 0; +} + +static int mxl692_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mxl692_dev *dev = fe->demodulator_priv; + + int status = 0; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; + u8 op_param = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + switch (p->modulation) { + case VSB_8: + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; + break; + case QAM_AUTO: + case QAM_64: + case QAM_128: + case QAM_256: + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; + break; + default: + return -EINVAL; + } + + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) { + dev_dbg(&dev->i2c_client->dev, "already set up\n"); + return 0; + } + + dev->current_frequency = -1; + dev->demod_type = -1; + + op_param = demod_type; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + &op_param, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, + "DEVICE_DEMODULATOR_TYPE_SET...FAIL err 0x%x\n", status); + goto err; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + mpeg_params.mpeg_parallel = 0; + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; + mpeg_params.mpeg3wire_mode_enable = 0; + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + qam_params.annex_type = qam_annex; + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; + if (p->modulation == QAM_64) + qam_params.symbol_rate_hz = 5057000; + else + qam_params.symbol_rate_hz = 5361000; + + qam_params.symbol_rate_256qam_hz = 5361000; + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + (u8 *)&qam_params, + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), + NULL, 0); + if (status) + goto err; + + break; + default: + break; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + tuner_params.freq_hz = p->frequency; + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; + + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + (u8 *)&tuner_params, + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + default: + break; + } + + dev->demod_type = demod_type; + dev->current_frequency = p->frequency; + + return 0; +err: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + p->modulation = c->modulation; + p->frequency = c->frequency; + + return 0; +} + +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + + *snr = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status) { + *snr = (u16)(atsc_status->snr_db_tenths / 10); + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = *snr; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status) + *snr = (u16)(qam_status->snr_db_tenths / 10); + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + return mxl_status; +} + +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + u32 utmp; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); + if (!mxl_status) { + if (atsc_errors->error_packets == 0) + utmp = 0; + else + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * + atsc_errors->total_packets); + /* ber */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp; + /* ucb */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += atsc_errors->error_packets; + + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + + return mxl_status; +} + +static int mxl692_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + *status = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status && atsc_status->atsc_lock) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status && qam_status->qam_locked) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if ((*status & FE_HAS_LOCK) == 0) { + /* No lock, reset all statistics */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + else + mxl_status = mxl692_read_ber_ucb(fe); + + return mxl_status; +} + +static const struct dvb_frontend_ops mxl692_ops = { + .delsys = { SYS_ATSC }, + .info = { + .name = "MaxLinear MxL692 VSB tuner-demodulator", + .frequency_min_hz = 54000000, + .frequency_max_hz = 858000000, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_8VSB + }, + + .init = mxl692_init, + .sleep = mxl692_sleep, + .set_frontend = mxl692_set_frontend, + .get_frontend = mxl692_get_frontend, + + .read_status = mxl692_read_status, + .read_snr = mxl692_read_snr, +}; + +static int mxl692_probe(struct i2c_client *client) +{ + struct mxl692_config *config = client->dev.platform_data; + struct mxl692_dev *dev; + int ret = 0; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + dev_dbg(&client->dev, "kzalloc() failed\n"); + goto err; + } + + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + dev->i2c_client = client; + *config->fe = &dev->fe; + mutex_init(&dev->i2c_lock); + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return -ENODEV; +} + +static void mxl692_remove(struct i2c_client *client) +{ + struct mxl692_dev *dev = i2c_get_clientdata(client); + + dev->fe.demodulator_priv = NULL; + i2c_set_clientdata(client, NULL); + kfree(dev); +} + +static const struct i2c_device_id mxl692_id_table[] = { + {"mxl692", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); + +static struct i2c_driver mxl692_driver = { + .driver = { + .name = "mxl692", + }, + .probe = mxl692_probe, + .remove = mxl692_remove, + .id_table = mxl692_id_table, +}; + +module_i2c_driver(mxl692_driver); + +MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>"); +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); +MODULE_FIRMWARE(MXL692_FIRMWARE); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h new file mode 100644 index 0000000000..77764a047c --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + */ + +#ifndef _MXL692_H_ +#define _MXL692_H_ + +#include <media/dvb_frontend.h> + +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" + +struct mxl692_config { + unsigned char id; + u8 i2c_addr; + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; +}; + +#endif /* _MXL692_H_ */ diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h new file mode 100644 index 0000000000..c603f3d6f2 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692_defs.h @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + */ + +/***************************************************************************** + * Defines + ***************************************************************************** + */ +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ +#define MXL_EAGLE_FW_LOAD_TIME 50 + +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_FW_HEADER_SIZE 16 +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 + +/* Enum of Eagle family devices */ +enum MXL_EAGLE_DEVICE_E { + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ + MXL_EAGLE_DEVICE_MAX, /* No such device */ +}; + +#define VER_A 1 +#define VER_B 1 +#define VER_C 1 +#define VER_D 3 +#define VER_E 6 + +/* Enum of Host to Eagle I2C protocol opcodes */ +enum MXL_EAGLE_OPCODE_E { + /* DEVICE */ + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, + + /* TUNER */ + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, + + /* ATSC */ + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, + + /* QAM */ + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, + + /* OOB */ + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, + MXL_EAGLE_OPCODE_OOB_RESTART_SET, + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_OOB_STATUS_GET, + + /* SMA */ + MXL_EAGLE_OPCODE_SMA_INIT_SET, + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, + + /* DEBUG */ + MXL_EAGLE_OPCODE_INTERNAL, + + MXL_EAGLE_OPCODE_MAX = 70, +}; + +/* Enum of Host to Eagle I2C protocol opcodes */ +static const char * const MXL_EAGLE_OPCODE_STRING[] = { + /* DEVICE */ + "DEVICE_DEMODULATOR_TYPE_SET", + "DEVICE_MPEG_OUT_PARAMS_SET", + "DEVICE_POWERMODE_SET", + "DEVICE_GPIO_DIRECTION_SET", + "DEVICE_GPO_LEVEL_SET", + "DEVICE_INTR_MASK_SET", + "DEVICE_IO_MUX_SET", + "DEVICE_VERSION_GET", + "DEVICE_STATUS_GET", + "DEVICE_GPI_LEVEL_GET", + + /* TUNER */ + "TUNER_CHANNEL_TUNE_SET", + "TUNER_LOCK_STATUS_GET", + "TUNER_AGC_STATUS_GET", + + /* ATSC */ + "ATSC_INIT_SET", + "ATSC_ACQUIRE_CARRIER_SET", + "ATSC_STATUS_GET", + "ATSC_ERROR_COUNTERS_GET", + "ATSC_EQUALIZER_FILTER_DFE_TAPS_GET", + "ATSC_EQUALIZER_FILTER_FFE_TAPS_GET", + + /* QAM */ + "QAM_PARAMS_SET", + "QAM_RESTART_SET", + "QAM_STATUS_GET", + "QAM_ERROR_COUNTERS_GET", + "QAM_CONSTELLATION_VALUE_GET", + "QAM_EQUALIZER_FILTER_FFE_GET", + "QAM_EQUALIZER_FILTER_SPUR_START_GET", + "QAM_EQUALIZER_FILTER_SPUR_END_GET", + "QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET", + "QAM_EQUALIZER_FILTER_DFE_START_GET", + "QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET", + "QAM_EQUALIZER_FILTER_DFE_END_GET", + + /* OOB */ + "OOB_PARAMS_SET", + "OOB_RESTART_SET", + "OOB_ERROR_COUNTERS_GET", + "OOB_STATUS_GET", + + /* SMA */ + "SMA_INIT_SET", + "SMA_PARAMS_SET", + "SMA_TRANSMIT_SET", + "SMA_RECEIVE_GET", + + /* DEBUG */ + "INTERNAL", +}; + +/* Enum of Callabck function types */ +enum MXL_EAGLE_CB_TYPE_E { + MXL_EAGLE_CB_FW_DOWNLOAD = 0, +}; + +/* Enum of power supply types */ +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ +}; + +/* Enum of I/O pad drive modes */ +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, +}; + +/* Enum of demodulator types. Used for selection of demodulator + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 + */ +enum MXL_EAGLE_DEMOD_TYPE_E { + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ +}; + +/* Enum of power modes. Used for initial + * activation, or for activating sleep mode + */ +enum MXL_EAGLE_POWER_MODE_E { + MXL_EAGLE_POWER_MODE_SLEEP, + MXL_EAGLE_POWER_MODE_ACTIVE +}; + +/* Enum of GPIOs, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_NUMBER_E { + MXL_EAGLE_GPIO_NUMBER_0, + MXL_EAGLE_GPIO_NUMBER_1, + MXL_EAGLE_GPIO_NUMBER_2, + MXL_EAGLE_GPIO_NUMBER_3, + MXL_EAGLE_GPIO_NUMBER_4, + MXL_EAGLE_GPIO_NUMBER_5, + MXL_EAGLE_GPIO_NUMBER_6 +}; + +/* Enum of GPIO directions, used in GPIO direction configuration API */ +enum MXL_EAGLE_GPIO_DIRECTION_E { + MXL_EAGLE_GPIO_DIRECTION_INPUT, + MXL_EAGLE_GPIO_DIRECTION_OUTPUT +}; + +/* Enum of GPIO level, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_LEVEL_E { + MXL_EAGLE_GPIO_LEVEL_LOW, + MXL_EAGLE_GPIO_LEVEL_HIGH, +}; + +/* Enum of I/O Mux function, used in device I/O mux configuration API */ +enum MXL_EAGLE_IOMUX_FUNCTION_E { + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, + MXL_EAGLE_IOMUX_FUNC_MERR, +}; + +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, + MXL_EAGLE_DATA_SERIAL_MSB_1ST, + + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE +}; + +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, + MXL_EAGLE_CLOCK_ACTIVE_LOW, + + MXL_EAGLE_CLOCK_POSITIVE = 0, + MXL_EAGLE_CLOCK_NEGATIVE, + + MXL_EAGLE_CLOCK_IN_PHASE = 0, + MXL_EAGLE_CLOCK_INVERTED, +}; + +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { + MXL_EAGLE_MPEG_CLOCK_54MHZ, + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, + MXL_EAGLE_MPEG_CLOCK_27MHZ, + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, +}; + +/* Enum of Interrupt mask bit, used in host interrupt configuration */ +enum MXL_EAGLE_INTR_MASK_BITS_E { + MXL_EAGLE_INTR_MASK_DEMOD = 0, + MXL_EAGLE_INTR_MASK_SMA_RX = 1, + MXL_EAGLE_INTR_MASK_WDOG = 31 +}; + +/* Enum of QAM Demodulator type, used in QAM configuration */ +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ +}; + +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { + MXL_EAGLE_QAM_DEMOD_QAM16, + MXL_EAGLE_QAM_DEMOD_QAM64, + MXL_EAGLE_QAM_DEMOD_QAM256, + MXL_EAGLE_QAM_DEMOD_QAM1024, + MXL_EAGLE_QAM_DEMOD_QAM32, + MXL_EAGLE_QAM_DEMOD_QAM128, + MXL_EAGLE_QAM_DEMOD_QPSK, + MXL_EAGLE_QAM_DEMOD_AUTO, +}; + +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ +enum MXL_EAGLE_IQ_FLIP_E { + MXL_EAGLE_DEMOD_IQ_NORMAL, + MXL_EAGLE_DEMOD_IQ_FLIPPED, + MXL_EAGLE_DEMOD_IQ_AUTO, +}; + +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ +}; + +/* Enum of tuner channel tuning mode */ +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_TUNER_BW_E { + MXL_EAGLE_TUNER_BW_6MHZ, + MXL_EAGLE_TUNER_BW_7MHZ, + MXL_EAGLE_TUNER_BW_8MHZ, +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, +}; + +/* Struct passed in optional callback used during FW download */ +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { + u32 total_len; + u32 downloaded_len; +}; + +/* Struct used of I2C protocol between host and Eagle, internal use only */ +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { + u8 opcode; + u8 seqnum; + u8 payload_size; + u8 status; + u32 checksum; +}; + +/* Device version information struct */ +struct __packed MXL_EAGLE_DEV_VER_T { + u8 chip_id; + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; +}; + +/* Xtal configuration struct */ +struct __packed MXL_EAGLE_DEV_XTAL_T { + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ + u8 clk_out_enable; + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ + u8 xtal_calibration_enable; /* enable for master, disable for slave */ +}; + +/* GPIO direction struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { + u8 gpio_number; + u8 gpio_direction; +}; + +/* GPO level struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { + u8 gpio_number; + u8 gpo_level; +}; + +/* Device Status struct */ +struct MXL_EAGLE_DEV_STATUS_T { + u8 temperature; + u8 demod_type; + u8 power_mode; + u8 cpu_utilization_percent; +}; + +/* Device interrupt configuration struct */ +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { + u32 intr_mask; + u8 edge_trigger; + u8 positive_trigger; + u8 global_enable_interrupt; +}; + +/* MPEG pad drive parameters, used on MPEG output configuration */ +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { + u8 pad_drv_mpeg_syn; + u8 pad_drv_mpeg_dat; + u8 pad_drv_mpeg_val; + u8 pad_drv_mpeg_clk; +}; + +/* MPEGOUT parameter struct, used in MPEG output configuration */ +struct MXL_EAGLE_MPEGOUT_PARAMS_T { + u8 mpeg_parallel; + u8 msb_first; + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ + u8 mpeg_valid_pol; + u8 mpeg_sync_pol; + u8 mpeg_clk_pol; + u8 mpeg3wire_mode_enable; + u8 mpeg_clk_freq; + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; +}; + +/* QAM Demodulator parameters struct, used in QAM params configuration */ +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 search_range_idx; + u8 spur_canceller_enable; + u32 symbol_rate_hz; + u32 symbol_rate_256qam_hz; +}; + +/* QAM Demodulator status */ +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 interleaver_depth_i; + u8 interleaver_depth_j; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; +}; + +/* QAM Demodulator error counters */ +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { + u32 corrected_code_words; + u32 uncorrected_code_words; + u32 total_code_words_received; + u32 corrected_bits; + u32 error_mpeg_frames; + u32 mpeg_frames_received; + u32 erasures; +}; + +/* QAM Demodulator constellation point */ +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { + s16 i_value[12]; + s16 q_value[12]; +}; + +/* QAM Demodulator equalizer filter taps */ +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; + u8 ffe_leading_tap_index; + u8 dfe_taps_number; +}; + +/* OOB Demodulator parameters struct, used in OOB params configuration */ +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { + u8 symbol_rate; + u8 iq_flip; + u8 clk_pol; +}; + +/* OOB Demodulator error counters */ +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { + u32 corrected_packets; + u32 uncorrected_packets; + u32 total_packets_received; +}; + +/* OOB status */ +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offsetHz; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u8 retune_required; + u8 iq_flip; +}; + +/* ATSC Demodulator status */ +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { + s16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; + u8 frame_lock; + u8 atsc_lock; + u8 fec_lock; +}; + +/* ATSC Demodulator error counters */ +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { + u32 error_packets; + u32 total_packets; + u32 error_bytes; +}; + +/* ATSC Demodulator equalizers filter taps */ +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; +}; + +/* Tuner AGC Status */ +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { + u8 locked; + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ + s16 rx_power_db_hundredths; +}; + +/* Tuner channel tune parameters */ +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { + u32 freq_hz; + u8 tune_mode; + u8 bandwidth; +}; + +/* Tuner channel lock indications */ +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { + u8 rf_pll_locked; + u8 ref_pll_locked; +}; + +/* Smart antenna parameters used in Smart antenna params configuration */ +struct __packed MXL_EAGLE_SMA_PARAMS_T { + u8 full_duplex_enable; + u8 rx_disable; + u8 idle_logic_high; +}; + +/* Smart antenna message format */ +struct __packed MXL_EAGLE_SMA_MESSAGE_T { + u32 payload_bits; + u8 total_num_bits; +}; + diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c new file mode 100644 index 0000000000..1c549ada6e --- /dev/null +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -0,0 +1,1220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for NXT2002 and NXT2004 - VSB/QAM + * + * Copyright (C) 2005 Kirk Lapray <kirk.lapray@gmail.com> + * Copyright (C) 2006-2014 Michael Krufky <mkrufky@linuxtv.org> + * based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net> + * and nxt2004 by Jean-Francois Thibert <jeanfrancois@sagetv.com> +*/ + +/* + * NOTES ABOUT THIS DRIVER + * + * This Linux driver supports: + * B2C2/BBTI Technisat Air2PC - ATSC (NXT2002) + * AverTVHD MCE A180 (NXT2004) + * ATI HDTV Wonder (NXT2004) + * + * This driver needs external firmware. Please use the command + * "<kerneldir>/scripts/get_dvb_firmware nxt2002" or + * "<kerneldir>/scripts/get_dvb_firmware nxt2004" to + * download/extract the appropriate firmware, and then copy it to + * /usr/lib/hotplug/firmware/ or /lib/firmware/ + * (depending on configuration of firmware hotplug). + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 256 + +#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw" +#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw" +#define CRC_CCIT_MASK 0x1021 + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <media/dvb_frontend.h> +#include "nxt200x.h" + +struct nxt200x_state { + + struct i2c_adapter* i2c; + const struct nxt200x_config* config; + struct dvb_frontend frontend; + + /* demodulator private data */ + nxt_chip_type demod_chip; + u8 initialised:1; +}; + +static int debug; +#define dprintk(args...) do { if (debug) pr_debug(args); } while (0) + +static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len) +{ + int err; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len }; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n", + __func__, addr, err); + return -EREMOTEIO; + } + return 0; +} + +static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len) +{ + int err; + struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len }; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n", + __func__, addr, err); + return -EREMOTEIO; + } + return 0; +} + +static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg, + const u8 *buf, u8 len) +{ + u8 buf2[MAX_XFER_SIZE]; + int err; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 }; + + if (1 + len > sizeof(buf2)) { + pr_warn("%s: i2c wr reg=%04x: len=%d is too big!\n", + __func__, reg, len); + return -EINVAL; + } + + buf2[0] = reg; + memcpy(&buf2[1], buf, len); + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n", + __func__, state->config->demod_address, err); + return -EREMOTEIO; + } + return 0; +} + +static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len) +{ + u8 reg2 [] = { reg }; + + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } }; + + int err; + + if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { + pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n", + __func__, state->config->demod_address, err); + return -EREMOTEIO; + } + return 0; +} + +static u16 nxt200x_crc(u16 crc, u8 c) +{ + u8 i; + u16 input = (u16) c & 0xFF; + + input<<=8; + for(i=0; i<8; i++) { + if((crc^input) & 0x8000) + crc=(crc<<1)^CRC_CCIT_MASK; + else + crc<<=1; + input<<=1; + } + return crc; +} + +static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len) +{ + u8 attr, len2, buf; + dprintk("%s\n", __func__); + + /* set multi register register */ + nxt200x_writebytes(state, 0x35, ®, 1); + + /* send the actual data */ + nxt200x_writebytes(state, 0x36, data, len); + + switch (state->demod_chip) { + case NXT2002: + len2 = len; + buf = 0x02; + break; + case NXT2004: + /* probably not right, but gives correct values */ + attr = 0x02; + if (reg & 0x80) { + attr = attr << 1; + if (reg & 0x04) + attr = attr >> 1; + } + /* set write bit */ + len2 = ((attr << 4) | 0x10) | len; + buf = 0x80; + break; + default: + return -EINVAL; + } + + /* set multi register length */ + nxt200x_writebytes(state, 0x34, &len2, 1); + + /* toggle the multireg write bit */ + nxt200x_writebytes(state, 0x21, &buf, 1); + + nxt200x_readbytes(state, 0x21, &buf, 1); + + switch (state->demod_chip) { + case NXT2002: + if ((buf & 0x02) == 0) + return 0; + break; + case NXT2004: + if (buf == 0) + return 0; + break; + default: + return -EINVAL; + } + + pr_warn("Error writing multireg register 0x%02X\n", reg); + + return 0; +} + +static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len) +{ + int i; + u8 buf, len2, attr; + dprintk("%s\n", __func__); + + /* set multi register register */ + nxt200x_writebytes(state, 0x35, ®, 1); + + switch (state->demod_chip) { + case NXT2002: + /* set multi register length */ + len2 = len & 0x80; + nxt200x_writebytes(state, 0x34, &len2, 1); + + /* read the actual data */ + nxt200x_readbytes(state, reg, data, len); + return 0; + case NXT2004: + /* probably not right, but gives correct values */ + attr = 0x02; + if (reg & 0x80) { + attr = attr << 1; + if (reg & 0x04) + attr = attr >> 1; + } + + /* set multi register length */ + len2 = (attr << 4) | len; + nxt200x_writebytes(state, 0x34, &len2, 1); + + /* toggle the multireg bit*/ + buf = 0x80; + nxt200x_writebytes(state, 0x21, &buf, 1); + + /* read the actual data */ + for(i = 0; i < len; i++) { + nxt200x_readbytes(state, 0x36 + i, &data[i], 1); + } + return 0; + default: + return -EINVAL; + } +} + +static void nxt200x_microcontroller_stop (struct nxt200x_state* state) +{ + u8 buf, stopval, counter = 0; + dprintk("%s\n", __func__); + + /* set correct stop value */ + switch (state->demod_chip) { + case NXT2002: + stopval = 0x40; + break; + case NXT2004: + stopval = 0x10; + break; + default: + stopval = 0; + break; + } + + buf = 0x80; + nxt200x_writebytes(state, 0x22, &buf, 1); + + while (counter < 20) { + nxt200x_readbytes(state, 0x31, &buf, 1); + if (buf & stopval) + return; + msleep(10); + counter++; + } + + pr_warn("Timeout waiting for nxt200x to stop. This is ok after firmware upload.\n"); + return; +} + +static void nxt200x_microcontroller_start (struct nxt200x_state* state) +{ + u8 buf; + dprintk("%s\n", __func__); + + buf = 0x00; + nxt200x_writebytes(state, 0x22, &buf, 1); +} + +static void nxt2004_microcontroller_init (struct nxt200x_state* state) +{ + u8 buf[9]; + u8 counter = 0; + dprintk("%s\n", __func__); + + buf[0] = 0x00; + nxt200x_writebytes(state, 0x2b, buf, 1); + buf[0] = 0x70; + nxt200x_writebytes(state, 0x34, buf, 1); + buf[0] = 0x04; + nxt200x_writebytes(state, 0x35, buf, 1); + buf[0] = 0x01; buf[1] = 0x23; buf[2] = 0x45; buf[3] = 0x67; buf[4] = 0x89; + buf[5] = 0xAB; buf[6] = 0xCD; buf[7] = 0xEF; buf[8] = 0xC0; + nxt200x_writebytes(state, 0x36, buf, 9); + buf[0] = 0x80; + nxt200x_writebytes(state, 0x21, buf, 1); + + while (counter < 20) { + nxt200x_readbytes(state, 0x21, buf, 1); + if (buf[0] == 0) + return; + msleep(10); + counter++; + } + + pr_warn("Timeout waiting for nxt2004 to init.\n"); + + return; +} + +static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) +{ + u8 buf, count = 0; + + dprintk("%s\n", __func__); + + dprintk("Tuner Bytes: %*ph\n", 4, data + 1); + + /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip. + * direct write is required for Philips TUV1236D and ALPS TDHU2 */ + switch (state->demod_chip) { + case NXT2004: + if (i2c_writebytes(state, data[0], data+1, 4)) + pr_warn("error writing to tuner\n"); + /* wait until we have a lock */ + while (count < 20) { + i2c_readbytes(state, data[0], &buf, 1); + if (buf & 0x40) + return 0; + msleep(100); + count++; + } + pr_warn("timeout waiting for tuner lock\n"); + break; + case NXT2002: + /* set the i2c transfer speed to the tuner */ + buf = 0x03; + nxt200x_writebytes(state, 0x20, &buf, 1); + + /* setup to transfer 4 bytes via i2c */ + buf = 0x04; + nxt200x_writebytes(state, 0x34, &buf, 1); + + /* write actual tuner bytes */ + nxt200x_writebytes(state, 0x36, data+1, 4); + + /* set tuner i2c address */ + buf = data[0] << 1; + nxt200x_writebytes(state, 0x35, &buf, 1); + + /* write UC Opmode to begin transfer */ + buf = 0x80; + nxt200x_writebytes(state, 0x21, &buf, 1); + + while (count < 20) { + nxt200x_readbytes(state, 0x21, &buf, 1); + if ((buf & 0x80)== 0x00) + return 0; + msleep(100); + count++; + } + pr_warn("timeout error writing to tuner\n"); + break; + default: + return -EINVAL; + } + return 0; +} + +static void nxt200x_agc_reset(struct nxt200x_state* state) +{ + u8 buf; + dprintk("%s\n", __func__); + + switch (state->demod_chip) { + case NXT2002: + buf = 0x08; + nxt200x_writebytes(state, 0x08, &buf, 1); + buf = 0x00; + nxt200x_writebytes(state, 0x08, &buf, 1); + break; + case NXT2004: + nxt200x_readreg_multibyte(state, 0x08, &buf, 1); + buf = 0x08; + nxt200x_writereg_multibyte(state, 0x08, &buf, 1); + buf = 0x00; + nxt200x_writereg_multibyte(state, 0x08, &buf, 1); + break; + default: + break; + } + return; +} + +static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) +{ + + struct nxt200x_state* state = fe->demodulator_priv; + u8 buf[3], written = 0, chunkpos = 0; + u16 rambase, position, crc = 0; + + dprintk("%s\n", __func__); + dprintk("Firmware is %zu bytes\n", fw->size); + + /* Get the RAM base for this nxt2002 */ + nxt200x_readbytes(state, 0x10, buf, 1); + + if (buf[0] & 0x10) + rambase = 0x1000; + else + rambase = 0x0000; + + dprintk("rambase on this nxt2002 is %04X\n", rambase); + + /* Hold the micro in reset while loading firmware */ + buf[0] = 0x80; + nxt200x_writebytes(state, 0x2B, buf, 1); + + for (position = 0; position < fw->size; position++) { + if (written == 0) { + crc = 0; + chunkpos = 0x28; + buf[0] = ((rambase + position) >> 8); + buf[1] = (rambase + position) & 0xFF; + buf[2] = 0x81; + /* write starting address */ + nxt200x_writebytes(state, 0x29, buf, 3); + } + written++; + chunkpos++; + + if ((written % 4) == 0) + nxt200x_writebytes(state, chunkpos, &fw->data[position-3], 4); + + crc = nxt200x_crc(crc, fw->data[position]); + + if ((written == 255) || (position+1 == fw->size)) { + /* write remaining bytes of firmware */ + nxt200x_writebytes(state, chunkpos+4-(written %4), + &fw->data[position-(written %4) + 1], + written %4); + buf[0] = crc << 8; + buf[1] = crc & 0xFF; + + /* write crc */ + nxt200x_writebytes(state, 0x2C, buf, 2); + + /* do a read to stop things */ + nxt200x_readbytes(state, 0x2A, buf, 1); + + /* set transfer mode to complete */ + buf[0] = 0x80; + nxt200x_writebytes(state, 0x2B, buf, 1); + + written = 0; + } + } + + return 0; +}; + +static int nxt2004_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) +{ + + struct nxt200x_state* state = fe->demodulator_priv; + u8 buf[3]; + u16 rambase, position, crc=0; + + dprintk("%s\n", __func__); + dprintk("Firmware is %zu bytes\n", fw->size); + + /* set rambase */ + rambase = 0x1000; + + /* hold the micro in reset while loading firmware */ + buf[0] = 0x80; + nxt200x_writebytes(state, 0x2B, buf,1); + + /* calculate firmware CRC */ + for (position = 0; position < fw->size; position++) { + crc = nxt200x_crc(crc, fw->data[position]); + } + + buf[0] = rambase >> 8; + buf[1] = rambase & 0xFF; + buf[2] = 0x81; + /* write starting address */ + nxt200x_writebytes(state,0x29,buf,3); + + for (position = 0; position < fw->size;) { + nxt200x_writebytes(state, 0x2C, &fw->data[position], + fw->size-position > 255 ? 255 : fw->size-position); + position += (fw->size-position > 255 ? 255 : fw->size-position); + } + buf[0] = crc >> 8; + buf[1] = crc & 0xFF; + + dprintk("firmware crc is 0x%02X 0x%02X\n", buf[0], buf[1]); + + /* write crc */ + nxt200x_writebytes(state, 0x2C, buf,2); + + /* do a read to stop things */ + nxt200x_readbytes(state, 0x2C, buf, 1); + + /* set transfer mode to complete */ + buf[0] = 0x80; + nxt200x_writebytes(state, 0x2B, buf,1); + + return 0; +}; + +static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct nxt200x_state* state = fe->demodulator_priv; + u8 buf[5]; + + /* stop the micro first */ + nxt200x_microcontroller_stop(state); + + if (state->demod_chip == NXT2004) { + /* make sure demod is set to digital */ + buf[0] = 0x04; + nxt200x_writebytes(state, 0x14, buf, 1); + buf[0] = 0x00; + nxt200x_writebytes(state, 0x17, buf, 1); + } + + /* set additional params */ + switch (p->modulation) { + case QAM_64: + case QAM_256: + /* Set punctured clock for QAM */ + /* This is just a guess since I am unable to test it */ + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 1); + break; + case VSB_8: + /* Set non-punctured clock for VSB */ + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + break; + default: + return -EINVAL; + } + + if (fe->ops.tuner_ops.calc_regs) { + /* get tuning information */ + fe->ops.tuner_ops.calc_regs(fe, buf, 5); + + /* write frequency information */ + nxt200x_writetuner(state, buf); + } + + /* reset the agc now that tuning has been completed */ + nxt200x_agc_reset(state); + + /* set target power level */ + switch (p->modulation) { + case QAM_64: + case QAM_256: + buf[0] = 0x74; + break; + case VSB_8: + buf[0] = 0x70; + break; + default: + return -EINVAL; + } + nxt200x_writebytes(state, 0x42, buf, 1); + + /* configure sdm */ + switch (state->demod_chip) { + case NXT2002: + buf[0] = 0x87; + break; + case NXT2004: + buf[0] = 0x07; + break; + default: + return -EINVAL; + } + nxt200x_writebytes(state, 0x57, buf, 1); + + /* write sdm1 input */ + buf[0] = 0x10; + buf[1] = 0x00; + switch (state->demod_chip) { + case NXT2002: + nxt200x_writereg_multibyte(state, 0x58, buf, 2); + break; + case NXT2004: + nxt200x_writebytes(state, 0x58, buf, 2); + break; + default: + return -EINVAL; + } + + /* write sdmx input */ + switch (p->modulation) { + case QAM_64: + buf[0] = 0x68; + break; + case QAM_256: + buf[0] = 0x64; + break; + case VSB_8: + buf[0] = 0x60; + break; + default: + return -EINVAL; + } + buf[1] = 0x00; + switch (state->demod_chip) { + case NXT2002: + nxt200x_writereg_multibyte(state, 0x5C, buf, 2); + break; + case NXT2004: + nxt200x_writebytes(state, 0x5C, buf, 2); + break; + default: + return -EINVAL; + } + + /* write adc power lpf fc */ + buf[0] = 0x05; + nxt200x_writebytes(state, 0x43, buf, 1); + + if (state->demod_chip == NXT2004) { + /* write ??? */ + buf[0] = 0x00; + buf[1] = 0x00; + nxt200x_writebytes(state, 0x46, buf, 2); + } + + /* write accumulator2 input */ + buf[0] = 0x80; + buf[1] = 0x00; + switch (state->demod_chip) { + case NXT2002: + nxt200x_writereg_multibyte(state, 0x4B, buf, 2); + break; + case NXT2004: + nxt200x_writebytes(state, 0x4B, buf, 2); + break; + default: + return -EINVAL; + } + + /* write kg1 */ + buf[0] = 0x00; + nxt200x_writebytes(state, 0x4D, buf, 1); + + /* write sdm12 lpf fc */ + buf[0] = 0x44; + nxt200x_writebytes(state, 0x55, buf, 1); + + /* write agc control reg */ + buf[0] = 0x04; + nxt200x_writebytes(state, 0x41, buf, 1); + + if (state->demod_chip == NXT2004) { + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x24; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + + /* soft reset? */ + nxt200x_readreg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x10; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + nxt200x_readreg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x04; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x81, buf, 1); + buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00; + nxt200x_writereg_multibyte(state, 0x82, buf, 3); + nxt200x_readreg_multibyte(state, 0x88, buf, 1); + buf[0] = 0x11; + nxt200x_writereg_multibyte(state, 0x88, buf, 1); + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x44; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + } + + /* write agc ucgp0 */ + switch (p->modulation) { + case QAM_64: + buf[0] = 0x02; + break; + case QAM_256: + buf[0] = 0x03; + break; + case VSB_8: + buf[0] = 0x00; + break; + default: + return -EINVAL; + } + nxt200x_writebytes(state, 0x30, buf, 1); + + /* write agc control reg */ + buf[0] = 0x00; + nxt200x_writebytes(state, 0x41, buf, 1); + + /* write accumulator2 input */ + buf[0] = 0x80; + buf[1] = 0x00; + switch (state->demod_chip) { + case NXT2002: + nxt200x_writereg_multibyte(state, 0x49, buf, 2); + nxt200x_writereg_multibyte(state, 0x4B, buf, 2); + break; + case NXT2004: + nxt200x_writebytes(state, 0x49, buf, 2); + nxt200x_writebytes(state, 0x4B, buf, 2); + break; + default: + return -EINVAL; + } + + /* write agc control reg */ + buf[0] = 0x04; + nxt200x_writebytes(state, 0x41, buf, 1); + + nxt200x_microcontroller_start(state); + + if (state->demod_chip == NXT2004) { + nxt2004_microcontroller_init(state); + + /* ???? */ + buf[0] = 0xF0; + buf[1] = 0x00; + nxt200x_writebytes(state, 0x5C, buf, 2); + } + + /* adjacent channel detection should be done here, but I don't + have any stations with this need so I cannot test it */ + + return 0; +} + +static int nxt200x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct nxt200x_state* state = fe->demodulator_priv; + u8 lock; + nxt200x_readbytes(state, 0x31, &lock, 1); + + *status = 0; + if (lock & 0x20) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + } + return 0; +} + +static int nxt200x_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct nxt200x_state* state = fe->demodulator_priv; + u8 b[3]; + + nxt200x_readreg_multibyte(state, 0xE6, b, 3); + + *ber = ((b[0] << 8) + b[1]) * 8; + + return 0; +} + +static int nxt200x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct nxt200x_state* state = fe->demodulator_priv; + u8 b[2]; + u16 temp = 0; + + /* setup to read cluster variance */ + b[0] = 0x00; + nxt200x_writebytes(state, 0xA1, b, 1); + + /* get multreg val */ + nxt200x_readreg_multibyte(state, 0xA6, b, 2); + + temp = (b[0] << 8) | b[1]; + *strength = ((0x7FFF - temp) & 0x0FFF) * 16; + + return 0; +} + +static int nxt200x_read_snr(struct dvb_frontend* fe, u16* snr) +{ + + struct nxt200x_state* state = fe->demodulator_priv; + u8 b[2]; + u16 temp = 0, temp2; + u32 snrdb = 0; + + /* setup to read cluster variance */ + b[0] = 0x00; + nxt200x_writebytes(state, 0xA1, b, 1); + + /* get multreg val from 0xA6 */ + nxt200x_readreg_multibyte(state, 0xA6, b, 2); + + temp = (b[0] << 8) | b[1]; + temp2 = 0x7FFF - temp; + + /* snr will be in db */ + if (temp2 > 0x7F00) + snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) ); + else if (temp2 > 0x7EC0) + snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) ); + else if (temp2 > 0x7C00) + snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) ); + else + snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) ); + + /* the value reported back from the frontend will be FFFF=32db 0000=0db */ + *snr = snrdb * (0xFFFF/32000); + + return 0; +} + +static int nxt200x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct nxt200x_state* state = fe->demodulator_priv; + u8 b[3]; + + nxt200x_readreg_multibyte(state, 0xE6, b, 3); + *ucblocks = b[2]; + + return 0; +} + +static int nxt200x_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int nxt2002_init(struct dvb_frontend* fe) +{ + struct nxt200x_state* state = fe->demodulator_priv; + const struct firmware *fw; + int ret; + u8 buf[2]; + + /* request the firmware, this will block until someone uploads it */ + pr_debug("%s: Waiting for firmware upload (%s)...\n", + __func__, NXT2002_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, NXT2002_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + pr_debug("%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + pr_err("%s: No firmware uploaded (timeout or file not found?)\n", + __func__); + return ret; + } + + ret = nxt2002_load_firmware(fe, fw); + release_firmware(fw); + if (ret) { + pr_err("%s: Writing firmware to device failed\n", __func__); + return ret; + } + pr_info("%s: Firmware upload complete\n", __func__); + + /* Put the micro into reset */ + nxt200x_microcontroller_stop(state); + + /* ensure transfer is complete */ + buf[0]=0x00; + nxt200x_writebytes(state, 0x2B, buf, 1); + + /* Put the micro into reset for real this time */ + nxt200x_microcontroller_stop(state); + + /* soft reset everything (agc,frontend,eq,fec)*/ + buf[0] = 0x0F; + nxt200x_writebytes(state, 0x08, buf, 1); + buf[0] = 0x00; + nxt200x_writebytes(state, 0x08, buf, 1); + + /* write agc sdm configure */ + buf[0] = 0xF1; + nxt200x_writebytes(state, 0x57, buf, 1); + + /* write mod output format */ + buf[0] = 0x20; + nxt200x_writebytes(state, 0x09, buf, 1); + + /* write fec mpeg mode */ + buf[0] = 0x7E; + buf[1] = 0x00; + nxt200x_writebytes(state, 0xE9, buf, 2); + + /* write mux selection */ + buf[0] = 0x00; + nxt200x_writebytes(state, 0xCC, buf, 1); + + return 0; +} + +static int nxt2004_init(struct dvb_frontend* fe) +{ + struct nxt200x_state* state = fe->demodulator_priv; + const struct firmware *fw; + int ret; + u8 buf[3]; + + /* ??? */ + buf[0]=0x00; + nxt200x_writebytes(state, 0x1E, buf, 1); + + /* request the firmware, this will block until someone uploads it */ + pr_debug("%s: Waiting for firmware upload (%s)...\n", + __func__, NXT2004_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, NXT2004_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + pr_debug("%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + pr_err("%s: No firmware uploaded (timeout or file not found?)\n", + __func__); + return ret; + } + + ret = nxt2004_load_firmware(fe, fw); + release_firmware(fw); + if (ret) { + pr_err("%s: Writing firmware to device failed\n", __func__); + return ret; + } + pr_info("%s: Firmware upload complete\n", __func__); + + /* ensure transfer is complete */ + buf[0] = 0x01; + nxt200x_writebytes(state, 0x19, buf, 1); + + nxt2004_microcontroller_init(state); + nxt200x_microcontroller_stop(state); + nxt200x_microcontroller_stop(state); + nxt2004_microcontroller_init(state); + nxt200x_microcontroller_stop(state); + + /* soft reset everything (agc,frontend,eq,fec)*/ + buf[0] = 0xFF; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + + /* write agc sdm configure */ + buf[0] = 0xD7; + nxt200x_writebytes(state, 0x57, buf, 1); + + /* ???*/ + buf[0] = 0x07; + buf[1] = 0xfe; + nxt200x_writebytes(state, 0x35, buf, 2); + buf[0] = 0x12; + nxt200x_writebytes(state, 0x34, buf, 1); + buf[0] = 0x80; + nxt200x_writebytes(state, 0x21, buf, 1); + + /* ???*/ + buf[0] = 0x21; + nxt200x_writebytes(state, 0x0A, buf, 1); + + /* ???*/ + buf[0] = 0x01; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + + /* write fec mpeg mode */ + buf[0] = 0x7E; + buf[1] = 0x00; + nxt200x_writebytes(state, 0xE9, buf, 2); + + /* write mux selection */ + buf[0] = 0x00; + nxt200x_writebytes(state, 0xCC, buf, 1); + + /* ???*/ + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + + /* soft reset? */ + nxt200x_readreg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x10; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + nxt200x_readreg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + + /* ???*/ + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x01; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x70; + nxt200x_writereg_multibyte(state, 0x81, buf, 1); + buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66; + nxt200x_writereg_multibyte(state, 0x82, buf, 3); + + nxt200x_readreg_multibyte(state, 0x88, buf, 1); + buf[0] = 0x11; + nxt200x_writereg_multibyte(state, 0x88, buf, 1); + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x40; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + + nxt200x_readbytes(state, 0x10, buf, 1); + buf[0] = 0x10; + nxt200x_writebytes(state, 0x10, buf, 1); + nxt200x_readbytes(state, 0x0A, buf, 1); + buf[0] = 0x21; + nxt200x_writebytes(state, 0x0A, buf, 1); + + nxt2004_microcontroller_init(state); + + buf[0] = 0x21; + nxt200x_writebytes(state, 0x0A, buf, 1); + buf[0] = 0x7E; + nxt200x_writebytes(state, 0xE9, buf, 1); + buf[0] = 0x00; + nxt200x_writebytes(state, 0xEA, buf, 1); + + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + + /* soft reset? */ + nxt200x_readreg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x10; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + nxt200x_readreg_multibyte(state, 0x08, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x08, buf, 1); + + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x04; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x00; + nxt200x_writereg_multibyte(state, 0x81, buf, 1); + buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00; + nxt200x_writereg_multibyte(state, 0x82, buf, 3); + + nxt200x_readreg_multibyte(state, 0x88, buf, 1); + buf[0] = 0x11; + nxt200x_writereg_multibyte(state, 0x88, buf, 1); + + nxt200x_readreg_multibyte(state, 0x80, buf, 1); + buf[0] = 0x44; + nxt200x_writereg_multibyte(state, 0x80, buf, 1); + + /* initialize tuner */ + nxt200x_readbytes(state, 0x10, buf, 1); + buf[0] = 0x12; + nxt200x_writebytes(state, 0x10, buf, 1); + buf[0] = 0x04; + nxt200x_writebytes(state, 0x13, buf, 1); + buf[0] = 0x00; + nxt200x_writebytes(state, 0x16, buf, 1); + buf[0] = 0x04; + nxt200x_writebytes(state, 0x14, buf, 1); + buf[0] = 0x00; + nxt200x_writebytes(state, 0x14, buf, 1); + nxt200x_writebytes(state, 0x17, buf, 1); + nxt200x_writebytes(state, 0x14, buf, 1); + nxt200x_writebytes(state, 0x17, buf, 1); + + return 0; +} + +static int nxt200x_init(struct dvb_frontend* fe) +{ + struct nxt200x_state* state = fe->demodulator_priv; + int ret = 0; + + if (!state->initialised) { + switch (state->demod_chip) { + case NXT2002: + ret = nxt2002_init(fe); + break; + case NXT2004: + ret = nxt2004_init(fe); + break; + default: + return -EINVAL; + } + state->initialised = 1; + } + return ret; +} + +static int nxt200x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 500; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void nxt200x_release(struct dvb_frontend* fe) +{ + struct nxt200x_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops nxt200x_ops; + +struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, + struct i2c_adapter* i2c) +{ + struct nxt200x_state* state = NULL; + u8 buf [] = {0,0,0,0,0}; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct nxt200x_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + + /* read card id */ + nxt200x_readbytes(state, 0x00, buf, 5); + dprintk("NXT info: %*ph\n", 5, buf); + + /* set demod chip */ + switch (buf[0]) { + case 0x04: + state->demod_chip = NXT2002; + pr_info("NXT2002 Detected\n"); + break; + case 0x05: + state->demod_chip = NXT2004; + pr_info("NXT2004 Detected\n"); + break; + default: + goto error; + } + + /* make sure demod chip is supported */ + switch (state->demod_chip) { + case NXT2002: + if (buf[0] != 0x04) goto error; /* device id */ + if (buf[1] != 0x02) goto error; /* fab id */ + if (buf[2] != 0x11) goto error; /* month */ + if (buf[3] != 0x20) goto error; /* year msb */ + if (buf[4] != 0x00) goto error; /* year lsb */ + break; + case NXT2004: + if (buf[0] != 0x05) goto error; /* device id */ + break; + default: + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &nxt200x_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + pr_err("Unknown/Unsupported NXT chip: %*ph\n", 5, buf); + return NULL; +} + +static const struct dvb_frontend_ops nxt200x_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Nextwave NXT200X VSB/QAM frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166666, /* stepsize is just a guess */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256 + }, + + .release = nxt200x_release, + + .init = nxt200x_init, + .sleep = nxt200x_sleep, + + .set_frontend = nxt200x_setup_frontend_parameters, + .get_tune_settings = nxt200x_get_tune_settings, + + .read_status = nxt200x_read_status, + .read_ber = nxt200x_read_ber, + .read_signal_strength = nxt200x_read_signal_strength, + .read_snr = nxt200x_read_snr, + .read_ucblocks = nxt200x_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); +MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(nxt200x_attach); + diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h new file mode 100644 index 0000000000..6b03aeb746 --- /dev/null +++ b/drivers/media/dvb-frontends/nxt200x.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for NXT2002 and NXT2004 - VSB/QAM + * + * Copyright (C) 2005 Kirk Lapray (kirk.lapray@gmail.com) + * based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net> + * and nxt2004 by Jean-Francois Thibert (jeanfrancois@sagetv.com) +*/ + +#ifndef NXT200X_H +#define NXT200X_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +typedef enum nxt_chip_t { + NXTUNDEFINED, + NXT2002, + NXT2004 +}nxt_chip_type; + +struct nxt200x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* need to set device param for start_dma */ + int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); +}; + +#if IS_REACHABLE(CONFIG_DVB_NXT200X) +extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_NXT200X + +#endif /* NXT200X_H */ diff --git a/drivers/media/dvb-frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c new file mode 100644 index 0000000000..e8d4940370 --- /dev/null +++ b/drivers/media/dvb-frontends/nxt6000.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + NxtWave Communications - NXT6000 demodulator driver + + Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org> + Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au> + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "nxt6000_priv.h" +#include "nxt6000.h" + + + +struct nxt6000_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct nxt6000_config* config; + struct dvb_frontend frontend; +}; + +static int debug; +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; + int ret; + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) + dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret); + + return (ret != 1) ? -EIO : 0; +} + +static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msgs[] = { + {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} + }; + + ret = i2c_transfer(state->i2c, msgs, 2); + + if (ret != 2) + dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret); + + return b1[0]; +} + +static void nxt6000_reset(struct nxt6000_state* state) +{ + u8 val; + + val = nxt6000_readreg(state, OFDM_COR_CTL); + + nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT); + nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT); +} + +static int nxt6000_set_bandwidth(struct nxt6000_state *state, u32 bandwidth) +{ + u16 nominal_rate; + int result; + + switch (bandwidth) { + case 6000000: + nominal_rate = 0x55B7; + break; + + case 7000000: + nominal_rate = 0x6400; + break; + + case 8000000: + nominal_rate = 0x7249; + break; + + default: + return -EINVAL; + } + + if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) + return result; + + return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); +} + +static int nxt6000_set_guard_interval(struct nxt6000_state *state, + enum fe_guard_interval guard_interval) +{ + switch (guard_interval) { + + case GUARD_INTERVAL_1_32: + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); + + case GUARD_INTERVAL_1_16: + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); + + case GUARD_INTERVAL_AUTO: + case GUARD_INTERVAL_1_8: + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); + + case GUARD_INTERVAL_1_4: + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); + + default: + return -EINVAL; + } +} + +static int nxt6000_set_inversion(struct nxt6000_state *state, + enum fe_spectral_inversion inversion) +{ + switch (inversion) { + + case INVERSION_OFF: + return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00); + + case INVERSION_ON: + return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV); + + default: + return -EINVAL; + + } +} + +static int +nxt6000_set_transmission_mode(struct nxt6000_state *state, + enum fe_transmit_mode transmission_mode) +{ + int result; + + switch (transmission_mode) { + + case TRANSMISSION_MODE_2K: + if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) + return result; + + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); + + case TRANSMISSION_MODE_8K: + case TRANSMISSION_MODE_AUTO: + if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) + return result; + + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); + + default: + return -EINVAL; + + } +} + +static void nxt6000_setup(struct dvb_frontend* fe) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM); + nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01); + nxt6000_writereg(state, VIT_BERTIME_2, 0x00); // BER Timer = 0x000200 * 256 = 131072 bits + nxt6000_writereg(state, VIT_BERTIME_1, 0x02); // + nxt6000_writereg(state, VIT_BERTIME_0, 0x00); // + nxt6000_writereg(state, VIT_COR_INTEN, 0x98); // Enable BER interrupts + nxt6000_writereg(state, VIT_COR_CTL, 0x82); // Enable BER measurement + nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC | 0x02 ); + nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F)); + nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); + nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); + nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06); + nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31); + nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04); + nxt6000_writereg(state, CAS_FREQ, 0xBB); /* CHECKME */ + nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2); + nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256); + nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49); + nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72); + nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5); + nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2); + nxt6000_writereg(state, DIAG_CONFIG, TB_SET); + + if (state->config->clock_inversion) + nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION); + else + nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); + + nxt6000_writereg(state, TS_FORMAT, 0); +} + +static void nxt6000_dump_status(struct nxt6000_state *state) +{ + u8 val; + +#if 0 + pr_info("RS_COR_STAT: 0x%02X\n", + nxt6000_readreg(fe, RS_COR_STAT)); + pr_info("VIT_SYNC_STATUS: 0x%02X\n", + nxt6000_readreg(fe, VIT_SYNC_STATUS)); + pr_info("OFDM_COR_STAT: 0x%02X\n", + nxt6000_readreg(fe, OFDM_COR_STAT)); + pr_info("OFDM_SYR_STAT: 0x%02X\n", + nxt6000_readreg(fe, OFDM_SYR_STAT)); + pr_info("OFDM_TPS_RCVD_1: 0x%02X\n", + nxt6000_readreg(fe, OFDM_TPS_RCVD_1)); + pr_info("OFDM_TPS_RCVD_2: 0x%02X\n", + nxt6000_readreg(fe, OFDM_TPS_RCVD_2)); + pr_info("OFDM_TPS_RCVD_3: 0x%02X\n", + nxt6000_readreg(fe, OFDM_TPS_RCVD_3)); + pr_info("OFDM_TPS_RCVD_4: 0x%02X\n", + nxt6000_readreg(fe, OFDM_TPS_RCVD_4)); + pr_info("OFDM_TPS_RESERVED_1: 0x%02X\n", + nxt6000_readreg(fe, OFDM_TPS_RESERVED_1)); + pr_info("OFDM_TPS_RESERVED_2: 0x%02X\n", + nxt6000_readreg(fe, OFDM_TPS_RESERVED_2)); +#endif + pr_info("NXT6000 status:"); + + val = nxt6000_readreg(state, RS_COR_STAT); + + pr_cont(" DATA DESCR LOCK: %d,", val & 0x01); + pr_cont(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01); + + val = nxt6000_readreg(state, VIT_SYNC_STATUS); + + pr_cont(" VITERBI LOCK: %d,", (val >> 7) & 0x01); + + switch ((val >> 4) & 0x07) { + + case 0x00: + pr_cont(" VITERBI CODERATE: 1/2,"); + break; + + case 0x01: + pr_cont(" VITERBI CODERATE: 2/3,"); + break; + + case 0x02: + pr_cont(" VITERBI CODERATE: 3/4,"); + break; + + case 0x03: + pr_cont(" VITERBI CODERATE: 5/6,"); + break; + + case 0x04: + pr_cont(" VITERBI CODERATE: 7/8,"); + break; + + default: + pr_cont(" VITERBI CODERATE: Reserved,"); + + } + + val = nxt6000_readreg(state, OFDM_COR_STAT); + + pr_cont(" CHCTrack: %d,", (val >> 7) & 0x01); + pr_cont(" TPSLock: %d,", (val >> 6) & 0x01); + pr_cont(" SYRLock: %d,", (val >> 5) & 0x01); + pr_cont(" AGCLock: %d,", (val >> 4) & 0x01); + + switch (val & 0x0F) { + + case 0x00: + pr_cont(" CoreState: IDLE,"); + break; + + case 0x02: + pr_cont(" CoreState: WAIT_AGC,"); + break; + + case 0x03: + pr_cont(" CoreState: WAIT_SYR,"); + break; + + case 0x04: + pr_cont(" CoreState: WAIT_PPM,"); + break; + + case 0x01: + pr_cont(" CoreState: WAIT_TRL,"); + break; + + case 0x05: + pr_cont(" CoreState: WAIT_TPS,"); + break; + + case 0x06: + pr_cont(" CoreState: MONITOR_TPS,"); + break; + + default: + pr_cont(" CoreState: Reserved,"); + + } + + val = nxt6000_readreg(state, OFDM_SYR_STAT); + + pr_cont(" SYRLock: %d,", (val >> 4) & 0x01); + pr_cont(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K"); + + switch ((val >> 4) & 0x03) { + + case 0x00: + pr_cont(" SYRGuard: 1/32,"); + break; + + case 0x01: + pr_cont(" SYRGuard: 1/16,"); + break; + + case 0x02: + pr_cont(" SYRGuard: 1/8,"); + break; + + case 0x03: + pr_cont(" SYRGuard: 1/4,"); + break; + } + + val = nxt6000_readreg(state, OFDM_TPS_RCVD_3); + + switch ((val >> 4) & 0x07) { + + case 0x00: + pr_cont(" TPSLP: 1/2,"); + break; + + case 0x01: + pr_cont(" TPSLP: 2/3,"); + break; + + case 0x02: + pr_cont(" TPSLP: 3/4,"); + break; + + case 0x03: + pr_cont(" TPSLP: 5/6,"); + break; + + case 0x04: + pr_cont(" TPSLP: 7/8,"); + break; + + default: + pr_cont(" TPSLP: Reserved,"); + + } + + switch (val & 0x07) { + + case 0x00: + pr_cont(" TPSHP: 1/2,"); + break; + + case 0x01: + pr_cont(" TPSHP: 2/3,"); + break; + + case 0x02: + pr_cont(" TPSHP: 3/4,"); + break; + + case 0x03: + pr_cont(" TPSHP: 5/6,"); + break; + + case 0x04: + pr_cont(" TPSHP: 7/8,"); + break; + + default: + pr_cont(" TPSHP: Reserved,"); + + } + + val = nxt6000_readreg(state, OFDM_TPS_RCVD_4); + + pr_cont(" TPSMode: %s,", val & 0x01 ? "8K" : "2K"); + + switch ((val >> 4) & 0x03) { + + case 0x00: + pr_cont(" TPSGuard: 1/32,"); + break; + + case 0x01: + pr_cont(" TPSGuard: 1/16,"); + break; + + case 0x02: + pr_cont(" TPSGuard: 1/8,"); + break; + + case 0x03: + pr_cont(" TPSGuard: 1/4,"); + break; + + } + + /* Strange magic required to gain access to RF_AGC_STATUS */ + nxt6000_readreg(state, RF_AGC_VAL_1); + val = nxt6000_readreg(state, RF_AGC_STATUS); + val = nxt6000_readreg(state, RF_AGC_STATUS); + + pr_cont(" RF AGC LOCK: %d,", (val >> 4) & 0x01); + pr_cont("\n"); +} + +static int nxt6000_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + u8 core_status; + struct nxt6000_state* state = fe->demodulator_priv; + + *status = 0; + + core_status = nxt6000_readreg(state, OFDM_COR_STAT); + + if (core_status & AGCLOCKED) + *status |= FE_HAS_SIGNAL; + + if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK) + *status |= FE_HAS_CARRIER; + + if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC) + *status |= FE_HAS_VITERBI; + + if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS) + *status |= FE_HAS_SYNC; + + if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))) + *status |= FE_HAS_LOCK; + + if (debug) + nxt6000_dump_status(state); + + return 0; +} + +static int nxt6000_init(struct dvb_frontend* fe) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + nxt6000_reset(state); + nxt6000_setup(fe); + + return 0; +} + +static int nxt6000_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct nxt6000_state* state = fe->demodulator_priv; + int result; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + result = nxt6000_set_bandwidth(state, p->bandwidth_hz); + if (result < 0) + return result; + + result = nxt6000_set_guard_interval(state, p->guard_interval); + if (result < 0) + return result; + + result = nxt6000_set_transmission_mode(state, p->transmission_mode); + if (result < 0) + return result; + + result = nxt6000_set_inversion(state, p->inversion); + if (result < 0) + return result; + + msleep(500); + return 0; +} + +static void nxt6000_release(struct dvb_frontend* fe) +{ + struct nxt6000_state* state = fe->demodulator_priv; + kfree(state); +} + +static int nxt6000_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + *snr = nxt6000_readreg( state, OFDM_CHC_SNR) / 8; + + return 0; +} + +static int nxt6000_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + nxt6000_writereg( state, VIT_COR_INTSTAT, 0x18 ); + + *ber = (nxt6000_readreg( state, VIT_BER_1 ) << 8 ) | + nxt6000_readreg( state, VIT_BER_0 ); + + nxt6000_writereg( state, VIT_COR_INTSTAT, 0x18); // Clear BER Done interrupts + + return 0; +} + +static int nxt6000_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + *signal_strength = (short) (511 - + (nxt6000_readreg(state, AGC_GAIN_1) + + ((nxt6000_readreg(state, AGC_GAIN_2) & 0x03) << 8))); + + return 0; +} + +static int nxt6000_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 500; + return 0; +} + +static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + if (enable) { + return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); + } else { + return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); + } +} + +static const struct dvb_frontend_ops nxt6000_ops; + +struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c) +{ + struct nxt6000_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops nxt6000_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "NxtWave NXT6000 DVB-T", + .frequency_min_hz = 0, + .frequency_max_hz = 863250 * kHz, + .frequency_stepsize_hz = 62500, + /*.frequency_tolerance = *//* FIXME: 12% of SR */ + .symbol_rate_min = 0, /* FIXME */ + .symbol_rate_max = 9360000, /* FIXME */ + .symbol_rate_tolerance = 4000, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = nxt6000_release, + + .init = nxt6000_init, + .i2c_gate_ctrl = nxt6000_i2c_gate_ctrl, + + .get_tune_settings = nxt6000_fe_get_tune_settings, + + .set_frontend = nxt6000_set_frontend, + + .read_status = nxt6000_read_status, + .read_ber = nxt6000_read_ber, + .read_signal_strength = nxt6000_read_signal_strength, + .read_snr = nxt6000_read_snr, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver"); +MODULE_AUTHOR("Florian Schirmer"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(nxt6000_attach); diff --git a/drivers/media/dvb-frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h new file mode 100644 index 0000000000..28d965e53c --- /dev/null +++ b/drivers/media/dvb-frontends/nxt6000.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + NxtWave Communications - NXT6000 demodulator driver + + Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org> + Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au> + +*/ + +#ifndef NXT6000_H +#define NXT6000_H + +#include <linux/dvb/frontend.h> + +struct nxt6000_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* should clock inversion be used? */ + u8 clock_inversion:1; +}; + +#if IS_REACHABLE(CONFIG_DVB_NXT6000) +extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_NXT6000 + +#endif // NXT6000_H diff --git a/drivers/media/dvb-frontends/nxt6000_priv.h b/drivers/media/dvb-frontends/nxt6000_priv.h new file mode 100644 index 0000000000..d317df02c8 --- /dev/null +++ b/drivers/media/dvb-frontends/nxt6000_priv.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Public Include File for DRV6000 users + * (ie. NxtWave Communications - NXT6000 demodulator driver) + * + * Copyright (C) 2001 NxtWave Communications, Inc. + * + */ + +/* Nxt6000 Register Addresses and Bit Masks */ + +/* Maximum Register Number */ +#define MAXNXT6000REG (0x9A) + +/* 0x1B A_VIT_BER_0 aka 0x3A */ +#define A_VIT_BER_0 (0x1B) + +/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */ +#define A_VIT_BER_TIMER_0 (0x1D) + +/* 0x21 RS_COR_STAT */ +#define RS_COR_STAT (0x21) +#define RSCORESTATUS (0x03) + +/* 0x22 RS_COR_INTEN */ +#define RS_COR_INTEN (0x22) + +/* 0x23 RS_COR_INSTAT */ +#define RS_COR_INSTAT (0x23) +#define INSTAT_ERROR (0x04) +#define LOCK_LOSS_BITS (0x03) + +/* 0x24 RS_COR_SYNC_PARAM */ +#define RS_COR_SYNC_PARAM (0x24) +#define SYNC_PARAM (0x03) + +/* 0x25 BER_CTRL */ +#define BER_CTRL (0x25) +#define BER_ENABLE (0x02) +#define BER_RESET (0x01) + +/* 0x26 BER_PAY */ +#define BER_PAY (0x26) + +/* 0x27 BER_PKT_L */ +#define BER_PKT_L (0x27) +#define BER_PKTOVERFLOW (0x80) + +/* 0x30 VIT_COR_CTL */ +#define VIT_COR_CTL (0x30) +#define BER_CONTROL (0x02) +#define VIT_COR_MASK (0x82) +#define VIT_COR_RESYNC (0x80) + + +/* 0x32 VIT_SYNC_STATUS */ +#define VIT_SYNC_STATUS (0x32) +#define VITINSYNC (0x80) + +/* 0x33 VIT_COR_INTEN */ +#define VIT_COR_INTEN (0x33) +#define GLOBAL_ENABLE (0x80) + +/* 0x34 VIT_COR_INTSTAT */ +#define VIT_COR_INTSTAT (0x34) +#define BER_DONE (0x08) +#define BER_OVERFLOW (0x10) + +/* 0x38 VIT_BERTIME_2 */ +#define VIT_BERTIME_2 (0x38) + +/* 0x39 VIT_BERTIME_1 */ +#define VIT_BERTIME_1 (0x39) + +/* 0x3A VIT_BERTIME_0 */ +#define VIT_BERTIME_0 (0x3a) + + /* 0x38 OFDM_BERTimer *//* Use the alias registers */ +#define A_VIT_BER_TIMER_0 (0x1D) + + /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */ +#define A_VIT_BER_0 (0x1B) + +/* 0x3B VIT_BER_1 */ +#define VIT_BER_1 (0x3b) + +/* 0x3C VIT_BER_0 */ +#define VIT_BER_0 (0x3c) + +/* 0x40 OFDM_COR_CTL */ +#define OFDM_COR_CTL (0x40) +#define COREACT (0x20) +#define HOLDSM (0x10) +#define WAIT_AGC (0x02) +#define WAIT_SYR (0x03) + +/* 0x41 OFDM_COR_STAT */ +#define OFDM_COR_STAT (0x41) +#define COR_STATUS (0x0F) +#define MONITOR_TPS (0x06) +#define TPSLOCKED (0x40) +#define AGCLOCKED (0x10) + +/* 0x42 OFDM_COR_INTEN */ +#define OFDM_COR_INTEN (0x42) +#define TPSRCVBAD (0x04) +#define TPSRCVCHANGED (0x02) +#define TPSRCVUPDATE (0x01) + +/* 0x43 OFDM_COR_INSTAT */ +#define OFDM_COR_INSTAT (0x43) + +/* 0x44 OFDM_COR_MODEGUARD */ +#define OFDM_COR_MODEGUARD (0x44) +#define FORCEMODE (0x08) +#define FORCEMODE8K (0x04) + +/* 0x45 OFDM_AGC_CTL */ +#define OFDM_AGC_CTL (0x45) +#define INITIAL_AGC_BW (0x08) +#define AGCNEG (0x02) +#define AGCLAST (0x10) + +/* 0x48 OFDM_AGC_TARGET */ +#define OFDM_AGC_TARGET (0x48) +#define OFDM_AGC_TARGET_DEFAULT (0x28) +#define OFDM_AGC_TARGET_IMPULSE (0x38) + +/* 0x49 OFDM_AGC_GAIN_1 */ +#define OFDM_AGC_GAIN_1 (0x49) + +/* 0x4B OFDM_ITB_CTL */ +#define OFDM_ITB_CTL (0x4B) +#define ITBINV (0x01) + +/* 0x49 AGC_GAIN_1 */ +#define AGC_GAIN_1 (0x49) + +/* 0x4A AGC_GAIN_2 */ +#define AGC_GAIN_2 (0x4A) + +/* 0x4C OFDM_ITB_FREQ_1 */ +#define OFDM_ITB_FREQ_1 (0x4C) + +/* 0x4D OFDM_ITB_FREQ_2 */ +#define OFDM_ITB_FREQ_2 (0x4D) + +/* 0x4E OFDM_CAS_CTL */ +#define OFDM_CAS_CTL (0x4E) +#define ACSDIS (0x40) +#define CCSEN (0x80) + +/* 0x4F CAS_FREQ */ +#define CAS_FREQ (0x4F) + +/* 0x51 OFDM_SYR_CTL */ +#define OFDM_SYR_CTL (0x51) +#define SIXTH_ENABLE (0x80) +#define SYR_TRACKING_DISABLE (0x01) + +/* 0x52 OFDM_SYR_STAT */ +#define OFDM_SYR_STAT (0x52) +#define GI14_2K_SYR_LOCK (0x13) +#define GI14_8K_SYR_LOCK (0x17) +#define GI14_SYR_LOCK (0x10) + +/* 0x55 OFDM_SYR_OFFSET_1 */ +#define OFDM_SYR_OFFSET_1 (0x55) + +/* 0x56 OFDM_SYR_OFFSET_2 */ +#define OFDM_SYR_OFFSET_2 (0x56) + +/* 0x58 OFDM_SCR_CTL */ +#define OFDM_SCR_CTL (0x58) +#define SYR_ADJ_DECAY_MASK (0x70) +#define SYR_ADJ_DECAY (0x30) + +/* 0x59 OFDM_PPM_CTL_1 */ +#define OFDM_PPM_CTL_1 (0x59) +#define PPMMAX_MASK (0x30) +#define PPM256 (0x30) + +/* 0x5B OFDM_TRL_NOMINALRATE_1 */ +#define OFDM_TRL_NOMINALRATE_1 (0x5B) + +/* 0x5C OFDM_TRL_NOMINALRATE_2 */ +#define OFDM_TRL_NOMINALRATE_2 (0x5C) + +/* 0x5D OFDM_TRL_TIME_1 */ +#define OFDM_TRL_TIME_1 (0x5D) + +/* 0x60 OFDM_CRL_FREQ_1 */ +#define OFDM_CRL_FREQ_1 (0x60) + +/* 0x63 OFDM_CHC_CTL_1 */ +#define OFDM_CHC_CTL_1 (0x63) +#define MANMEAN1 (0xF0); +#define CHCFIR (0x01) + +/* 0x64 OFDM_CHC_SNR */ +#define OFDM_CHC_SNR (0x64) + +/* 0x65 OFDM_BDI_CTL */ +#define OFDM_BDI_CTL (0x65) +#define LP_SELECT (0x02) + +/* 0x67 OFDM_TPS_RCVD_1 */ +#define OFDM_TPS_RCVD_1 (0x67) +#define TPSFRAME (0x03) + +/* 0x68 OFDM_TPS_RCVD_2 */ +#define OFDM_TPS_RCVD_2 (0x68) + +/* 0x69 OFDM_TPS_RCVD_3 */ +#define OFDM_TPS_RCVD_3 (0x69) + +/* 0x6A OFDM_TPS_RCVD_4 */ +#define OFDM_TPS_RCVD_4 (0x6A) + +/* 0x6B OFDM_TPS_RESERVED_1 */ +#define OFDM_TPS_RESERVED_1 (0x6B) + +/* 0x6C OFDM_TPS_RESERVED_2 */ +#define OFDM_TPS_RESERVED_2 (0x6C) + +/* 0x73 OFDM_MSC_REV */ +#define OFDM_MSC_REV (0x73) + +/* 0x76 OFDM_SNR_CARRIER_2 */ +#define OFDM_SNR_CARRIER_2 (0x76) +#define MEAN_MASK (0x80) +#define MEANBIT (0x80) + +/* 0x80 ANALOG_CONTROL_0 */ +#define ANALOG_CONTROL_0 (0x80) +#define POWER_DOWN_ADC (0x40) + +/* 0x81 ENABLE_TUNER_IIC */ +#define ENABLE_TUNER_IIC (0x81) +#define ENABLE_TUNER_BIT (0x01) + +/* 0x82 EN_DMD_RACQ */ +#define EN_DMD_RACQ (0x82) +#define EN_DMD_RACQ_REG_VAL (0x81) +#define EN_DMD_RACQ_REG_VAL_14 (0x01) + +/* 0x84 SNR_COMMAND */ +#define SNR_COMMAND (0x84) +#define SNRStat (0x80) + +/* 0x85 SNRCARRIERNUMBER_LSB */ +#define SNRCARRIERNUMBER_LSB (0x85) + +/* 0x87 SNRMINTHRESHOLD_LSB */ +#define SNRMINTHRESHOLD_LSB (0x87) + +/* 0x89 SNR_PER_CARRIER_LSB */ +#define SNR_PER_CARRIER_LSB (0x89) + +/* 0x8B SNRBELOWTHRESHOLD_LSB */ +#define SNRBELOWTHRESHOLD_LSB (0x8B) + +/* 0x91 RF_AGC_VAL_1 */ +#define RF_AGC_VAL_1 (0x91) + +/* 0x92 RF_AGC_STATUS */ +#define RF_AGC_STATUS (0x92) + +/* 0x98 DIAG_CONFIG */ +#define DIAG_CONFIG (0x98) +#define DIAG_MASK (0x70) +#define TB_SET (0x10) +#define TRAN_SELECT (0x07) +#define SERIAL_SELECT (0x01) + +/* 0x99 SUB_DIAG_MODE_SEL */ +#define SUB_DIAG_MODE_SEL (0x99) +#define CLKINVERSION (0x01) + +/* 0x9A TS_FORMAT */ +#define TS_FORMAT (0x9A) +#define ERROR_SENSE (0x08) +#define VALID_SENSE (0x04) +#define SYNC_SENSE (0x02) +#define GATED_CLOCK (0x01) + +#define NXT6000ASICDEVICE (0x0b) diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c new file mode 100644 index 0000000000..74e04c7cca --- /dev/null +++ b/drivers/media/dvb-frontends/or51132.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM + * + * Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org> + * + * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com> + * + * Based on code from Jack Kelliher (kelliher@xmission.com) + * Copyright (C) 2002 & pcHDTV, inc. +*/ + +/* + * This driver needs two external firmware files. Please copy + * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to + * /usr/lib/hotplug/firmware/ or /lib/firmware/ + * (depending on configuration of firmware hotplug). + */ +#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw" +#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <asm/byteorder.h> + +#include <linux/int_log.h> +#include <media/dvb_frontend.h> +#include "or51132.h" + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "or51132: " args); \ + } while (0) + + +struct or51132_state +{ + struct i2c_adapter* i2c; + + /* Configuration settings */ + const struct or51132_config* config; + + struct dvb_frontend frontend; + + /* Demodulator private data */ + enum fe_modulation current_modulation; + u32 snr; /* Result of last SNR calculation */ + + /* Tuner private data */ + u32 current_frequency; +}; + + +/* Write buffer to demod */ +static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) +{ + int err; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = (u8*)buf, .len = len }; + + /* msleep(20); */ /* doesn't appear to be necessary */ + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n", + msg.addr, msg.len, err); + return -EREMOTEIO; + } + return 0; +} + +/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00); + Less code and more efficient that loading a buffer on the stack with + the bytes to send and then calling or51132_writebuf() on that. */ +#define or51132_writebytes(state, data...) \ + ({ static const u8 _data[] = {data}; \ + or51132_writebuf(state, _data, sizeof(_data)); }) + +/* Read data from demod into buffer. Returns 0 on success. */ +static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len) +{ + int err; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = I2C_M_RD, .buf = buf, .len = len }; + + /* msleep(20); */ /* doesn't appear to be necessary */ + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n", + msg.addr, msg.len, err); + return -EREMOTEIO; + } + return 0; +} + +/* Reads a 16-bit demod register. Returns <0 on error. */ +static int or51132_readreg(struct or51132_state *state, u8 reg) +{ + u8 buf[2] = { 0x04, reg }; + struct i2c_msg msg[2] = { + {.addr = state->config->demod_address, .flags = 0, + .buf = buf, .len = 2 }, + {.addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = buf, .len = 2 }}; + int err; + + if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) { + printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n", + reg, err); + return -EREMOTEIO; + } + return buf[0] | (buf[1] << 8); +} + +static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) +{ + struct or51132_state* state = fe->demodulator_priv; + static const u8 run_buf[] = {0x7F,0x01}; + u8 rec_buf[8]; + u32 firmwareAsize, firmwareBsize; + int i,ret; + + dprintk("Firmware is %zd bytes\n",fw->size); + + /* Get size of firmware A and B */ + firmwareAsize = le32_to_cpu(*((__le32*)fw->data)); + dprintk("FirmwareA is %i bytes\n",firmwareAsize); + firmwareBsize = le32_to_cpu(*((__le32*)(fw->data+4))); + dprintk("FirmwareB is %i bytes\n",firmwareBsize); + + /* Upload firmware */ + if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) { + printk(KERN_WARNING "or51132: load_firmware error 1\n"); + return ret; + } + if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize], + firmwareBsize))) { + printk(KERN_WARNING "or51132: load_firmware error 2\n"); + return ret; + } + + if ((ret = or51132_writebuf(state, run_buf, 2))) { + printk(KERN_WARNING "or51132: load_firmware error 3\n"); + return ret; + } + if ((ret = or51132_writebuf(state, run_buf, 2))) { + printk(KERN_WARNING "or51132: load_firmware error 4\n"); + return ret; + } + + /* 50ms for operation to begin */ + msleep(50); + + /* Read back ucode version to besure we loaded correctly and are really up and running */ + /* Get uCode version */ + if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) { + printk(KERN_WARNING "or51132: load_firmware error a\n"); + return ret; + } + if ((ret = or51132_writebytes(state, 0x04, 0x17))) { + printk(KERN_WARNING "or51132: load_firmware error b\n"); + return ret; + } + if ((ret = or51132_writebytes(state, 0x00, 0x00))) { + printk(KERN_WARNING "or51132: load_firmware error c\n"); + return ret; + } + for (i=0;i<4;i++) { + /* Once upon a time, this command might have had something + to do with getting the firmware version, but it's + not used anymore: + {0x04,0x00,0x30,0x00,i+1} */ + /* Read 8 bytes, two bytes at a time */ + if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) { + printk(KERN_WARNING + "or51132: load_firmware error d - %d\n",i); + return ret; + } + } + + printk(KERN_WARNING + "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n", + rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2], + rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6], + rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f, + rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f); + + if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) { + printk(KERN_WARNING "or51132: load_firmware error e\n"); + return ret; + } + return 0; +}; + +static int or51132_init(struct dvb_frontend* fe) +{ + return 0; +} + +static int or51132_read_ber(struct dvb_frontend* fe, u32* ber) +{ + *ber = 0; + return 0; +} + +static int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int or51132_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int or51132_setmode(struct dvb_frontend* fe) +{ + struct or51132_state* state = fe->demodulator_priv; + u8 cmd_buf1[3] = {0x04, 0x01, 0x5f}; + u8 cmd_buf2[3] = {0x1c, 0x00, 0 }; + + dprintk("setmode %d\n",(int)state->current_modulation); + + switch (state->current_modulation) { + case VSB_8: + /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */ + cmd_buf1[2] = 0x50; + /* REC MODE inv IF spectrum, Normal */ + cmd_buf2[1] = 0x03; + /* Channel MODE ATSC/VSB8 */ + cmd_buf2[2] = 0x06; + break; + /* All QAM modes are: + Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high + REC MODE Normal Carrier Lock */ + case QAM_AUTO: + /* Channel MODE Auto QAM64/256 */ + cmd_buf2[2] = 0x4f; + break; + case QAM_256: + /* Channel MODE QAM256 */ + cmd_buf2[2] = 0x45; + break; + case QAM_64: + /* Channel MODE QAM64 */ + cmd_buf2[2] = 0x43; + break; + default: + printk(KERN_WARNING + "or51132: setmode: Modulation set to unsupported value (%d)\n", + state->current_modulation); + return -EINVAL; + } + + /* Set Receiver 1 register */ + if (or51132_writebuf(state, cmd_buf1, 3)) { + printk(KERN_WARNING "or51132: set_mode error 1\n"); + return -EREMOTEIO; + } + dprintk("set #1 to %02x\n", cmd_buf1[2]); + + /* Set operation mode in Receiver 6 register */ + if (or51132_writebuf(state, cmd_buf2, 3)) { + printk(KERN_WARNING "or51132: set_mode error 2\n"); + return -EREMOTEIO; + } + dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]); + + return 0; +} + +/* Some modulations use the same firmware. This classifies modulations + by the firmware they use. */ +#define MOD_FWCLASS_UNKNOWN 0 +#define MOD_FWCLASS_VSB 1 +#define MOD_FWCLASS_QAM 2 +static int modulation_fw_class(enum fe_modulation modulation) +{ + switch(modulation) { + case VSB_8: + return MOD_FWCLASS_VSB; + case QAM_AUTO: + case QAM_64: + case QAM_256: + return MOD_FWCLASS_QAM; + default: + return MOD_FWCLASS_UNKNOWN; + } +} + +static int or51132_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int ret; + struct or51132_state* state = fe->demodulator_priv; + const struct firmware *fw; + const char *fwname; + int clock_mode; + + /* Upload new firmware only if we need a different one */ + if (modulation_fw_class(state->current_modulation) != + modulation_fw_class(p->modulation)) { + switch (modulation_fw_class(p->modulation)) { + case MOD_FWCLASS_VSB: + dprintk("set_parameters VSB MODE\n"); + fwname = OR51132_VSB_FIRMWARE; + + /* Set non-punctured clock for VSB */ + clock_mode = 0; + break; + case MOD_FWCLASS_QAM: + dprintk("set_parameters QAM MODE\n"); + fwname = OR51132_QAM_FIRMWARE; + + /* Set punctured clock for QAM */ + clock_mode = 1; + break; + default: + printk("or51132: Modulation type(%d) UNSUPPORTED\n", + p->modulation); + return -1; + } + printk("or51132: Waiting for firmware upload(%s)...\n", + fwname); + ret = request_firmware(&fw, fwname, state->i2c->dev.parent); + if (ret) { + printk(KERN_WARNING "or51132: No firmware uploaded(timeout or file not found?)\n"); + return ret; + } + ret = or51132_load_firmware(fe, fw); + release_firmware(fw); + if (ret) { + printk(KERN_WARNING "or51132: Writing firmware to device failed!\n"); + return ret; + } + printk("or51132: Firmware upload complete.\n"); + state->config->set_ts_params(fe, clock_mode); + } + /* Change only if we are actually changing the modulation */ + if (state->current_modulation != p->modulation) { + state->current_modulation = p->modulation; + or51132_setmode(fe); + } + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Set to current mode */ + or51132_setmode(fe); + + /* Update current frequency */ + state->current_frequency = p->frequency; + return 0; +} + +static int or51132_get_parameters(struct dvb_frontend* fe, + struct dtv_frontend_properties *p) +{ + struct or51132_state* state = fe->demodulator_priv; + int status; + int retry = 1; + +start: + /* Receiver Status */ + if ((status = or51132_readreg(state, 0x00)) < 0) { + printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n"); + return -EREMOTEIO; + } + switch(status&0xff) { + case 0x06: + p->modulation = VSB_8; + break; + case 0x43: + p->modulation = QAM_64; + break; + case 0x45: + p->modulation = QAM_256; + break; + default: + if (retry--) + goto start; + printk(KERN_WARNING "or51132: unknown status 0x%02x\n", + status&0xff); + return -EREMOTEIO; + } + + /* FIXME: Read frequency from frontend, take AFC into account */ + p->frequency = state->current_frequency; + + /* FIXME: How to read inversion setting? Receiver 6 register? */ + p->inversion = INVERSION_AUTO; + + return 0; +} + +static int or51132_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct or51132_state* state = fe->demodulator_priv; + int reg; + + /* Receiver Status */ + if ((reg = or51132_readreg(state, 0x00)) < 0) { + printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg); + *status = 0; + return -EREMOTEIO; + } + dprintk("%s: read_status %04x\n", __func__, reg); + + if (reg & 0x0100) /* Receiver Lock */ + *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI| + FE_HAS_SYNC|FE_HAS_LOCK; + else + *status = 0; + return 0; +} + +/* Calculate SNR estimation (scaled by 2^24) + + 8-VSB SNR and QAM equations from Oren datasheets + + For 8-VSB: + SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K + + Where K = 0 if NTSC rejection filter is OFF; and + K = 3 if NTSC rejection filter is ON + + For QAM64: + SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) + + For QAM256: + SNR[dB] = 10 * log10(907832426.314266 / MSE^2 ) + + We re-write the snr equation as: + SNR * 2^24 = 10*(c - 2*intlog10(MSE)) + Where for QAM256, c = log10(907832426.314266) * 2^24 + and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */ + +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* No signal */ + return 0; + + mse = 2*intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); +} + +static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct or51132_state* state = fe->demodulator_priv; + int noise, reg; + u32 c, usK = 0; + int retry = 1; + +start: + /* SNR after Equalizer */ + noise = or51132_readreg(state, 0x02); + if (noise < 0) { + printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n"); + return -EREMOTEIO; + } + dprintk("read_snr noise (%d)\n", noise); + + /* Read status, contains modulation type for QAM_AUTO and + NTSC filter for VSB */ + reg = or51132_readreg(state, 0x00); + if (reg < 0) { + printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n"); + return -EREMOTEIO; + } + + switch (reg&0xff) { + case 0x06: + if (reg & 0x1000) usK = 3 << 24; + fallthrough; + case 0x43: /* QAM64 */ + c = 150204167; + break; + case 0x45: + c = 150290396; + break; + default: + printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff); + if (retry--) goto start; + return -EREMOTEIO; + } + dprintk("%s: modulation %02x, NTSC rej O%s\n", __func__, + reg&0xff, reg&0x1000?"n":"ff"); + + /* Calculate SNR using noise, c, and NTSC rejection correction */ + state->snr = calculate_snr(noise, c) - usK; + *snr = (state->snr) >> 16; + + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + /* Calculate Strength from SNR up to 35dB */ + /* Even though the SNR can go higher than 35dB, there is some comfort */ + /* factor in having a range of strong signals that can show at 100% */ + struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv; + u16 snr; + int ret; + + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; + + return 0; +} + +static int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 500; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + + return 0; +} + +static void or51132_release(struct dvb_frontend* fe) +{ + struct or51132_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops or51132_ops; + +struct dvb_frontend* or51132_attach(const struct or51132_config* config, + struct i2c_adapter* i2c) +{ + struct or51132_state* state = NULL; + + /* Allocate memory for the internal state */ + state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + /* Setup the state */ + state->config = config; + state->i2c = i2c; + state->current_frequency = -1; + state->current_modulation = -1; + + /* Create dvb_frontend */ + memcpy(&state->frontend.ops, &or51132_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +static const struct dvb_frontend_ops or51132_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Oren OR51132 VSB/QAM Frontend", + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 958 * MHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | + FE_CAN_8VSB + }, + + .release = or51132_release, + + .init = or51132_init, + .sleep = or51132_sleep, + + .set_frontend = or51132_set_parameters, + .get_frontend = or51132_get_parameters, + .get_tune_settings = or51132_get_tune_settings, + + .read_status = or51132_read_status, + .read_ber = or51132_read_ber, + .read_signal_strength = or51132_read_signal_strength, + .read_snr = or51132_read_snr, + .read_ucblocks = or51132_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver"); +MODULE_AUTHOR("Kirk Lapray"); +MODULE_AUTHOR("Trent Piepho"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(or51132_attach); diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h new file mode 100644 index 0000000000..75592cd2c6 --- /dev/null +++ b/drivers/media/dvb-frontends/or51132.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM + * + * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com> +*/ + +#ifndef OR51132_H +#define OR51132_H + +#include <linux/firmware.h> +#include <linux/dvb/frontend.h> + +struct or51132_config +{ + /* The demodulator's i2c address */ + u8 demod_address; + + /* Need to set device param for start_dma */ + int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); +}; + +#if IS_REACHABLE(CONFIG_DVB_OR51132) +extern struct dvb_frontend* or51132_attach(const struct or51132_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_OR51132 + +#endif // OR51132_H diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c new file mode 100644 index 0000000000..2e8e7071a6 --- /dev/null +++ b/drivers/media/dvb-frontends/or51211.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Support for OR51211 (pcHDTV HD-2000) - VSB + * + * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com> + * + * Based on code from Jack Kelliher (kelliher@xmission.com) + * Copyright (C) 2002 & pcHDTV, inc. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +/* + * This driver needs external firmware. Please use the command + * "<kerneldir>/scripts/get_dvb_firmware or51211" to + * download/extract it, and then copy it to /usr/lib/hotplug/firmware + * or /lib/firmware (depending on configuration of firmware hotplug). + */ +#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <asm/byteorder.h> + +#include <linux/int_log.h> +#include <media/dvb_frontend.h> +#include "or51211.h" + +static int debug; +#define dprintk(args...) \ + do { if (debug) pr_debug(args); } while (0) + +static u8 run_buf[] = {0x7f,0x01}; +static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC + +struct or51211_state { + + struct i2c_adapter* i2c; + + /* Configuration settings */ + const struct or51211_config* config; + + struct dvb_frontend frontend; + struct bt878* bt; + + /* Demodulator private data */ + u8 initialized:1; + u32 snr; /* Result of last SNR calculation */ + + /* Tuner private data */ + u32 current_frequency; +}; + +static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, + int len) +{ + int err; + struct i2c_msg msg; + msg.addr = reg; + msg.flags = 0; + msg.len = len; + msg.buf = (u8 *)buf; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + pr_warn("error (addr %02x, err == %i)\n", reg, err); + return -EREMOTEIO; + } + + return 0; +} + +static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) +{ + int err; + struct i2c_msg msg; + msg.addr = reg; + msg.flags = I2C_M_RD; + msg.len = len; + msg.buf = buf; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + pr_warn("error (addr %02x, err == %i)\n", reg, err); + return -EREMOTEIO; + } + + return 0; +} + +static int or51211_load_firmware (struct dvb_frontend* fe, + const struct firmware *fw) +{ + struct or51211_state* state = fe->demodulator_priv; + u8 tudata[585]; + int i; + + dprintk("Firmware is %zu bytes\n", fw->size); + + /* Get eprom data */ + tudata[0] = 17; + if (i2c_writebytes(state,0x50,tudata,1)) { + pr_warn("error eprom addr\n"); + return -1; + } + if (i2c_readbytes(state,0x50,&tudata[145],192)) { + pr_warn("error eprom\n"); + return -1; + } + + /* Create firmware buffer */ + for (i = 0; i < 145; i++) + tudata[i] = fw->data[i]; + + for (i = 0; i < 248; i++) + tudata[i+337] = fw->data[145+i]; + + state->config->reset(fe); + + if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { + pr_warn("error 1\n"); + return -1; + } + msleep(1); + + if (i2c_writebytes(state,state->config->demod_address, + &fw->data[393],8125)) { + pr_warn("error 2\n"); + return -1; + } + msleep(1); + + if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { + pr_warn("error 3\n"); + return -1; + } + + /* Wait at least 5 msec */ + msleep(10); + if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { + pr_warn("error 4\n"); + return -1; + } + msleep(10); + + pr_info("Done.\n"); + return 0; +}; + +static int or51211_setmode(struct dvb_frontend* fe, int mode) +{ + struct or51211_state* state = fe->demodulator_priv; + u8 rec_buf[14]; + + state->config->setmode(fe, mode); + + if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { + pr_warn("error 1\n"); + return -1; + } + + /* Wait at least 5 msec */ + msleep(10); + if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { + pr_warn("error 2\n"); + return -1; + } + + msleep(10); + + /* Set operation mode in Receiver 1 register; + * type 1: + * data 0x50h Automatic sets receiver channel conditions + * Automatic NTSC rejection filter + * Enable MPEG serial data output + * MPEG2tr + * High tuner phase noise + * normal +/-150kHz Carrier acquisition range + */ + if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { + pr_warn("error 3\n"); + return -1; + } + + rec_buf[0] = 0x04; + rec_buf[1] = 0x00; + rec_buf[2] = 0x03; + rec_buf[3] = 0x00; + msleep(20); + if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { + pr_warn("error 5\n"); + } + msleep(3); + if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { + pr_warn("error 6\n"); + return -1; + } + dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]); + + return 0; +} + +static int or51211_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct or51211_state* state = fe->demodulator_priv; + + /* Change only if we are actually changing the channel */ + if (state->current_frequency != p->frequency) { + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Set to ATSC mode */ + or51211_setmode(fe,0); + + /* Update current frequency */ + state->current_frequency = p->frequency; + } + return 0; +} + +static int or51211_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct or51211_state* state = fe->demodulator_priv; + unsigned char rec_buf[2]; + unsigned char snd_buf[] = {0x04,0x00,0x03,0x00}; + *status = 0; + + /* Receiver Status */ + if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { + pr_warn("write error\n"); + return -1; + } + msleep(3); + if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { + pr_warn("read error\n"); + return -1; + } + dprintk("%x %x\n", rec_buf[0], rec_buf[1]); + + if (rec_buf[0] & 0x01) { /* Receiver Lock */ + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + } + return 0; +} + +/* Calculate SNR estimation (scaled by 2^24) + + 8-VSB SNR equation from Oren datasheets + + For 8-VSB: + SNR[dB] = 10 * log10(219037.9454 / MSE^2 ) + + We re-write the snr equation as: + SNR * 2^24 = 10*(c - 2*intlog10(MSE)) + Where for 8-VSB, c = log10(219037.9454) * 2^24 */ + +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* No signal */ + return 0; + + mse = 2*intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); +} + +static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct or51211_state* state = fe->demodulator_priv; + u8 rec_buf[2]; + u8 snd_buf[3]; + + /* SNR after Equalizer */ + snd_buf[0] = 0x04; + snd_buf[1] = 0x00; + snd_buf[2] = 0x04; + + if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { + pr_warn("error writing snr reg\n"); + return -1; + } + if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { + pr_warn("read_status read error\n"); + return -1; + } + + state->snr = calculate_snr(rec_buf[0], 89599047); + *snr = (state->snr) >> 16; + + dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0], + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + /* Calculate Strength from SNR up to 35dB */ + /* Even though the SNR can go higher than 35dB, there is some comfort */ + /* factor in having a range of strong signals that can show at 100% */ + struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv; + u16 snr; + int ret; + + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; + + return 0; +} + +static int or51211_read_ber(struct dvb_frontend* fe, u32* ber) +{ + *ber = -ENOSYS; + return 0; +} + +static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + *ucblocks = -ENOSYS; + return 0; +} + +static int or51211_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int or51211_init(struct dvb_frontend* fe) +{ + struct or51211_state* state = fe->demodulator_priv; + const struct or51211_config* config = state->config; + const struct firmware* fw; + unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00}; + unsigned char rec_buf[14]; + int ret,i; + + if (!state->initialized) { + /* Request the firmware, this will block until it uploads */ + pr_info("Waiting for firmware upload (%s)...\n", + OR51211_DEFAULT_FIRMWARE); + ret = config->request_firmware(fe, &fw, + OR51211_DEFAULT_FIRMWARE); + pr_info("Got Hotplug firmware\n"); + if (ret) { + pr_warn("No firmware uploaded (timeout or file not found?)\n"); + return ret; + } + + ret = or51211_load_firmware(fe, fw); + release_firmware(fw); + if (ret) { + pr_warn("Writing firmware to device failed!\n"); + return ret; + } + pr_info("Firmware upload complete.\n"); + + /* Set operation mode in Receiver 1 register; + * type 1: + * data 0x50h Automatic sets receiver channel conditions + * Automatic NTSC rejection filter + * Enable MPEG serial data output + * MPEG2tr + * High tuner phase noise + * normal +/-150kHz Carrier acquisition range + */ + if (i2c_writebytes(state,state->config->demod_address, + cmd_buf,3)) { + pr_warn("Load DVR Error 5\n"); + return -1; + } + + /* Read back ucode version to besure we loaded correctly */ + /* and are really up and running */ + rec_buf[0] = 0x04; + rec_buf[1] = 0x00; + rec_buf[2] = 0x03; + rec_buf[3] = 0x00; + msleep(30); + if (i2c_writebytes(state,state->config->demod_address, + rec_buf,3)) { + pr_warn("Load DVR Error A\n"); + return -1; + } + msleep(3); + if (i2c_readbytes(state,state->config->demod_address, + &rec_buf[10],2)) { + pr_warn("Load DVR Error B\n"); + return -1; + } + + rec_buf[0] = 0x04; + rec_buf[1] = 0x00; + rec_buf[2] = 0x01; + rec_buf[3] = 0x00; + msleep(20); + if (i2c_writebytes(state,state->config->demod_address, + rec_buf,3)) { + pr_warn("Load DVR Error C\n"); + return -1; + } + msleep(3); + if (i2c_readbytes(state,state->config->demod_address, + &rec_buf[12],2)) { + pr_warn("Load DVR Error D\n"); + return -1; + } + + for (i = 0; i < 8; i++) + rec_buf[i]=0xed; + + for (i = 0; i < 5; i++) { + msleep(30); + get_ver_buf[4] = i+1; + if (i2c_writebytes(state,state->config->demod_address, + get_ver_buf,5)) { + pr_warn("Load DVR Error 6 - %d\n", i); + return -1; + } + msleep(3); + + if (i2c_readbytes(state,state->config->demod_address, + &rec_buf[i*2],2)) { + pr_warn("Load DVR Error 7 - %d\n", i); + return -1; + } + /* If we didn't receive the right index, try again */ + if ((int)rec_buf[i*2+1]!=i+1){ + i--; + } + } + dprintk("read_fwbits %10ph\n", rec_buf); + + pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n", + rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12], + rec_buf[10]); + + rec_buf[0] = 0x04; + rec_buf[1] = 0x00; + rec_buf[2] = 0x03; + rec_buf[3] = 0x00; + msleep(20); + if (i2c_writebytes(state,state->config->demod_address, + rec_buf,3)) { + pr_warn("Load DVR Error 8\n"); + return -1; + } + msleep(20); + if (i2c_readbytes(state,state->config->demod_address, + &rec_buf[8],2)) { + pr_warn("Load DVR Error 9\n"); + return -1; + } + state->initialized = 1; + } + + return 0; +} + +static int or51211_get_tune_settings(struct dvb_frontend* fe, + struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 500; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void or51211_release(struct dvb_frontend* fe) +{ + struct or51211_state* state = fe->demodulator_priv; + state->config->sleep(fe); + kfree(state); +} + +static const struct dvb_frontend_ops or51211_ops; + +struct dvb_frontend* or51211_attach(const struct or51211_config* config, + struct i2c_adapter* i2c) +{ + struct or51211_state* state = NULL; + + /* Allocate memory for the internal state */ + state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + /* Setup the state */ + state->config = config; + state->i2c = i2c; + state->initialized = 0; + state->current_frequency = 0; + + /* Create dvb_frontend */ + memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +static const struct dvb_frontend_ops or51211_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Oren OR51211 VSB Frontend", + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 958 * MHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_8VSB + }, + + .release = or51211_release, + + .init = or51211_init, + .sleep = or51211_sleep, + + .set_frontend = or51211_set_parameters, + .get_tune_settings = or51211_get_tune_settings, + + .read_status = or51211_read_status, + .read_ber = or51211_read_ber, + .read_signal_strength = or51211_read_signal_strength, + .read_snr = or51211_read_snr, + .read_ucblocks = or51211_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver"); +MODULE_AUTHOR("Kirk Lapray"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(or51211_attach); + diff --git a/drivers/media/dvb-frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h new file mode 100644 index 0000000000..1d07639169 --- /dev/null +++ b/drivers/media/dvb-frontends/or51211.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for OR51211 (pcHDTV HD-2000) - VSB + * + * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com> +*/ + +#ifndef OR51211_H +#define OR51211_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct or51211_config +{ + /* The demodulator's i2c address */ + u8 demod_address; + + /* Request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); + void (*setmode)(struct dvb_frontend * fe, int mode); + void (*reset)(struct dvb_frontend * fe); + void (*sleep)(struct dvb_frontend * fe); +}; + +#if IS_REACHABLE(CONFIG_DVB_OR51211) +extern struct dvb_frontend* or51211_attach(const struct or51211_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_OR51211 + +#endif // OR51211_H + diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c new file mode 100644 index 0000000000..35c969fd2c --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Realtek RTL2830 DVB-T demodulator driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#include "rtl2830_priv.h" + +/* Our regmap is bypassing I2C adapter lock, thus we do it! */ +static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg, + const void *val, size_t val_count) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_bulk_write(dev->regmap, reg, val, val_count); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + return ret; +} + +static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg, + unsigned int mask, unsigned int val) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_update_bits(dev->regmap, reg, mask, val); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + return ret; +} + +static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg, + void *val, size_t val_count) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_bulk_read(dev->regmap, reg, val, val_count); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + return ret; +} + +static int rtl2830_init(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, i; + struct rtl2830_reg_val_mask tab[] = { + {0x00d, 0x01, 0x03}, + {0x00d, 0x10, 0x10}, + {0x104, 0x00, 0x1e}, + {0x105, 0x80, 0x80}, + {0x110, 0x02, 0x03}, + {0x110, 0x08, 0x0c}, + {0x17b, 0x00, 0x40}, + {0x17d, 0x05, 0x0f}, + {0x17d, 0x50, 0xf0}, + {0x18c, 0x08, 0x0f}, + {0x18d, 0x00, 0xc0}, + {0x188, 0x05, 0x0f}, + {0x189, 0x00, 0xfc}, + {0x2d5, 0x02, 0x02}, + {0x2f1, 0x02, 0x06}, + {0x2f1, 0x20, 0xf8}, + {0x16d, 0x00, 0x01}, + {0x1a6, 0x00, 0x80}, + {0x106, dev->pdata->vtop, 0x3f}, + {0x107, dev->pdata->krf, 0x3f}, + {0x112, 0x28, 0xff}, + {0x103, dev->pdata->agc_targ_val, 0xff}, + {0x00a, 0x02, 0x07}, + {0x140, 0x0c, 0x3c}, + {0x140, 0x40, 0xc0}, + {0x15b, 0x05, 0x07}, + {0x15b, 0x28, 0x38}, + {0x15c, 0x05, 0x07}, + {0x15c, 0x28, 0x38}, + {0x115, dev->pdata->spec_inv, 0x01}, + {0x16f, 0x01, 0x07}, + {0x170, 0x18, 0x38}, + {0x172, 0x0f, 0x0f}, + {0x173, 0x08, 0x38}, + {0x175, 0x01, 0x07}, + {0x176, 0x00, 0xc0}, + }; + + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = rtl2830_update_bits(client, tab[i].reg, tab[i].mask, + tab[i].val); + if (ret) + goto err; + } + + ret = rtl2830_bulk_write(client, 0x18f, "\x28\x00", 2); + if (ret) + goto err; + + ret = rtl2830_bulk_write(client, 0x195, + "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8); + if (ret) + goto err; + + /* TODO: spec init */ + + /* soft reset */ + ret = rtl2830_update_bits(client, 0x101, 0x04, 0x04); + if (ret) + goto err; + + ret = rtl2830_update_bits(client, 0x101, 0x04, 0x00); + if (ret) + goto err; + + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dev->sleeping = false; + + return ret; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2830_sleep(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + dev->sleeping = true; + dev->fe_status = 0; + + return 0; +} + +static int rtl2830_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 500; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; + + return 0; +} + +static int rtl2830_set_frontend(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u64 num; + u8 buf[3], u8tmp; + u32 if_ctl, if_frequency; + static const u8 bw_params1[3][34] = { + { + 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41, + 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a, + 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82, + 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */ + }, { + 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca, + 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca, + 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e, + 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */ + }, { + 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0, + 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a, + 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f, + 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */ + }, + }; + static const u8 bw_params2[3][6] = { + {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30}, /* 6 MHz */ + {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98}, /* 7 MHz */ + {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */ + }; + + dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n", + c->frequency, c->bandwidth_hz, c->inversion); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + switch (c->bandwidth_hz) { + case 6000000: + i = 0; + break; + case 7000000: + i = 1; + break; + case 8000000: + i = 2; + break; + default: + dev_err(&client->dev, "invalid bandwidth_hz %u\n", + c->bandwidth_hz); + return -EINVAL; + } + + ret = rtl2830_update_bits(client, 0x008, 0x06, i << 1); + if (ret) + goto err; + + /* program if frequency */ + if (fe->ops.tuner_ops.get_if_frequency) + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + else + ret = -EINVAL; + if (ret) + goto err; + + num = if_frequency % dev->pdata->clk; + num *= 0x400000; + num = div_u64(num, dev->pdata->clk); + num = -num; + if_ctl = num & 0x3fffff; + dev_dbg(&client->dev, "if_frequency=%d if_ctl=%08x\n", + if_frequency, if_ctl); + + buf[0] = (if_ctl >> 16) & 0x3f; + buf[1] = (if_ctl >> 8) & 0xff; + buf[2] = (if_ctl >> 0) & 0xff; + + ret = rtl2830_bulk_read(client, 0x119, &u8tmp, 1); + if (ret) + goto err; + + buf[0] |= u8tmp & 0xc0; /* [7:6] */ + + ret = rtl2830_bulk_write(client, 0x119, buf, 3); + if (ret) + goto err; + + /* 1/2 split I2C write */ + ret = rtl2830_bulk_write(client, 0x11c, &bw_params1[i][0], 17); + if (ret) + goto err; + + /* 2/2 split I2C write */ + ret = rtl2830_bulk_write(client, 0x12d, &bw_params1[i][17], 17); + if (ret) + goto err; + + ret = rtl2830_bulk_write(client, 0x19d, bw_params2[i], 6); + if (ret) + goto err; + + return ret; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2830_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + int ret; + u8 buf[3]; + + if (dev->sleeping) + return 0; + + ret = rtl2830_bulk_read(client, 0x33c, buf, 2); + if (ret) + goto err; + + ret = rtl2830_bulk_read(client, 0x351, &buf[2], 1); + if (ret) + goto err; + + dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf); + + switch ((buf[0] >> 2) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[2] >> 2) & 1) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[2] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[1] >> 3) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[1] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, stmp; + unsigned int utmp; + u8 u8tmp, buf[2]; + + *status = 0; + + if (dev->sleeping) + return 0; + + ret = rtl2830_bulk_read(client, 0x351, &u8tmp, 1); + if (ret) + goto err; + + u8tmp = (u8tmp >> 3) & 0x0f; /* [6:3] */ + if (u8tmp == 11) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (u8tmp == 10) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + } + + dev->fe_status = *status; + + /* Signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + /* Read IF AGC */ + ret = rtl2830_bulk_read(client, 0x359, buf, 2); + if (ret) + goto err; + + stmp = buf[0] << 8 | buf[1] << 0; + stmp = sign_extend32(stmp, 13); + utmp = clamp_val(-4 * stmp + 32767, 0x0000, 0xffff); + + dev_dbg(&client->dev, "IF AGC=%d\n", stmp); + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = utmp; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned int hierarchy, constellation; + #define CONSTELLATION_NUM 3 + #define HIERARCHY_NUM 4 + static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + {70705899, 70705899, 70705899, 70705899}, + {82433173, 82433173, 87483115, 94445660}, + {92888734, 92888734, 95487525, 99770748}, + }; + + ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1); + if (ret) + goto err; + + constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ + if (constellation > CONSTELLATION_NUM - 1) + goto err; + + hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err; + + ret = rtl2830_bulk_read(client, 0x40c, buf, 2); + if (ret) + goto err; + + utmp = buf[0] << 8 | buf[1] << 0; + if (utmp) + stmp = (constant[constellation][hierarchy] - + intlog10(utmp)) / ((1 << 24) / 10000); + else + stmp = 0; + + dev_dbg(&client->dev, "CNR raw=%u\n", utmp); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = stmp; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + ret = rtl2830_bulk_read(client, 0x34e, buf, 2); + if (ret) + goto err; + + utmp = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += utmp; + dev->post_bit_count += 1000000; + + dev_dbg(&client->dev, "BER errors=%u total=1000000\n", utmp); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + + return ret; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + + return 0; +} + +static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; + + return 0; +} + +static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + + return 0; +} + +static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->strength.stat[0].scale == FE_SCALE_RELATIVE) + *strength = c->strength.stat[0].uvalue; + else + *strength = 0; + + return 0; +} + +static const struct dvb_frontend_ops rtl2830_ops = { + .delsys = {SYS_DVBT}, + .info = { + .name = "Realtek RTL2830 (DVB-T)", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .init = rtl2830_init, + .sleep = rtl2830_sleep, + + .get_tune_settings = rtl2830_get_tune_settings, + + .set_frontend = rtl2830_set_frontend, + .get_frontend = rtl2830_get_frontend, + + .read_status = rtl2830_read_status, + .read_snr = rtl2830_read_snr, + .read_ber = rtl2830_read_ber, + .read_ucblocks = rtl2830_read_ucblocks, + .read_signal_strength = rtl2830_read_signal_strength, +}; + +static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct i2c_client *client = fe->demodulator_priv; + int ret; + u8 u8tmp; + + dev_dbg(&client->dev, "onoff=%d\n", onoff); + + /* enable / disable PID filter */ + if (onoff) + u8tmp = 0x80; + else + u8tmp = 0x00; + + ret = rtl2830_update_bits(client, 0x061, 0x80, u8tmp); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2830_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int onoff) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + int ret; + u8 buf[4]; + + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", + index, pid, onoff); + + /* skip invalid PIDs (0x2000) */ + if (pid > 0x1fff || index > 32) + return 0; + + if (onoff) + set_bit(index, &dev->filters); + else + clear_bit(index, &dev->filters); + + /* enable / disable PIDs */ + buf[0] = (dev->filters >> 0) & 0xff; + buf[1] = (dev->filters >> 8) & 0xff; + buf[2] = (dev->filters >> 16) & 0xff; + buf[3] = (dev->filters >> 24) & 0xff; + ret = rtl2830_bulk_write(client, 0x062, buf, 4); + if (ret) + goto err; + + /* add PID */ + buf[0] = (pid >> 8) & 0xff; + buf[1] = (pid >> 0) & 0xff; + ret = rtl2830_bulk_write(client, 0x066 + 2 * index, buf, 2); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +/* + * I2C gate/mux/repeater logic + * We must use unlocked __i2c_transfer() here (through regmap) because of I2C + * adapter lock is already taken by tuner driver. + * Gate is closed automatically after single I2C transfer. + */ +static int rtl2830_select(struct i2c_mux_core *muxc, u32 chan_id) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + struct rtl2830_dev *dev = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "\n"); + + /* open I2C repeater for 1 transfer, closes automatically */ + /* XXX: regmap_update_bits() does not lock I2C adapter */ + ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static struct dvb_frontend *rtl2830_get_dvb_frontend(struct i2c_client *client) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return dev->muxc->adapter[0]; +} + +/* + * We implement own I2C access routines for regmap in order to get manual access + * to I2C adapter lock, which is needed for I2C mux adapter. + */ +static int rtl2830_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct i2c_client *client = context; + int ret; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = reg_size, + .buf = (u8 *)reg_buf, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = val_size, + .buf = val_buf, + } + }; + + ret = __i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + dev_warn(&client->dev, "i2c reg read failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} + +static int rtl2830_regmap_write(void *context, const void *data, size_t count) +{ + struct i2c_client *client = context; + int ret; + struct i2c_msg msg[1] = { + { + .addr = client->addr, + .flags = 0, + .len = count, + .buf = (u8 *)data, + } + }; + + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c reg write failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} + +static int rtl2830_regmap_gather_write(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct i2c_client *client = context; + int ret; + u8 buf[256]; + struct i2c_msg msg[1] = { + { + .addr = client->addr, + .flags = 0, + .len = 1 + val_len, + .buf = buf, + } + }; + + buf[0] = *(u8 const *)reg; + memcpy(&buf[1], val, val_len); + + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c reg write failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} + +static int rtl2830_probe(struct i2c_client *client) +{ + struct rtl2830_platform_data *pdata = client->dev.platform_data; + struct rtl2830_dev *dev; + int ret; + u8 u8tmp; + static const struct regmap_bus regmap_bus = { + .read = rtl2830_regmap_read, + .write = rtl2830_regmap_write, + .gather_write = rtl2830_regmap_gather_write, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, + }; + static const struct regmap_range_cfg regmap_range_cfg[] = { + { + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + .range_min = 0 * 0x100, + .range_max = 5 * 0x100, + }, + }; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 5 * 0x100, + .ranges = regmap_range_cfg, + .num_ranges = ARRAY_SIZE(regmap_range_cfg), + }; + + dev_dbg(&client->dev, "\n"); + + if (pdata == NULL) { + ret = -EINVAL; + goto err; + } + + /* allocate memory for the internal state */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + goto err; + } + + /* setup the state */ + i2c_set_clientdata(client, dev); + dev->client = client; + dev->pdata = client->dev.platform_data; + dev->sleeping = true; + dev->regmap = regmap_init(&client->dev, ®map_bus, client, + ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + /* check if the demod is there */ + ret = rtl2830_bulk_read(client, 0x000, &u8tmp, 1); + if (ret) + goto err_regmap_exit; + + /* create muxed i2c adapter for tuner */ + dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0, + rtl2830_select, NULL); + if (!dev->muxc) { + ret = -ENOMEM; + goto err_regmap_exit; + } + dev->muxc->priv = client; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_regmap_exit; + + /* create dvb frontend */ + memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops)); + dev->fe.demodulator_priv = client; + + /* setup callbacks */ + pdata->get_dvb_frontend = rtl2830_get_dvb_frontend; + pdata->get_i2c_adapter = rtl2830_get_i2c_adapter; + pdata->pid_filter = rtl2830_pid_filter; + pdata->pid_filter_ctrl = rtl2830_pid_filter_ctrl; + + dev_info(&client->dev, "Realtek RTL2830 successfully attached\n"); + + return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void rtl2830_remove(struct i2c_client *client) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + i2c_mux_del_adapters(dev->muxc); + regmap_exit(dev->regmap); + kfree(dev); +} + +static const struct i2c_device_id rtl2830_id_table[] = { + {"rtl2830", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); + +static struct i2c_driver rtl2830_driver = { + .driver = { + .name = "rtl2830", + .suppress_bind_attrs = true, + }, + .probe = rtl2830_probe, + .remove = rtl2830_remove, + .id_table = rtl2830_id_table, +}; + +module_i2c_driver(rtl2830_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h new file mode 100644 index 0000000000..fb5522025c --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2830.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Realtek RTL2830 DVB-T demodulator driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#ifndef RTL2830_H +#define RTL2830_H + +#include <linux/dvb/frontend.h> + +/** + * struct rtl2830_platform_data - Platform data for the rtl2830 driver + * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000). + * @spec_inv: Spectrum inversion. + * @vtop: AGC take-over point. + * @krf: AGC ratio. + * @agc_targ_val: AGC. + * @get_dvb_frontend: Get DVB frontend. + * @get_i2c_adapter: Get I2C adapter. + * @pid_filter: Set PID to PID filter. + * @pid_filter_ctrl: Control PID filter. + */ +struct rtl2830_platform_data { + u32 clk; + bool spec_inv; + u8 vtop; + u8 krf; + u8 agc_targ_val; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + int (*pid_filter)(struct dvb_frontend *, u8, u16, int); + int (*pid_filter_ctrl)(struct dvb_frontend *, int); +}; + +#endif /* RTL2830_H */ diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h new file mode 100644 index 0000000000..ae1fc24a4d --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2830_priv.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Realtek RTL2830 DVB-T demodulator driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#ifndef RTL2830_PRIV_H +#define RTL2830_PRIV_H + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "rtl2830.h" +#include <linux/i2c-mux.h> +#include <linux/math64.h> +#include <linux/regmap.h> +#include <linux/bitops.h> + +struct rtl2830_dev { + struct rtl2830_platform_data *pdata; + struct i2c_client *client; + struct regmap *regmap; + struct i2c_mux_core *muxc; + struct dvb_frontend fe; + bool sleeping; + unsigned long filters; + enum fe_status fe_status; + u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ + u64 post_bit_error; + u64 post_bit_count; +}; + +struct rtl2830_reg_val_mask { + u16 reg; + u8 val; + u8 mask; +}; + +#endif /* RTL2830_PRIV_H */ diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c new file mode 100644 index 0000000000..601cf45c39 --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -0,0 +1,1148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Realtek RTL2832 DVB-T demodulator driver + * + * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi> + */ + +#include "rtl2832_priv.h" + +#define REG_MASK(b) (BIT(b + 1) - 1) + +static const struct rtl2832_reg_entry registers[] = { + [DVBT_SOFT_RST] = {0x101, 2, 2}, + [DVBT_IIC_REPEAT] = {0x101, 3, 3}, + [DVBT_TR_WAIT_MIN_8K] = {0x188, 11, 2}, + [DVBT_RSD_BER_FAIL_VAL] = {0x18f, 15, 0}, + [DVBT_EN_BK_TRK] = {0x1a6, 7, 7}, + [DVBT_AD_EN_REG] = {0x008, 7, 7}, + [DVBT_AD_EN_REG1] = {0x008, 6, 6}, + [DVBT_EN_BBIN] = {0x1b1, 0, 0}, + [DVBT_MGD_THD0] = {0x195, 7, 0}, + [DVBT_MGD_THD1] = {0x196, 7, 0}, + [DVBT_MGD_THD2] = {0x197, 7, 0}, + [DVBT_MGD_THD3] = {0x198, 7, 0}, + [DVBT_MGD_THD4] = {0x199, 7, 0}, + [DVBT_MGD_THD5] = {0x19a, 7, 0}, + [DVBT_MGD_THD6] = {0x19b, 7, 0}, + [DVBT_MGD_THD7] = {0x19c, 7, 0}, + [DVBT_EN_CACQ_NOTCH] = {0x161, 4, 4}, + [DVBT_AD_AV_REF] = {0x009, 6, 0}, + [DVBT_REG_PI] = {0x00a, 2, 0}, + [DVBT_PIP_ON] = {0x021, 3, 3}, + [DVBT_SCALE1_B92] = {0x292, 7, 0}, + [DVBT_SCALE1_B93] = {0x293, 7, 0}, + [DVBT_SCALE1_BA7] = {0x2a7, 7, 0}, + [DVBT_SCALE1_BA9] = {0x2a9, 7, 0}, + [DVBT_SCALE1_BAA] = {0x2aa, 7, 0}, + [DVBT_SCALE1_BAB] = {0x2ab, 7, 0}, + [DVBT_SCALE1_BAC] = {0x2ac, 7, 0}, + [DVBT_SCALE1_BB0] = {0x2b0, 7, 0}, + [DVBT_SCALE1_BB1] = {0x2b1, 7, 0}, + [DVBT_KB_P1] = {0x164, 3, 1}, + [DVBT_KB_P2] = {0x164, 6, 4}, + [DVBT_KB_P3] = {0x165, 2, 0}, + [DVBT_OPT_ADC_IQ] = {0x006, 5, 4}, + [DVBT_AD_AVI] = {0x009, 1, 0}, + [DVBT_AD_AVQ] = {0x009, 3, 2}, + [DVBT_K1_CR_STEP12] = {0x2ad, 9, 4}, + [DVBT_TRK_KS_P2] = {0x16f, 2, 0}, + [DVBT_TRK_KS_I2] = {0x170, 5, 3}, + [DVBT_TR_THD_SET2] = {0x172, 3, 0}, + [DVBT_TRK_KC_P2] = {0x173, 5, 3}, + [DVBT_TRK_KC_I2] = {0x175, 2, 0}, + [DVBT_CR_THD_SET2] = {0x176, 7, 6}, + [DVBT_PSET_IFFREQ] = {0x119, 21, 0}, + [DVBT_SPEC_INV] = {0x115, 0, 0}, + [DVBT_RSAMP_RATIO] = {0x19f, 27, 2}, + [DVBT_CFREQ_OFF_RATIO] = {0x19d, 23, 4}, + [DVBT_FSM_STAGE] = {0x351, 6, 3}, + [DVBT_RX_CONSTEL] = {0x33c, 3, 2}, + [DVBT_RX_HIER] = {0x33c, 6, 4}, + [DVBT_RX_C_RATE_LP] = {0x33d, 2, 0}, + [DVBT_RX_C_RATE_HP] = {0x33d, 5, 3}, + [DVBT_GI_IDX] = {0x351, 1, 0}, + [DVBT_FFT_MODE_IDX] = {0x351, 2, 2}, + [DVBT_RSD_BER_EST] = {0x34e, 15, 0}, + [DVBT_CE_EST_EVM] = {0x40c, 15, 0}, + [DVBT_RF_AGC_VAL] = {0x35b, 13, 0}, + [DVBT_IF_AGC_VAL] = {0x359, 13, 0}, + [DVBT_DAGC_VAL] = {0x305, 7, 0}, + [DVBT_SFREQ_OFF] = {0x318, 13, 0}, + [DVBT_CFREQ_OFF] = {0x35f, 17, 0}, + [DVBT_POLAR_RF_AGC] = {0x00e, 1, 1}, + [DVBT_POLAR_IF_AGC] = {0x00e, 0, 0}, + [DVBT_AAGC_HOLD] = {0x104, 5, 5}, + [DVBT_EN_RF_AGC] = {0x104, 6, 6}, + [DVBT_EN_IF_AGC] = {0x104, 7, 7}, + [DVBT_IF_AGC_MIN] = {0x108, 7, 0}, + [DVBT_IF_AGC_MAX] = {0x109, 7, 0}, + [DVBT_RF_AGC_MIN] = {0x10a, 7, 0}, + [DVBT_RF_AGC_MAX] = {0x10b, 7, 0}, + [DVBT_IF_AGC_MAN] = {0x10c, 6, 6}, + [DVBT_IF_AGC_MAN_VAL] = {0x10c, 13, 0}, + [DVBT_RF_AGC_MAN] = {0x10e, 6, 6}, + [DVBT_RF_AGC_MAN_VAL] = {0x10e, 13, 0}, + [DVBT_DAGC_TRG_VAL] = {0x112, 7, 0}, + [DVBT_AGC_TARG_VAL_0] = {0x102, 0, 0}, + [DVBT_AGC_TARG_VAL_8_1] = {0x103, 7, 0}, + [DVBT_AAGC_LOOP_GAIN] = {0x1c7, 5, 1}, + [DVBT_LOOP_GAIN2_3_0] = {0x104, 4, 1}, + [DVBT_LOOP_GAIN2_4] = {0x105, 7, 7}, + [DVBT_LOOP_GAIN3] = {0x1c8, 4, 0}, + [DVBT_VTOP1] = {0x106, 5, 0}, + [DVBT_VTOP2] = {0x1c9, 5, 0}, + [DVBT_VTOP3] = {0x1ca, 5, 0}, + [DVBT_KRF1] = {0x1cb, 7, 0}, + [DVBT_KRF2] = {0x107, 7, 0}, + [DVBT_KRF3] = {0x1cd, 7, 0}, + [DVBT_KRF4] = {0x1ce, 7, 0}, + [DVBT_EN_GI_PGA] = {0x1e5, 0, 0}, + [DVBT_THD_LOCK_UP] = {0x1d9, 8, 0}, + [DVBT_THD_LOCK_DW] = {0x1db, 8, 0}, + [DVBT_THD_UP1] = {0x1dd, 7, 0}, + [DVBT_THD_DW1] = {0x1de, 7, 0}, + [DVBT_INTER_CNT_LEN] = {0x1d8, 3, 0}, + [DVBT_GI_PGA_STATE] = {0x1e6, 3, 3}, + [DVBT_EN_AGC_PGA] = {0x1d7, 0, 0}, + [DVBT_CKOUTPAR] = {0x17b, 5, 5}, + [DVBT_CKOUT_PWR] = {0x17b, 6, 6}, + [DVBT_SYNC_DUR] = {0x17b, 7, 7}, + [DVBT_ERR_DUR] = {0x17c, 0, 0}, + [DVBT_SYNC_LVL] = {0x17c, 1, 1}, + [DVBT_ERR_LVL] = {0x17c, 2, 2}, + [DVBT_VAL_LVL] = {0x17c, 3, 3}, + [DVBT_SERIAL] = {0x17c, 4, 4}, + [DVBT_SER_LSB] = {0x17c, 5, 5}, + [DVBT_CDIV_PH0] = {0x17d, 3, 0}, + [DVBT_CDIV_PH1] = {0x17d, 7, 4}, + [DVBT_MPEG_IO_OPT_2_2] = {0x006, 7, 7}, + [DVBT_MPEG_IO_OPT_1_0] = {0x007, 7, 6}, + [DVBT_CKOUTPAR_PIP] = {0x0b7, 4, 4}, + [DVBT_CKOUT_PWR_PIP] = {0x0b7, 3, 3}, + [DVBT_SYNC_LVL_PIP] = {0x0b7, 2, 2}, + [DVBT_ERR_LVL_PIP] = {0x0b7, 1, 1}, + [DVBT_VAL_LVL_PIP] = {0x0b7, 0, 0}, + [DVBT_CKOUTPAR_PID] = {0x0b9, 4, 4}, + [DVBT_CKOUT_PWR_PID] = {0x0b9, 3, 3}, + [DVBT_SYNC_LVL_PID] = {0x0b9, 2, 2}, + [DVBT_ERR_LVL_PID] = {0x0b9, 1, 1}, + [DVBT_VAL_LVL_PID] = {0x0b9, 0, 0}, + [DVBT_SM_PASS] = {0x193, 11, 0}, + [DVBT_AD7_SETTING] = {0x011, 15, 0}, + [DVBT_RSSI_R] = {0x301, 6, 0}, + [DVBT_ACI_DET_IND] = {0x312, 0, 0}, + [DVBT_REG_MON] = {0x00d, 1, 0}, + [DVBT_REG_MONSEL] = {0x00d, 2, 2}, + [DVBT_REG_GPE] = {0x00d, 7, 7}, + [DVBT_REG_GPO] = {0x010, 0, 0}, + [DVBT_REG_4MSEL] = {0x013, 0, 0}, +}; + +static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) +{ + struct i2c_client *client = dev->client; + int ret, i; + u16 reg_start_addr; + u8 msb, lsb, reading[4], len; + u32 reading_tmp, mask; + + reg_start_addr = registers[reg].start_address; + msb = registers[reg].msb; + lsb = registers[reg].lsb; + len = (msb >> 3) + 1; + mask = REG_MASK(msb - lsb); + + ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len); + if (ret) + goto err; + + reading_tmp = 0; + for (i = 0; i < len; i++) + reading_tmp |= reading[i] << ((len - 1 - i) * 8); + + *val = (reading_tmp >> lsb) & mask; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val) +{ + struct i2c_client *client = dev->client; + int ret, i; + u16 reg_start_addr; + u8 msb, lsb, reading[4], writing[4], len; + u32 reading_tmp, writing_tmp, mask; + + reg_start_addr = registers[reg].start_address; + msb = registers[reg].msb; + lsb = registers[reg].lsb; + len = (msb >> 3) + 1; + mask = REG_MASK(msb - lsb); + + ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len); + if (ret) + goto err; + + reading_tmp = 0; + for (i = 0; i < len; i++) + reading_tmp |= reading[i] << ((len - 1 - i) * 8); + + writing_tmp = reading_tmp & ~(mask << lsb); + writing_tmp |= ((val & mask) << lsb); + + for (i = 0; i < len; i++) + writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff; + + ret = regmap_bulk_write(dev->regmap, reg_start_addr, writing, len); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u64 pset_iffreq; + u8 en_bbin = (if_freq == 0 ? 0x1 : 0x0); + + /* + * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) + * / CrystalFreqHz) + */ + pset_iffreq = if_freq % dev->pdata->clk; + pset_iffreq *= 0x400000; + pset_iffreq = div_u64(pset_iffreq, dev->pdata->clk); + pset_iffreq = -pset_iffreq; + pset_iffreq = pset_iffreq & 0x3fffff; + dev_dbg(&client->dev, "if_frequency=%d pset_iffreq=%08x\n", + if_freq, (unsigned)pset_iffreq); + + ret = rtl2832_wr_demod_reg(dev, DVBT_EN_BBIN, en_bbin); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(dev, DVBT_PSET_IFFREQ, pset_iffreq); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_init(struct dvb_frontend *fe) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + const struct rtl2832_reg_value *init; + int i, ret, len; + /* initialization values for the demodulator registers */ + struct rtl2832_reg_value rtl2832_initial_regs[] = { + {DVBT_AD_EN_REG, 0x1}, + {DVBT_AD_EN_REG1, 0x1}, + {DVBT_RSD_BER_FAIL_VAL, 0x2800}, + {DVBT_MGD_THD0, 0x10}, + {DVBT_MGD_THD1, 0x20}, + {DVBT_MGD_THD2, 0x20}, + {DVBT_MGD_THD3, 0x40}, + {DVBT_MGD_THD4, 0x22}, + {DVBT_MGD_THD5, 0x32}, + {DVBT_MGD_THD6, 0x37}, + {DVBT_MGD_THD7, 0x39}, + {DVBT_EN_BK_TRK, 0x0}, + {DVBT_EN_CACQ_NOTCH, 0x0}, + {DVBT_AD_AV_REF, 0x2a}, + {DVBT_REG_PI, 0x6}, + {DVBT_PIP_ON, 0x0}, + {DVBT_CDIV_PH0, 0x8}, + {DVBT_CDIV_PH1, 0x8}, + {DVBT_SCALE1_B92, 0x4}, + {DVBT_SCALE1_B93, 0xb0}, + {DVBT_SCALE1_BA7, 0x78}, + {DVBT_SCALE1_BA9, 0x28}, + {DVBT_SCALE1_BAA, 0x59}, + {DVBT_SCALE1_BAB, 0x83}, + {DVBT_SCALE1_BAC, 0xd4}, + {DVBT_SCALE1_BB0, 0x65}, + {DVBT_SCALE1_BB1, 0x43}, + {DVBT_KB_P1, 0x1}, + {DVBT_KB_P2, 0x4}, + {DVBT_KB_P3, 0x7}, + {DVBT_K1_CR_STEP12, 0xa}, + {DVBT_REG_GPE, 0x1}, + {DVBT_SERIAL, 0x0}, + {DVBT_CDIV_PH0, 0x9}, + {DVBT_CDIV_PH1, 0x9}, + {DVBT_MPEG_IO_OPT_2_2, 0x0}, + {DVBT_MPEG_IO_OPT_1_0, 0x0}, + {DVBT_TRK_KS_P2, 0x4}, + {DVBT_TRK_KS_I2, 0x7}, + {DVBT_TR_THD_SET2, 0x6}, + {DVBT_TRK_KC_I2, 0x5}, + {DVBT_CR_THD_SET2, 0x1}, + }; + + dev_dbg(&client->dev, "\n"); + + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; + + for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { + ret = rtl2832_wr_demod_reg(dev, rtl2832_initial_regs[i].reg, + rtl2832_initial_regs[i].value); + if (ret) + goto err; + } + + /* load tuner specific settings */ + dev_dbg(&client->dev, "load settings for tuner=%02x\n", + dev->pdata->tuner); + switch (dev->pdata->tuner) { + case RTL2832_TUNER_FC2580: + len = ARRAY_SIZE(rtl2832_tuner_init_fc2580); + init = rtl2832_tuner_init_fc2580; + break; + case RTL2832_TUNER_FC0012: + case RTL2832_TUNER_FC0013: + len = ARRAY_SIZE(rtl2832_tuner_init_fc0012); + init = rtl2832_tuner_init_fc0012; + break; + case RTL2832_TUNER_TUA9001: + len = ARRAY_SIZE(rtl2832_tuner_init_tua9001); + init = rtl2832_tuner_init_tua9001; + break; + case RTL2832_TUNER_E4000: + len = ARRAY_SIZE(rtl2832_tuner_init_e4000); + init = rtl2832_tuner_init_e4000; + break; + case RTL2832_TUNER_R820T: + case RTL2832_TUNER_R828D: + len = ARRAY_SIZE(rtl2832_tuner_init_r820t); + init = rtl2832_tuner_init_r820t; + break; + case RTL2832_TUNER_SI2157: + len = ARRAY_SIZE(rtl2832_tuner_init_si2157); + init = rtl2832_tuner_init_si2157; + break; + default: + ret = -EINVAL; + goto err; + } + + for (i = 0; i < len; i++) { + ret = rtl2832_wr_demod_reg(dev, init[i].reg, init[i].value); + if (ret) + goto err; + } + + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + dev->sleeping = false; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_sleep(struct dvb_frontend *fe) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + + dev_dbg(&client->dev, "\n"); + + dev->sleeping = true; + dev->fe_status = 0; + + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "\n"); + s->min_delay_ms = 1000; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; + return 0; +} + +static int rtl2832_set_frontend(struct dvb_frontend *fe) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, j; + u64 bw_mode, num, num2; + u32 resamp_ratio, cfreq_off_ratio; + static u8 bw_params[3][32] = { + /* 6 MHz bandwidth */ + { + 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f, + 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2, + 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67, + 0x19, 0xe0, + }, + + /* 7 MHz bandwidth */ + { + 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf, + 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30, + 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22, + 0x19, 0x10, + }, + + /* 8 MHz bandwidth */ + { + 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf, + 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7, + 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8, + 0x19, 0xe0, + }, + }; + + dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n", + c->frequency, c->bandwidth_hz, c->inversion); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* If the frontend has get_if_frequency(), use it */ + if (fe->ops.tuner_ops.get_if_frequency) { + u32 if_freq; + + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + if (ret) + goto err; + + ret = rtl2832_set_if(fe, if_freq); + if (ret) + goto err; + } + + switch (c->bandwidth_hz) { + case 6000000: + i = 0; + bw_mode = 48000000; + break; + case 7000000: + i = 1; + bw_mode = 56000000; + break; + case 8000000: + i = 2; + bw_mode = 64000000; + break; + default: + dev_err(&client->dev, "invalid bandwidth_hz %u\n", + c->bandwidth_hz); + ret = -EINVAL; + goto err; + } + + for (j = 0; j < sizeof(bw_params[0]); j++) { + ret = regmap_bulk_write(dev->regmap, + 0x11c + j, &bw_params[i][j], 1); + if (ret) + goto err; + } + + /* calculate and set resample ratio + * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22) + * / ConstWithBandwidthMode) + */ + num = dev->pdata->clk * 7ULL; + num *= 0x400000; + num = div_u64(num, bw_mode); + resamp_ratio = num & 0x3ffffff; + ret = rtl2832_wr_demod_reg(dev, DVBT_RSAMP_RATIO, resamp_ratio); + if (ret) + goto err; + + /* calculate and set cfreq off ratio + * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20) + * / (CrystalFreqHz * 7)) + */ + num = bw_mode << 20; + num2 = dev->pdata->clk * 7ULL; + num = div_u64(num, num2); + num = -num; + cfreq_off_ratio = num & 0xfffff; + ret = rtl2832_wr_demod_reg(dev, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio); + if (ret) + goto err; + + /* soft reset */ + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 buf[3]; + + if (dev->sleeping) + return 0; + + ret = regmap_bulk_read(dev->regmap, 0x33c, buf, 2); + if (ret) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0x351, &buf[2], 1); + if (ret) + goto err; + + dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf); + + switch ((buf[0] >> 2) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[2] >> 2) & 1) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[2] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[1] >> 3) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[1] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u32 tmp; + u8 u8tmp, buf[2]; + u16 u16tmp; + + dev_dbg(&client->dev, "\n"); + + *status = 0; + if (dev->sleeping) + return 0; + + ret = rtl2832_rd_demod_reg(dev, DVBT_FSM_STAGE, &tmp); + if (ret) + goto err; + + if (tmp == 11) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (tmp == 10) { + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + } + + dev->fe_status = *status; + + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + /* read digital AGC */ + ret = regmap_bulk_read(dev->regmap, 0x305, &u8tmp, 1); + if (ret) + goto err; + + dev_dbg(&client->dev, "digital agc=%02x", u8tmp); + + u8tmp = ~u8tmp; + u16tmp = u8tmp << 8 | u8tmp << 0; + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = u16tmp; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned hierarchy, constellation; + #define CONSTELLATION_NUM 3 + #define HIERARCHY_NUM 4 + static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + {85387325, 85387325, 85387325, 85387325}, + {86676178, 86676178, 87167949, 87795660}, + {87659938, 87659938, 87885178, 88241743}, + }; + + ret = regmap_bulk_read(dev->regmap, 0x33c, &u8tmp, 1); + if (ret) + goto err; + + constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ + ret = -EINVAL; + if (constellation > CONSTELLATION_NUM - 1) + goto err; + + hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0x40c, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + if (u16tmp) + tmp = (constant[constellation][hierarchy] - + intlog10(u16tmp)) / ((1 << 24) / 10000); + else + tmp = 0; + + dev_dbg(&client->dev, "cnr raw=%u\n", u16tmp); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = tmp; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + ret = regmap_bulk_read(dev->regmap, 0x34e, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += u16tmp; + dev->post_bit_count += 1000000; + + dev_dbg(&client->dev, "ber errors=%u total=1000000\n", u16tmp); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* report SNR in resolution of 0.1 dB */ + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + + return 0; +} + +static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; + + return 0; +} + +/* + * I2C gate/mux/repeater logic + * There is delay mechanism to avoid unneeded I2C gate open / close. Gate close + * is delayed here a little bit in order to see if there is sequence of I2C + * messages sent to same I2C bus. + */ +static void rtl2832_i2c_gate_work(struct work_struct *work) +{ + struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, i2c_gate_work.work); + struct i2c_client *client = dev->client; + int ret; + + /* close gate */ + ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x00); + if (ret) + goto err; + + return; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); +} + +static int rtl2832_select(struct i2c_mux_core *muxc, u32 chan_id) +{ + struct rtl2832_dev *dev = i2c_mux_priv(muxc); + struct i2c_client *client = dev->client; + int ret; + + /* terminate possible gate closing */ + cancel_delayed_work(&dev->i2c_gate_work); + + /* open gate */ + ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_deselect(struct i2c_mux_core *muxc, u32 chan_id) +{ + struct rtl2832_dev *dev = i2c_mux_priv(muxc); + + schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100)); + return 0; +} + +static const struct dvb_frontend_ops rtl2832_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Realtek RTL2832 (DVB-T)", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .init = rtl2832_init, + .sleep = rtl2832_sleep, + + .get_tune_settings = rtl2832_get_tune_settings, + + .set_frontend = rtl2832_set_frontend, + .get_frontend = rtl2832_get_frontend, + + .read_status = rtl2832_read_status, + .read_snr = rtl2832_read_snr, + .read_ber = rtl2832_read_ber, +}; + +static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x305: + case 0x33c: + case 0x34e: + case 0x351: + case 0x40c ... 0x40d: + return true; + default: + break; + } + + return false; +} + +static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client) +{ + struct rtl2832_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + return &dev->fe; +} + +static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client) +{ + struct rtl2832_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + return dev->muxc->adapter[0]; +} + +static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) +{ + struct rtl2832_dev *dev = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "enable=%d\n", enable); + + if (enable) { + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap, 0x10c, "\x5f\xff", 2); + if (ret) + goto err; + ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x18", 1); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap, 0x192, "\x7f\xf7\xff", 3); + if (ret) + goto err; + } else { + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x08", 1); + if (ret) + goto err; + ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap, 0x10c, "\x00\x00", 2); + if (ret) + goto err; + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + } + + dev->slave_ts = enable; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 u8tmp; + + dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts); + + /* enable / disable PID filter */ + if (onoff) + u8tmp = 0x80; + else + u8tmp = 0x00; + + if (dev->slave_ts) + ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp); + else + ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, + int onoff) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 buf[4]; + + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n", + index, pid, onoff, dev->slave_ts); + + /* skip invalid PIDs (0x2000) */ + if (pid > 0x1fff || index > 32) + return 0; + + if (onoff) + set_bit(index, &dev->filters); + else + clear_bit(index, &dev->filters); + + /* enable / disable PIDs */ + buf[0] = (dev->filters >> 0) & 0xff; + buf[1] = (dev->filters >> 8) & 0xff; + buf[2] = (dev->filters >> 16) & 0xff; + buf[3] = (dev->filters >> 24) & 0xff; + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4); + else + ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); + if (ret) + goto err; + + /* add PID */ + buf[0] = (pid >> 8) & 0xff; + buf[1] = (pid >> 0) & 0xff; + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2); + else + ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_probe(struct i2c_client *client) +{ + struct rtl2832_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *i2c = client->adapter; + struct rtl2832_dev *dev; + int ret; + u8 tmp; + static const struct regmap_range_cfg regmap_range_cfg[] = { + { + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + .range_min = 0 * 0x100, + .range_max = 5 * 0x100, + }, + }; + + dev_dbg(&client->dev, "\n"); + + /* allocate memory for the internal state */ + dev = kzalloc(sizeof(struct rtl2832_dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + goto err; + } + + /* setup the state */ + i2c_set_clientdata(client, dev); + dev->client = client; + dev->pdata = client->dev.platform_data; + dev->sleeping = true; + INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); + /* create regmap */ + dev->regmap_config.reg_bits = 8; + dev->regmap_config.val_bits = 8; + dev->regmap_config.volatile_reg = rtl2832_volatile_reg; + dev->regmap_config.max_register = 5 * 0x100; + dev->regmap_config.ranges = regmap_range_cfg; + dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg); + dev->regmap_config.cache_type = REGCACHE_NONE; + dev->regmap = regmap_init_i2c(client, &dev->regmap_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + /* check if the demod is there */ + ret = regmap_bulk_read(dev->regmap, 0x000, &tmp, 1); + if (ret) + goto err_regmap_exit; + + /* create muxed i2c adapter for demod tuner bus */ + dev->muxc = i2c_mux_alloc(i2c, &i2c->dev, 1, 0, I2C_MUX_LOCKED, + rtl2832_select, rtl2832_deselect); + if (!dev->muxc) { + ret = -ENOMEM; + goto err_regmap_exit; + } + dev->muxc->priv = dev; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_regmap_exit; + + /* create dvb_frontend */ + memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + + /* setup callbacks */ + pdata->get_dvb_frontend = rtl2832_get_dvb_frontend; + pdata->get_i2c_adapter = rtl2832_get_i2c_adapter; + pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl; + pdata->pid_filter = rtl2832_pid_filter; + pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl; + pdata->regmap = dev->regmap; + + dev_info(&client->dev, "Realtek RTL2832 successfully attached\n"); + return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void rtl2832_remove(struct i2c_client *client) +{ + struct rtl2832_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + cancel_delayed_work_sync(&dev->i2c_gate_work); + + i2c_mux_del_adapters(dev->muxc); + + regmap_exit(dev->regmap); + + kfree(dev); +} + +static const struct i2c_device_id rtl2832_id_table[] = { + {"rtl2832", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); + +static struct i2c_driver rtl2832_driver = { + .driver = { + .name = "rtl2832", + .suppress_bind_attrs = true, + }, + .probe = rtl2832_probe, + .remove = rtl2832_remove, + .id_table = rtl2832_id_table, +}; + +module_i2c_driver(rtl2832_driver); + +MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h new file mode 100644 index 0000000000..85a8064bf9 --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Realtek RTL2832 DVB-T demodulator driver + * + * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi> + */ + +#ifndef RTL2832_H +#define RTL2832_H + +#include <linux/dvb/frontend.h> +#include <linux/i2c-mux.h> + +/** + * struct rtl2832_platform_data - Platform data for the rtl2832 driver + * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000). + * @tuner: Used tuner model. + * @get_dvb_frontend: Get DVB frontend. + * @get_i2c_adapter: Get I2C adapter. + * @slave_ts_ctrl: Control slave TS interface. + * @pid_filter: Set PID to PID filter. + * @pid_filter_ctrl: Control PID filter. + */ +struct rtl2832_platform_data { + u32 clk; + /* + * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver. + */ +#define RTL2832_TUNER_FC2580 0x21 +#define RTL2832_TUNER_TUA9001 0x24 +#define RTL2832_TUNER_FC0012 0x26 +#define RTL2832_TUNER_E4000 0x27 +#define RTL2832_TUNER_FC0013 0x29 +#define RTL2832_TUNER_R820T 0x2a +#define RTL2832_TUNER_R828D 0x2b +#define RTL2832_TUNER_SI2157 0x2c + u8 tuner; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + int (*slave_ts_ctrl)(struct i2c_client *, bool); + int (*pid_filter)(struct dvb_frontend *, u8, u16, int); + int (*pid_filter_ctrl)(struct dvb_frontend *, int); +/* private: Register access for SDR module use only */ + struct regmap *regmap; +}; + +#endif /* RTL2832_H */ diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h new file mode 100644 index 0000000000..f11ba038d5 --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -0,0 +1,415 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Realtek RTL2832 DVB-T demodulator driver + * + * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi> + */ + +#ifndef RTL2832_PRIV_H +#define RTL2832_PRIV_H + +#include <linux/regmap.h> +#include <linux/math64.h> +#include <linux/bitops.h> + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "rtl2832.h" + +struct rtl2832_dev { + struct rtl2832_platform_data *pdata; + struct i2c_client *client; + struct regmap_config regmap_config; + struct regmap *regmap; + struct i2c_mux_core *muxc; + struct dvb_frontend fe; + enum fe_status fe_status; + u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ + u64 post_bit_error; + u64 post_bit_count; + bool sleeping; + struct delayed_work i2c_gate_work; + unsigned long filters; /* PID filter */ + bool slave_ts; +}; + +struct rtl2832_reg_entry { + u16 start_address; + u8 msb; + u8 lsb; +}; + +struct rtl2832_reg_value { + int reg; + u32 value; +}; + +/* Demod register bit names */ +enum DVBT_REG_BIT_NAME { + DVBT_SOFT_RST, + DVBT_IIC_REPEAT, + DVBT_TR_WAIT_MIN_8K, + DVBT_RSD_BER_FAIL_VAL, + DVBT_EN_BK_TRK, + DVBT_REG_PI, + DVBT_REG_PFREQ_1_0, + DVBT_PD_DA8, + DVBT_LOCK_TH, + DVBT_BER_PASS_SCAL, + DVBT_CE_FFSM_BYPASS, + DVBT_ALPHAIIR_N, + DVBT_ALPHAIIR_DIF, + DVBT_EN_TRK_SPAN, + DVBT_LOCK_TH_LEN, + DVBT_CCI_THRE, + DVBT_CCI_MON_SCAL, + DVBT_CCI_M0, + DVBT_CCI_M1, + DVBT_CCI_M2, + DVBT_CCI_M3, + DVBT_SPEC_INIT_0, + DVBT_SPEC_INIT_1, + DVBT_SPEC_INIT_2, + DVBT_AD_EN_REG, + DVBT_AD_EN_REG1, + DVBT_EN_BBIN, + DVBT_MGD_THD0, + DVBT_MGD_THD1, + DVBT_MGD_THD2, + DVBT_MGD_THD3, + DVBT_MGD_THD4, + DVBT_MGD_THD5, + DVBT_MGD_THD6, + DVBT_MGD_THD7, + DVBT_EN_CACQ_NOTCH, + DVBT_AD_AV_REF, + DVBT_PIP_ON, + DVBT_SCALE1_B92, + DVBT_SCALE1_B93, + DVBT_SCALE1_BA7, + DVBT_SCALE1_BA9, + DVBT_SCALE1_BAA, + DVBT_SCALE1_BAB, + DVBT_SCALE1_BAC, + DVBT_SCALE1_BB0, + DVBT_SCALE1_BB1, + DVBT_KB_P1, + DVBT_KB_P2, + DVBT_KB_P3, + DVBT_OPT_ADC_IQ, + DVBT_AD_AVI, + DVBT_AD_AVQ, + DVBT_K1_CR_STEP12, + DVBT_TRK_KS_P2, + DVBT_TRK_KS_I2, + DVBT_TR_THD_SET2, + DVBT_TRK_KC_P2, + DVBT_TRK_KC_I2, + DVBT_CR_THD_SET2, + DVBT_PSET_IFFREQ, + DVBT_SPEC_INV, + DVBT_BW_INDEX, + DVBT_RSAMP_RATIO, + DVBT_CFREQ_OFF_RATIO, + DVBT_FSM_STAGE, + DVBT_RX_CONSTEL, + DVBT_RX_HIER, + DVBT_RX_C_RATE_LP, + DVBT_RX_C_RATE_HP, + DVBT_GI_IDX, + DVBT_FFT_MODE_IDX, + DVBT_RSD_BER_EST, + DVBT_CE_EST_EVM, + DVBT_RF_AGC_VAL, + DVBT_IF_AGC_VAL, + DVBT_DAGC_VAL, + DVBT_SFREQ_OFF, + DVBT_CFREQ_OFF, + DVBT_POLAR_RF_AGC, + DVBT_POLAR_IF_AGC, + DVBT_AAGC_HOLD, + DVBT_EN_RF_AGC, + DVBT_EN_IF_AGC, + DVBT_IF_AGC_MIN, + DVBT_IF_AGC_MAX, + DVBT_RF_AGC_MIN, + DVBT_RF_AGC_MAX, + DVBT_IF_AGC_MAN, + DVBT_IF_AGC_MAN_VAL, + DVBT_RF_AGC_MAN, + DVBT_RF_AGC_MAN_VAL, + DVBT_DAGC_TRG_VAL, + DVBT_AGC_TARG_VAL, + DVBT_LOOP_GAIN_3_0, + DVBT_LOOP_GAIN_4, + DVBT_VTOP, + DVBT_KRF, + DVBT_AGC_TARG_VAL_0, + DVBT_AGC_TARG_VAL_8_1, + DVBT_AAGC_LOOP_GAIN, + DVBT_LOOP_GAIN2_3_0, + DVBT_LOOP_GAIN2_4, + DVBT_LOOP_GAIN3, + DVBT_VTOP1, + DVBT_VTOP2, + DVBT_VTOP3, + DVBT_KRF1, + DVBT_KRF2, + DVBT_KRF3, + DVBT_KRF4, + DVBT_EN_GI_PGA, + DVBT_THD_LOCK_UP, + DVBT_THD_LOCK_DW, + DVBT_THD_UP1, + DVBT_THD_DW1, + DVBT_INTER_CNT_LEN, + DVBT_GI_PGA_STATE, + DVBT_EN_AGC_PGA, + DVBT_CKOUTPAR, + DVBT_CKOUT_PWR, + DVBT_SYNC_DUR, + DVBT_ERR_DUR, + DVBT_SYNC_LVL, + DVBT_ERR_LVL, + DVBT_VAL_LVL, + DVBT_SERIAL, + DVBT_SER_LSB, + DVBT_CDIV_PH0, + DVBT_CDIV_PH1, + DVBT_MPEG_IO_OPT_2_2, + DVBT_MPEG_IO_OPT_1_0, + DVBT_CKOUTPAR_PIP, + DVBT_CKOUT_PWR_PIP, + DVBT_SYNC_LVL_PIP, + DVBT_ERR_LVL_PIP, + DVBT_VAL_LVL_PIP, + DVBT_CKOUTPAR_PID, + DVBT_CKOUT_PWR_PID, + DVBT_SYNC_LVL_PID, + DVBT_ERR_LVL_PID, + DVBT_VAL_LVL_PID, + DVBT_SM_PASS, + DVBT_UPDATE_REG_2, + DVBT_BTHD_P3, + DVBT_BTHD_D3, + DVBT_FUNC4_REG0, + DVBT_FUNC4_REG1, + DVBT_FUNC4_REG2, + DVBT_FUNC4_REG3, + DVBT_FUNC4_REG4, + DVBT_FUNC4_REG5, + DVBT_FUNC4_REG6, + DVBT_FUNC4_REG7, + DVBT_FUNC4_REG8, + DVBT_FUNC4_REG9, + DVBT_FUNC4_REG10, + DVBT_FUNC5_REG0, + DVBT_FUNC5_REG1, + DVBT_FUNC5_REG2, + DVBT_FUNC5_REG3, + DVBT_FUNC5_REG4, + DVBT_FUNC5_REG5, + DVBT_FUNC5_REG6, + DVBT_FUNC5_REG7, + DVBT_FUNC5_REG8, + DVBT_FUNC5_REG9, + DVBT_FUNC5_REG10, + DVBT_FUNC5_REG11, + DVBT_FUNC5_REG12, + DVBT_FUNC5_REG13, + DVBT_FUNC5_REG14, + DVBT_FUNC5_REG15, + DVBT_FUNC5_REG16, + DVBT_FUNC5_REG17, + DVBT_FUNC5_REG18, + DVBT_AD7_SETTING, + DVBT_RSSI_R, + DVBT_ACI_DET_IND, + DVBT_REG_MON, + DVBT_REG_MONSEL, + DVBT_REG_GPE, + DVBT_REG_GPO, + DVBT_REG_4MSEL, + DVBT_TEST_REG_1, + DVBT_TEST_REG_2, + DVBT_TEST_REG_3, + DVBT_TEST_REG_4, + DVBT_REG_BIT_NAME_ITEM_TERMINATOR, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_fc2580[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x6}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x16}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x9c}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x6}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x16}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x9c}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, + {DVBT_OPT_ADC_IQ, 0x1}, + {DVBT_AD_AVI, 0x0}, + {DVBT_AD_AVQ, 0x0}, + {DVBT_SPEC_INV, 0x0}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { + {DVBT_DAGC_TRG_VAL, 0x5a}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x6}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x16}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9bf}, + {DVBT_EN_GI_PGA, 0x0}, + {DVBT_THD_LOCK_UP, 0x0}, + {DVBT_THD_LOCK_DW, 0x0}, + {DVBT_THD_UP1, 0x11}, + {DVBT_THD_DW1, 0xef}, + {DVBT_INTER_CNT_LEN, 0xc}, + {DVBT_GI_PGA_STATE, 0x0}, + {DVBT_EN_AGC_PGA, 0x1}, + {DVBT_IF_AGC_MAN, 0x0}, + {DVBT_SPEC_INV, 0x0}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = { + {DVBT_DAGC_TRG_VAL, 0x5a}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x18}, + {DVBT_LOOP_GAIN2_3_0, 0x8}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x18}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9d4}, + {DVBT_EN_GI_PGA, 0x0}, + {DVBT_THD_LOCK_UP, 0x0}, + {DVBT_THD_LOCK_DW, 0x0}, + {DVBT_THD_UP1, 0x14}, + {DVBT_THD_DW1, 0xec}, + {DVBT_INTER_CNT_LEN, 0xc}, + {DVBT_GI_PGA_STATE, 0x0}, + {DVBT_EN_AGC_PGA, 0x1}, + {DVBT_REG_GPE, 0x1}, + {DVBT_REG_GPO, 0x1}, + {DVBT_REG_MONSEL, 0x1}, + {DVBT_REG_MON, 0x1}, + {DVBT_REG_4MSEL, 0x0}, + {DVBT_SPEC_INV, 0x0}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x40}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x8}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x18}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, + {DVBT_SPEC_INV, 0x1}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_si2157[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x40}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x8}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x18}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, + {DVBT_SPEC_INV, 0x0}, +}; + +#endif /* RTL2832_PRIV_H */ diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c new file mode 100644 index 0000000000..02c619e516 --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -0,0 +1,1495 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Realtek RTL2832U SDR driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + * + * GNU Radio plugin "gr-kernel" for device usage will be on: + * https://git.linuxtv.org/anttip/gr-kernel.git + */ + +#include "rtl2832_sdr.h" +#include "dvb_usb.h" + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.h> + +#include <linux/platform_device.h> +#include <linux/jiffies.h> +#include <linux/math64.h> +#include <linux/regmap.h> + +static bool rtl2832_sdr_emulated_fmt; +module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644); +MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)"); + +/* Original macro does not contain enough null pointer checks for our need */ +#define V4L2_SUBDEV_HAS_OP(sd, o, f) \ + ((sd) && (sd)->ops && (sd)->ops->o && (sd)->ops->o->f) + +#define MAX_BULK_BUFS (10) +#define BULK_BUFFER_SIZE (128 * 512) + +static const struct v4l2_frequency_band bands_adc[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 300000, + .rangehigh = 300000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 900001, + .rangehigh = 2800000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 2, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 3200000, + .rangehigh = 3200000, + }, +}; + +static const struct v4l2_frequency_band bands_fm[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 50000000, + .rangehigh = 2000000000, + }, +}; + +/* stream formats */ +struct rtl2832_sdr_format { + char *name; + u32 pixelformat; + u32 buffersize; +}; + +static struct rtl2832_sdr_format formats[] = { + { + .pixelformat = V4L2_SDR_FMT_CU8, + .buffersize = BULK_BUFFER_SIZE, + }, { + .pixelformat = V4L2_SDR_FMT_CU16LE, + .buffersize = BULK_BUFFER_SIZE * 2, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +/* intermediate buffers with raw data from the USB device */ +struct rtl2832_sdr_frame_buf { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct rtl2832_sdr_dev { +#define POWER_ON 0 /* BIT(0) */ +#define URB_BUF 1 /* BIT(1) */ + unsigned long flags; + + struct platform_device *pdev; + struct regmap *regmap; + + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_subdev *v4l2_subdev; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + unsigned sequence; /* buffer sequence counter */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ + + unsigned int vb_full; /* vb is full and packets dropped */ + + struct urb *urb_list[MAX_BULK_BUFS]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_BULK_BUFS]; + dma_addr_t dma_addr[MAX_BULK_BUFS]; + int urbs_initialized; + int urbs_submitted; + + unsigned int f_adc, f_tuner; + u32 pixelformat; + u32 buffersize; + unsigned int num_formats; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + + /* for sample rate calc */ + unsigned int sample; + unsigned int sample_measured; + unsigned long jiffies_next; +}; + +/* Private functions */ +static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( + struct rtl2832_sdr_dev *dev) +{ + unsigned long flags; + struct rtl2832_sdr_frame_buf *buf = NULL; + + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + if (list_empty(&dev->queued_bufs)) + goto leave; + + buf = list_entry(dev->queued_bufs.next, + struct rtl2832_sdr_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); + return buf; +} + +static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_dev *dev, + void *dst, const u8 *src, unsigned int src_len) +{ + struct platform_device *pdev = dev->pdev; + unsigned int dst_len; + + if (dev->pixelformat == V4L2_SDR_FMT_CU8) { + /* native stream, no need to convert */ + memcpy(dst, src, src_len); + dst_len = src_len; + } else if (dev->pixelformat == V4L2_SDR_FMT_CU16LE) { + /* convert u8 to u16 */ + unsigned int i; + u16 *u16dst = dst; + + for (i = 0; i < src_len; i++) + *u16dst++ = (src[i] << 8) | (src[i] >> 0); + dst_len = 2 * src_len; + } else { + dst_len = 0; + } + + /* calculate sample rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(dev->jiffies_next))) { + #define MSECS 10000UL + unsigned int msecs = jiffies_to_msecs(jiffies - + dev->jiffies_next + msecs_to_jiffies(MSECS)); + unsigned int samples = dev->sample - dev->sample_measured; + + dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + dev->sample_measured = dev->sample; + dev_dbg(&pdev->dev, + "slen=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, samples * 1000UL / msecs); + } + + /* total number of I+Q pairs */ + dev->sample += src_len / 2; + + return dst_len; +} + +/* + * This gets called for the bulk stream pipe. This is done in interrupt + * time, so it has to be fast, not crash, and not stall. Neat. + */ +static void rtl2832_sdr_urb_complete(struct urb *urb) +{ + struct rtl2832_sdr_dev *dev = urb->context; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_frame_buf *fbuf; + + dev_dbg_ratelimited(&pdev->dev, "status=%d length=%d/%d errors=%d\n", + urb->status, urb->actual_length, + urb->transfer_buffer_length, urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_err_ratelimited(&pdev->dev, "urb failed=%d\n", urb->status); + break; + } + + if (likely(urb->actual_length > 0)) { + void *ptr; + unsigned int len; + /* get free framebuffer */ + fbuf = rtl2832_sdr_get_next_fill_buf(dev); + if (unlikely(fbuf == NULL)) { + dev->vb_full++; + dev_notice_ratelimited(&pdev->dev, + "video buffer is full, %d packets dropped\n", + dev->vb_full); + goto skip; + } + + /* fill framebuffer */ + ptr = vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0); + len = rtl2832_sdr_convert_stream(dev, ptr, urb->transfer_buffer, + urb->actual_length); + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, len); + fbuf->vb.vb2_buf.timestamp = ktime_get_ns(); + fbuf->vb.sequence = dev->sequence++; + vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } +skip: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + int i; + + for (i = dev->urbs_submitted - 1; i >= 0; i--) { + dev_dbg(&pdev->dev, "kill urb=%d\n", i); + /* stop the URB */ + usb_kill_urb(dev->urb_list[i]); + } + dev->urbs_submitted = 0; + + return 0; +} + +static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + int i, ret; + + for (i = 0; i < dev->urbs_initialized; i++) { + dev_dbg(&pdev->dev, "submit urb=%d\n", i); + ret = usb_submit_urb(dev->urb_list[i], GFP_KERNEL); + if (ret) { + dev_err(&pdev->dev, + "Could not submit urb no. %d - get them all back\n", + i); + rtl2832_sdr_kill_urbs(dev); + return ret; + } + dev->urbs_submitted++; + } + + return 0; +} + +static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + + if (test_bit(URB_BUF, &dev->flags)) { + while (dev->buf_num) { + dev->buf_num--; + dev_dbg(&pdev->dev, "free buf=%d\n", dev->buf_num); + usb_free_coherent(dev->udev, dev->buf_size, + dev->buf_list[dev->buf_num], + dev->dma_addr[dev->buf_num]); + } + } + clear_bit(URB_BUF, &dev->flags); + + return 0; +} + +static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + + dev->buf_num = 0; + dev->buf_size = BULK_BUFFER_SIZE; + + dev_dbg(&pdev->dev, "all in all I will use %u bytes for streaming\n", + MAX_BULK_BUFS * BULK_BUFFER_SIZE); + + for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { + dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, + BULK_BUFFER_SIZE, GFP_KERNEL, + &dev->dma_addr[dev->buf_num]); + if (!dev->buf_list[dev->buf_num]) { + dev_dbg(&pdev->dev, "alloc buf=%d failed\n", + dev->buf_num); + rtl2832_sdr_free_stream_bufs(dev); + return -ENOMEM; + } + + dev_dbg(&pdev->dev, "alloc buf=%d %p (dma %llu)\n", + dev->buf_num, dev->buf_list[dev->buf_num], + (long long)dev->dma_addr[dev->buf_num]); + set_bit(URB_BUF, &dev->flags); + } + + return 0; +} + +static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + int i; + + rtl2832_sdr_kill_urbs(dev); + + for (i = dev->urbs_initialized - 1; i >= 0; i--) { + if (dev->urb_list[i]) { + dev_dbg(&pdev->dev, "free urb=%d\n", i); + /* free the URBs */ + usb_free_urb(dev->urb_list[i]); + } + } + dev->urbs_initialized = 0; + + return 0; +} + +static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + int i, j; + + /* allocate the URBs */ + for (i = 0; i < MAX_BULK_BUFS; i++) { + dev_dbg(&pdev->dev, "alloc urb=%d\n", i); + dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb_list[i]) { + for (j = 0; j < i; j++) { + usb_free_urb(dev->urb_list[j]); + dev->urb_list[j] = NULL; + } + dev->urbs_initialized = 0; + return -ENOMEM; + } + usb_fill_bulk_urb(dev->urb_list[i], + dev->udev, + usb_rcvbulkpipe(dev->udev, 0x81), + dev->buf_list[i], + BULK_BUFFER_SIZE, + rtl2832_sdr_urb_complete, dev); + + dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + dev->urb_list[i]->transfer_dma = dev->dma_addr[i]; + dev->urbs_initialized++; + } + + return 0; +} + +/* Must be called with vb_queue_lock hold */ +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + unsigned long flags; + + dev_dbg(&pdev->dev, "\n"); + + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + while (!list_empty(&dev->queued_bufs)) { + struct rtl2832_sdr_frame_buf *buf; + + buf = list_entry(dev->queued_bufs.next, + struct rtl2832_sdr_frame_buf, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); +} + +static int rtl2832_sdr_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "\n"); + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->card, dev->vdev.name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + return 0; +} + +/* Videobuf2 operations */ +static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) +{ + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "nbuffers=%d\n", *nbuffers); + + /* Need at least 8 buffers */ + if (vq->num_buffers + *nbuffers < 8) + *nbuffers = 8 - vq->num_buffers; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(dev->buffersize); + dev_dbg(&pdev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); + return 0; +} + +static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb) +{ + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + + /* Don't allow queueing new buffers after device disconnection */ + if (!dev->udev) + return -ENODEV; + + return 0; +} + +static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct rtl2832_sdr_frame_buf *buf = + container_of(vbuf, struct rtl2832_sdr_frame_buf, vb); + unsigned long flags; + + /* Check the device has not disconnected between prep and queuing */ + if (!dev->udev) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &dev->queued_bufs); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); +} + +static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + int ret; + unsigned int f_sr, f_if; + u8 buf[4], u8tmp1, u8tmp2; + u64 u64tmp; + u32 u32tmp; + + dev_dbg(&pdev->dev, "f_adc=%u\n", dev->f_adc); + + if (!test_bit(POWER_ON, &dev->flags)) + return 0; + + if (dev->f_adc == 0) + return 0; + + f_sr = dev->f_adc; + + ret = regmap_bulk_write(dev->regmap, 0x13e, "\x00\x00", 2); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x115, "\x00\x00\x00\x00", 4); + if (ret) + goto err; + + /* get IF from tuner */ + if (fe->ops.tuner_ops.get_if_frequency) + ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if); + else + ret = -EINVAL; + + if (ret) + goto err; + + /* program IF */ + u64tmp = f_if % pdata->clk; + u64tmp *= 0x400000; + u64tmp = div_u64(u64tmp, pdata->clk); + u64tmp = -u64tmp; + u32tmp = u64tmp & 0x3fffff; + + dev_dbg(&pdev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp); + + buf[0] = (u32tmp >> 16) & 0xff; + buf[1] = (u32tmp >> 8) & 0xff; + buf[2] = (u32tmp >> 0) & 0xff; + + ret = regmap_bulk_write(dev->regmap, 0x119, buf, 3); + if (ret) + goto err; + + /* BB / IF mode */ + /* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */ + if (f_if) { + u8tmp1 = 0x1a; /* disable Zero-IF */ + u8tmp2 = 0x8d; /* enable ADC I */ + } else { + u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */ + u8tmp2 = 0xcd; /* enable ADC I, ADC Q */ + } + + ret = regmap_write(dev->regmap, 0x1b1, u8tmp1); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x008, u8tmp2); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x006, 0x80); + if (ret) + goto err; + + /* program sampling rate (resampling down) */ + u32tmp = div_u64(pdata->clk * 0x400000ULL, f_sr * 4U); + u32tmp <<= 2; + buf[0] = (u32tmp >> 24) & 0xff; + buf[1] = (u32tmp >> 16) & 0xff; + buf[2] = (u32tmp >> 8) & 0xff; + buf[3] = (u32tmp >> 0) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x19f, buf, 4); + if (ret) + goto err; + + /* low-pass filter */ + ret = regmap_bulk_write(dev->regmap, 0x11c, + "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", + 20); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2); + if (ret) + goto err; + + /* mode */ + ret = regmap_write(dev->regmap, 0x019, 0x05); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x01a, + "\x1b\x16\x0d\x06\x01\xff", 6); + if (ret) + goto err; + + /* FSM */ + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\xf0\x0f", 3); + if (ret) + goto err; + + /* PID filter */ + ret = regmap_write(dev->regmap, 0x061, 0x60); + if (ret) + goto err; + + /* used RF tuner based settings */ + switch (pdata->tuner) { + case RTL2832_SDR_TUNER_E4000: + ret = regmap_write(dev->regmap, 0x112, 0x5a); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x103, 0x5a); + ret = regmap_write(dev->regmap, 0x1c7, 0x30); + ret = regmap_write(dev->regmap, 0x104, 0xd0); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x18); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x80); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x011, 0xd4); + ret = regmap_write(dev->regmap, 0x1e5, 0xf0); + ret = regmap_write(dev->regmap, 0x1d9, 0x00); + ret = regmap_write(dev->regmap, 0x1db, 0x00); + ret = regmap_write(dev->regmap, 0x1dd, 0x14); + ret = regmap_write(dev->regmap, 0x1de, 0xec); + ret = regmap_write(dev->regmap, 0x1d8, 0x0c); + ret = regmap_write(dev->regmap, 0x1e6, 0x02); + ret = regmap_write(dev->regmap, 0x1d7, 0x09); + ret = regmap_write(dev->regmap, 0x00d, 0x83); + ret = regmap_write(dev->regmap, 0x010, 0x49); + ret = regmap_write(dev->regmap, 0x00d, 0x87); + ret = regmap_write(dev->regmap, 0x00d, 0x85); + ret = regmap_write(dev->regmap, 0x013, 0x02); + break; + case RTL2832_SDR_TUNER_FC0012: + case RTL2832_SDR_TUNER_FC0013: + ret = regmap_write(dev->regmap, 0x112, 0x5a); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x103, 0x5a); + ret = regmap_write(dev->regmap, 0x1c7, 0x2c); + ret = regmap_write(dev->regmap, 0x104, 0xcc); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x16); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x80); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xbf", 2); + ret = regmap_write(dev->regmap, 0x1e5, 0xf0); + ret = regmap_write(dev->regmap, 0x1d9, 0x00); + ret = regmap_write(dev->regmap, 0x1db, 0x00); + ret = regmap_write(dev->regmap, 0x1dd, 0x11); + ret = regmap_write(dev->regmap, 0x1de, 0xef); + ret = regmap_write(dev->regmap, 0x1d8, 0x0c); + ret = regmap_write(dev->regmap, 0x1e6, 0x02); + ret = regmap_write(dev->regmap, 0x1d7, 0x09); + break; + case RTL2832_SDR_TUNER_R820T: + case RTL2832_SDR_TUNER_R828D: + ret = regmap_write(dev->regmap, 0x112, 0x5a); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x115, 0x01); + ret = regmap_write(dev->regmap, 0x103, 0x80); + ret = regmap_write(dev->regmap, 0x1c7, 0x24); + ret = regmap_write(dev->regmap, 0x104, 0xcc); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x14); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x80); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x011, 0xf4); + break; + case RTL2832_SDR_TUNER_FC2580: + ret = regmap_write(dev->regmap, 0x112, 0x39); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x103, 0x5a); + ret = regmap_write(dev->regmap, 0x1c7, 0x2c); + ret = regmap_write(dev->regmap, 0x104, 0xcc); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x16); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x9c); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xf4", 2); + break; + default: + dev_notice(&pdev->dev, "Unsupported tuner\n"); + } + + /* software reset */ + ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x04); + if (ret) + goto err; + + ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x00); + if (ret) + goto err; +err: + return ret; +}; + +static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + /* PID filter */ + ret = regmap_write(dev->regmap, 0x061, 0xe0); + if (ret) + goto err; + + /* mode */ + ret = regmap_write(dev->regmap, 0x019, 0x20); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2); + if (ret) + goto err; + + /* FSM */ + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x13e, "\x40\x00", 2); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x115, "\x06\x3f\xce\xcc", 4); + if (ret) + goto err; +err: + return; +}; + +static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + + /* + * tuner RF (Hz) + */ + if (dev->f_tuner == 0) + return 0; + + /* + * bandwidth (Hz) + */ + bandwidth_auto = v4l2_ctrl_find(&dev->hdl, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); + bandwidth = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); + if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { + c->bandwidth_hz = dev->f_adc; + v4l2_ctrl_s_ctrl(bandwidth, dev->f_adc); + } else { + c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth); + } + + c->frequency = dev->f_tuner; + c->delivery_system = SYS_DVBT; + + dev_dbg(&pdev->dev, "frequency=%u bandwidth=%d\n", + c->frequency, c->bandwidth_hz); + + if (!test_bit(POWER_ON, &dev->flags)) + return 0; + + if (!V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) { + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + } + + return 0; +}; + +static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + + dev_dbg(&pdev->dev, "\n"); + + if (fe->ops.tuner_ops.init) + fe->ops.tuner_ops.init(fe); + + return 0; +}; + +static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + + dev_dbg(&pdev->dev, "\n"); + + if (fe->ops.tuner_ops.sleep) + fe->ops.tuner_ops.sleep(fe); + + return; +}; + +static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_usb_device *d = pdata->dvb_usb_device; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + if (!dev->udev) + return -ENODEV; + + if (mutex_lock_interruptible(&dev->v4l2_lock)) + return -ERESTARTSYS; + + if (d->props->power_ctrl) + d->props->power_ctrl(d, 1); + + /* enable ADC */ + if (d->props->frontend_ctrl) + d->props->frontend_ctrl(pdata->dvb_frontend, 1); + + set_bit(POWER_ON, &dev->flags); + + /* wake-up tuner */ + if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power)) + ret = v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); + else + ret = rtl2832_sdr_set_tuner(dev); + if (ret) + goto err; + + ret = rtl2832_sdr_set_tuner_freq(dev); + if (ret) + goto err; + + ret = rtl2832_sdr_set_adc(dev); + if (ret) + goto err; + + ret = rtl2832_sdr_alloc_stream_bufs(dev); + if (ret) + goto err; + + ret = rtl2832_sdr_alloc_urbs(dev); + if (ret) + goto err; + + dev->sequence = 0; + + ret = rtl2832_sdr_submit_urbs(dev); + if (ret) + goto err; + +err: + mutex_unlock(&dev->v4l2_lock); + + return ret; +} + +static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) +{ + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_usb_device *d = pdata->dvb_usb_device; + + dev_dbg(&pdev->dev, "\n"); + + mutex_lock(&dev->v4l2_lock); + + rtl2832_sdr_kill_urbs(dev); + rtl2832_sdr_free_urbs(dev); + rtl2832_sdr_free_stream_bufs(dev); + rtl2832_sdr_cleanup_queued_bufs(dev); + rtl2832_sdr_unset_adc(dev); + + /* sleep tuner */ + if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power)) + v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 0); + else + rtl2832_sdr_unset_tuner(dev); + + clear_bit(POWER_ON, &dev->flags); + + /* disable ADC */ + if (d->props->frontend_ctrl) + d->props->frontend_ctrl(pdata->dvb_frontend, 0); + + if (d->props->power_ctrl) + d->props->power_ctrl(d, 0); + + mutex_unlock(&dev->v4l2_lock); +} + +static const struct vb2_ops rtl2832_sdr_vb2_ops = { + .queue_setup = rtl2832_sdr_queue_setup, + .buf_prepare = rtl2832_sdr_buf_prepare, + .buf_queue = rtl2832_sdr_buf_queue, + .start_streaming = rtl2832_sdr_start_streaming, + .stop_streaming = rtl2832_sdr_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int rtl2832_sdr_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "index=%d type=%d\n", v->index, v->type); + + if (v->index == 0) { + strscpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name)); + v->type = V4L2_TUNER_ADC; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 300000; + v->rangehigh = 3200000; + ret = 0; + } else if (v->index == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_tuner)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_tuner, v); + } else if (v->index == 1) { + strscpy(v->name, "RF: <unknown>", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 50000000; + v->rangehigh = 2000000000; + ret = 0; + } else { + ret = -EINVAL; + } + return ret; +} + +static int rtl2832_sdr_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + if (v->index == 0) { + ret = 0; + } else if (v->index == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_tuner)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_tuner, v); + } else if (v->index == 1) { + ret = 0; + } else { + ret = -EINVAL; + } + return ret; +} + +static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); + + if (band->tuner == 0) { + if (band->index >= ARRAY_SIZE(bands_adc)) + return -EINVAL; + + *band = bands_adc[band->index]; + ret = 0; + } else if (band->tuner == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, enum_freq_bands)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, enum_freq_bands, band); + } else if (band->tuner == 1) { + if (band->index >= ARRAY_SIZE(bands_fm)) + return -EINVAL; + + *band = bands_fm[band->index]; + ret = 0; + } else { + ret = -EINVAL; + } + return ret; +} + +static int rtl2832_sdr_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "tuner=%d type=%d\n", f->tuner, f->type); + + if (f->tuner == 0) { + f->frequency = dev->f_adc; + f->type = V4L2_TUNER_ADC; + ret = 0; + } else if (f->tuner == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_frequency)) { + f->type = V4L2_TUNER_RF; + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_frequency, f); + } else if (f->tuner == 1) { + f->frequency = dev->f_tuner; + f->type = V4L2_TUNER_RF; + ret = 0; + } else { + ret = -EINVAL; + } + return ret; +} + +static int rtl2832_sdr_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + int ret, band; + + dev_dbg(&pdev->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); + + /* ADC band midpoints */ + #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) + #define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2) + + if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) { + if (f->frequency < BAND_ADC_0) + band = 0; + else if (f->frequency < BAND_ADC_1) + band = 1; + else + band = 2; + + dev->f_adc = clamp_t(unsigned int, f->frequency, + bands_adc[band].rangelow, + bands_adc[band].rangehigh); + + dev_dbg(&pdev->dev, "ADC frequency=%u Hz\n", dev->f_adc); + ret = rtl2832_sdr_set_adc(dev); + } else if (f->tuner == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_frequency, f); + } else if (f->tuner == 1) { + dev->f_tuner = clamp_t(unsigned int, f->frequency, + bands_fm[0].rangelow, + bands_fm[0].rangehigh); + dev_dbg(&pdev->dev, "RF frequency=%u Hz\n", f->frequency); + + ret = rtl2832_sdr_set_tuner_freq(dev); + } else { + ret = -EINVAL; + } + return ret; +} + +static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "\n"); + + if (f->index >= dev->num_formats) + return -EINVAL; + + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "\n"); + + f->fmt.sdr.pixelformat = dev->pixelformat; + f->fmt.sdr.buffersize = dev->buffersize; + + return 0; +} + +static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + struct vb2_queue *q = &dev->vb_queue; + int i; + + dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); + + if (vb2_is_busy(q)) + return -EBUSY; + + for (i = 0; i < dev->num_formats; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + dev->pixelformat = formats[i].pixelformat; + dev->buffersize = formats[i].buffersize; + f->fmt.sdr.buffersize = formats[i].buffersize; + return 0; + } + } + + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; + + return 0; +} + +static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + int i; + + dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); + + for (i = 0; i < dev->num_formats; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + f->fmt.sdr.buffersize = formats[i].buffersize; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; + + return 0; +} + +static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = { + .vidioc_querycap = rtl2832_sdr_querycap, + + .vidioc_enum_fmt_sdr_cap = rtl2832_sdr_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = rtl2832_sdr_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = rtl2832_sdr_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = rtl2832_sdr_try_fmt_sdr_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_tuner = rtl2832_sdr_g_tuner, + .vidioc_s_tuner = rtl2832_sdr_s_tuner, + + .vidioc_enum_freq_bands = rtl2832_sdr_enum_freq_bands, + .vidioc_g_frequency = rtl2832_sdr_g_frequency, + .vidioc_s_frequency = rtl2832_sdr_s_frequency, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations rtl2832_sdr_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static struct video_device rtl2832_sdr_template = { + .name = "Realtek RTL2832 SDR", + .release = video_device_release_empty, + .fops = &rtl2832_sdr_fops, + .ioctl_ops = &rtl2832_sdr_ioctl_ops, + .device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER, +}; + +static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rtl2832_sdr_dev *dev = + container_of(ctrl->handler, struct rtl2832_sdr_dev, + hdl); + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&pdev->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, + ctrl->step); + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + /* TODO: these controls should be moved to tuner drivers */ + if (dev->bandwidth_auto->val) { + /* Round towards the closest legal value */ + s32 val = dev->f_adc + div_u64(dev->bandwidth->step, 2); + u32 offset; + + val = clamp_t(s32, val, dev->bandwidth->minimum, + dev->bandwidth->maximum); + offset = val - dev->bandwidth->minimum; + offset = dev->bandwidth->step * + div_u64(offset, dev->bandwidth->step); + dev->bandwidth->val = dev->bandwidth->minimum + offset; + } + c->bandwidth_hz = dev->bandwidth->val; + + if (!test_bit(POWER_ON, &dev->flags)) + return 0; + + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + else + ret = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = { + .s_ctrl = rtl2832_sdr_s_ctrl, +}; + +static void rtl2832_sdr_video_release(struct v4l2_device *v) +{ + struct rtl2832_sdr_dev *dev = + container_of(v, struct rtl2832_sdr_dev, v4l2_dev); + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "\n"); + + v4l2_ctrl_handler_free(&dev->hdl); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); +} + +/* Platform driver interface */ +static int rtl2832_sdr_probe(struct platform_device *pdev) +{ + struct rtl2832_sdr_dev *dev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops; + struct v4l2_subdev *subdev; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + if (!pdata) { + dev_err(&pdev->dev, "Cannot proceed without platform data\n"); + ret = -EINVAL; + goto err; + } + if (!pdev->dev.parent->driver) { + dev_dbg(&pdev->dev, "No parent device\n"); + ret = -EINVAL; + goto err; + } + /* try to refcount host drv since we are the consumer */ + if (!try_module_get(pdev->dev.parent->driver->owner)) { + dev_err(&pdev->dev, "Refcount fail"); + ret = -EINVAL; + goto err; + } + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + goto err_module_put; + } + + /* setup the state */ + subdev = pdata->v4l2_subdev; + dev->v4l2_subdev = pdata->v4l2_subdev; + dev->pdev = pdev; + dev->regmap = pdata->regmap; + dev->udev = pdata->dvb_usb_device->udev; + dev->f_adc = bands_adc[0].rangelow; + dev->f_tuner = bands_fm[0].rangelow; + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; + dev->num_formats = NUM_FORMATS; + if (!rtl2832_sdr_emulated_fmt) + dev->num_formats -= 1; + + mutex_init(&dev->v4l2_lock); + mutex_init(&dev->vb_queue_lock); + spin_lock_init(&dev->queued_bufs_lock); + INIT_LIST_HEAD(&dev->queued_bufs); + + /* Init videobuf2 queue structure */ + dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + dev->vb_queue.drv_priv = dev; + dev->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf); + dev->vb_queue.ops = &rtl2832_sdr_vb2_ops; + dev->vb_queue.mem_ops = &vb2_vmalloc_memops; + dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&dev->vb_queue); + if (ret) { + dev_err(&pdev->dev, "Could not initialize vb2 queue\n"); + goto err_kfree; + } + + /* Register controls */ + switch (pdata->tuner) { + case RTL2832_SDR_TUNER_E4000: + v4l2_ctrl_handler_init(&dev->hdl, 9); + if (subdev) + v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, + NULL, true); + break; + case RTL2832_SDR_TUNER_R820T: + case RTL2832_SDR_TUNER_R828D: + v4l2_ctrl_handler_init(&dev->hdl, 2); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 0, 8000000, 100000, 0); + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); + break; + case RTL2832_SDR_TUNER_FC0012: + case RTL2832_SDR_TUNER_FC0013: + v4l2_ctrl_handler_init(&dev->hdl, 2); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 6000000, 8000000, 1000000, + 6000000); + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); + break; + case RTL2832_SDR_TUNER_FC2580: + v4l2_ctrl_handler_init(&dev->hdl, 2); + if (subdev) + v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, + NULL, true); + break; + default: + v4l2_ctrl_handler_init(&dev->hdl, 0); + dev_err(&pdev->dev, "Unsupported tuner\n"); + ret = -ENODEV; + goto err_v4l2_ctrl_handler_free; + } + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(&pdev->dev, "Could not initialize controls\n"); + goto err_v4l2_ctrl_handler_free; + } + + /* Init video_device structure */ + dev->vdev = rtl2832_sdr_template; + dev->vdev.queue = &dev->vb_queue; + dev->vdev.queue->lock = &dev->vb_queue_lock; + video_set_drvdata(&dev->vdev, dev); + + /* Register the v4l2_device structure */ + dev->v4l2_dev.release = rtl2832_sdr_video_release; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2-device %d\n", ret); + goto err_v4l2_ctrl_handler_free; + } + + dev->v4l2_dev.ctrl_handler = &dev->hdl; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.lock = &dev->v4l2_lock; + dev->vdev.vfl_dir = VFL_DIR_RX; + + ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(&pdev->dev, "Failed to register as video device %d\n", + ret); + goto err_v4l2_device_unregister; + } + dev_info(&pdev->dev, "Registered as %s\n", + video_device_node_name(&dev->vdev)); + dev_info(&pdev->dev, "Realtek RTL2832 SDR attached\n"); + dev_notice(&pdev->dev, + "SDR API is still slightly experimental and functionality changes may follow\n"); + platform_set_drvdata(pdev, dev); + return 0; +err_v4l2_device_unregister: + v4l2_device_unregister(&dev->v4l2_dev); +err_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&dev->hdl); +err_kfree: + kfree(dev); +err_module_put: + module_put(pdev->dev.parent->driver->owner); +err: + return ret; +} + +static void rtl2832_sdr_remove(struct platform_device *pdev) +{ + struct rtl2832_sdr_dev *dev = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "\n"); + + mutex_lock(&dev->vb_queue_lock); + mutex_lock(&dev->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + dev->udev = NULL; + v4l2_device_disconnect(&dev->v4l2_dev); + video_unregister_device(&dev->vdev); + mutex_unlock(&dev->v4l2_lock); + mutex_unlock(&dev->vb_queue_lock); + v4l2_device_put(&dev->v4l2_dev); + module_put(pdev->dev.parent->driver->owner); +} + +static struct platform_driver rtl2832_sdr_driver = { + .driver = { + .name = "rtl2832_sdr", + }, + .probe = rtl2832_sdr_probe, + .remove_new = rtl2832_sdr_remove, +}; +module_platform_driver(rtl2832_sdr_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Realtek RTL2832 SDR driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h new file mode 100644 index 0000000000..26ad709ccd --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832_sdr.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Realtek RTL2832U SDR driver + * + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> + * + * GNU Radio plugin "gr-kernel" for device usage will be on: + * http://git.linuxtv.org/anttip/gr-kernel.git + */ + +#ifndef RTL2832_SDR_H +#define RTL2832_SDR_H + +#include <linux/i2c.h> +#include <media/v4l2-subdev.h> +#include <media/dvb_frontend.h> + +/** + * struct rtl2832_sdr_platform_data - Platform data for the rtl2832_sdr driver + * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000). + * @tuner: Used tuner model. + * @regmap: pointer to &struct regmap. + * @dvb_frontend: rtl2832 DVB frontend. + * @v4l2_subdev: Tuner v4l2 controls. + * @dvb_usb_device: DVB USB interface for USB streaming. + */ +struct rtl2832_sdr_platform_data { + u32 clk; + /* + * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver. + */ +#define RTL2832_SDR_TUNER_FC2580 0x21 +#define RTL2832_SDR_TUNER_TUA9001 0x24 +#define RTL2832_SDR_TUNER_FC0012 0x26 +#define RTL2832_SDR_TUNER_E4000 0x27 +#define RTL2832_SDR_TUNER_FC0013 0x29 +#define RTL2832_SDR_TUNER_R820T 0x2a +#define RTL2832_SDR_TUNER_R828D 0x2b + u8 tuner; + + struct regmap *regmap; + struct dvb_frontend *dvb_frontend; + struct v4l2_subdev *v4l2_subdev; + struct dvb_usb_device *dvb_usb_device; +}; + +#endif /* RTL2832_SDR_H */ diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c new file mode 100644 index 0000000000..28b1dca077 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -0,0 +1,1011 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Samsung S5H1409 VSB/QAM demodulator driver + + Copyright (C) 2006 Steven Toth <stoth@linuxtv.org> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <media/dvb_frontend.h> +#include "s5h1409.h" + +struct s5h1409_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct s5h1409_config *config; + + struct dvb_frontend frontend; + + /* previous uncorrected block counter */ + enum fe_modulation current_modulation; + + u32 current_frequency; + int if_freq; + + u32 is_qam_locked; + + /* QAM tuning state goes through the following state transitions */ +#define QAM_STATE_UNTUNED 0 +#define QAM_STATE_TUNING_STARTED 1 +#define QAM_STATE_INTERLEAVE_SET 2 +#define QAM_STATE_QAM_OPTIMIZED_L1 3 +#define QAM_STATE_QAM_OPTIMIZED_L2 4 +#define QAM_STATE_QAM_OPTIMIZED_L3 5 + u8 qam_state; +}; + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +#define dprintk if (debug) printk + +/* Register values to initialise the demod, this will set VSB by default */ +static struct init_tab { + u8 reg; + u16 data; +} init_tab[] = { + { 0x00, 0x0071, }, + { 0x01, 0x3213, }, + { 0x09, 0x0025, }, + { 0x1c, 0x001d, }, + { 0x1f, 0x002d, }, + { 0x20, 0x001d, }, + { 0x22, 0x0022, }, + { 0x23, 0x0020, }, + { 0x29, 0x110f, }, + { 0x2a, 0x10b4, }, + { 0x2b, 0x10ae, }, + { 0x2c, 0x0031, }, + { 0x31, 0x010d, }, + { 0x32, 0x0100, }, + { 0x44, 0x0510, }, + { 0x54, 0x0104, }, + { 0x58, 0x2222, }, + { 0x59, 0x1162, }, + { 0x5a, 0x3211, }, + { 0x5d, 0x0370, }, + { 0x5e, 0x0296, }, + { 0x61, 0x0010, }, + { 0x63, 0x4a00, }, + { 0x65, 0x0800, }, + { 0x71, 0x0003, }, + { 0x72, 0x0470, }, + { 0x81, 0x0002, }, + { 0x82, 0x0600, }, + { 0x86, 0x0002, }, + { 0x8a, 0x2c38, }, + { 0x8b, 0x2a37, }, + { 0x92, 0x302f, }, + { 0x93, 0x3332, }, + { 0x96, 0x000c, }, + { 0x99, 0x0101, }, + { 0x9c, 0x2e37, }, + { 0x9d, 0x2c37, }, + { 0x9e, 0x2c37, }, + { 0xab, 0x0100, }, + { 0xac, 0x1003, }, + { 0xad, 0x103f, }, + { 0xe2, 0x0100, }, + { 0xe3, 0x1000, }, + { 0x28, 0x1010, }, + { 0xb1, 0x000e, }, +}; + +/* VSB SNR lookup table */ +static struct vsb_snr_tab { + u16 val; + u16 data; +} vsb_snr_tab[] = { + { 924, 300, }, + { 923, 300, }, + { 918, 295, }, + { 915, 290, }, + { 911, 285, }, + { 906, 280, }, + { 901, 275, }, + { 896, 270, }, + { 891, 265, }, + { 885, 260, }, + { 879, 255, }, + { 873, 250, }, + { 864, 245, }, + { 858, 240, }, + { 850, 235, }, + { 841, 230, }, + { 832, 225, }, + { 823, 220, }, + { 812, 215, }, + { 802, 210, }, + { 788, 205, }, + { 778, 200, }, + { 767, 195, }, + { 753, 190, }, + { 740, 185, }, + { 725, 180, }, + { 707, 175, }, + { 689, 170, }, + { 671, 165, }, + { 656, 160, }, + { 637, 155, }, + { 616, 150, }, + { 542, 145, }, + { 519, 140, }, + { 507, 135, }, + { 497, 130, }, + { 492, 125, }, + { 474, 120, }, + { 300, 111, }, + { 0, 0, }, +}; + +/* QAM64 SNR lookup table */ +static struct qam64_snr_tab { + u16 val; + u16 data; +} qam64_snr_tab[] = { + { 1, 0, }, + { 12, 300, }, + { 15, 290, }, + { 18, 280, }, + { 22, 270, }, + { 23, 268, }, + { 24, 266, }, + { 25, 264, }, + { 27, 262, }, + { 28, 260, }, + { 29, 258, }, + { 30, 256, }, + { 32, 254, }, + { 33, 252, }, + { 34, 250, }, + { 35, 249, }, + { 36, 248, }, + { 37, 247, }, + { 38, 246, }, + { 39, 245, }, + { 40, 244, }, + { 41, 243, }, + { 42, 241, }, + { 43, 240, }, + { 44, 239, }, + { 45, 238, }, + { 46, 237, }, + { 47, 236, }, + { 48, 235, }, + { 49, 234, }, + { 50, 233, }, + { 51, 232, }, + { 52, 231, }, + { 53, 230, }, + { 55, 229, }, + { 56, 228, }, + { 57, 227, }, + { 58, 226, }, + { 59, 225, }, + { 60, 224, }, + { 62, 223, }, + { 63, 222, }, + { 65, 221, }, + { 66, 220, }, + { 68, 219, }, + { 69, 218, }, + { 70, 217, }, + { 72, 216, }, + { 73, 215, }, + { 75, 214, }, + { 76, 213, }, + { 78, 212, }, + { 80, 211, }, + { 81, 210, }, + { 83, 209, }, + { 84, 208, }, + { 85, 207, }, + { 87, 206, }, + { 89, 205, }, + { 91, 204, }, + { 93, 203, }, + { 95, 202, }, + { 96, 201, }, + { 104, 200, }, + { 255, 0, }, +}; + +/* QAM256 SNR lookup table */ +static struct qam256_snr_tab { + u16 val; + u16 data; +} qam256_snr_tab[] = { + { 1, 0, }, + { 12, 400, }, + { 13, 390, }, + { 15, 380, }, + { 17, 360, }, + { 19, 350, }, + { 22, 348, }, + { 23, 346, }, + { 24, 344, }, + { 25, 342, }, + { 26, 340, }, + { 27, 336, }, + { 28, 334, }, + { 29, 332, }, + { 30, 330, }, + { 31, 328, }, + { 32, 326, }, + { 33, 325, }, + { 34, 322, }, + { 35, 320, }, + { 37, 318, }, + { 39, 316, }, + { 40, 314, }, + { 41, 312, }, + { 42, 310, }, + { 43, 308, }, + { 46, 306, }, + { 47, 304, }, + { 49, 302, }, + { 51, 300, }, + { 53, 298, }, + { 54, 297, }, + { 55, 296, }, + { 56, 295, }, + { 57, 294, }, + { 59, 293, }, + { 60, 292, }, + { 61, 291, }, + { 63, 290, }, + { 64, 289, }, + { 65, 288, }, + { 66, 287, }, + { 68, 286, }, + { 69, 285, }, + { 71, 284, }, + { 72, 283, }, + { 74, 282, }, + { 75, 281, }, + { 76, 280, }, + { 77, 279, }, + { 78, 278, }, + { 81, 277, }, + { 83, 276, }, + { 84, 275, }, + { 86, 274, }, + { 87, 273, }, + { 89, 272, }, + { 90, 271, }, + { 92, 270, }, + { 93, 269, }, + { 95, 268, }, + { 96, 267, }, + { 98, 266, }, + { 100, 265, }, + { 102, 264, }, + { 104, 263, }, + { 105, 262, }, + { 106, 261, }, + { 110, 260, }, + { 255, 0, }, +}; + +/* 8 bit registers, 16 bit values */ +static int s5h1409_writereg(struct s5h1409_state *state, u8 reg, u16 data) +{ + int ret; + u8 buf[] = { reg, data >> 8, data & 0xff }; + + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u16 s5h1409_readreg(struct s5h1409_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0, 0 }; + + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 2 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk("%s: readreg error (ret == %i)\n", __func__, ret); + return (b1[0] << 8) | b1[1]; +} + +static int s5h1409_softreset(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + s5h1409_writereg(state, 0xf5, 0); + s5h1409_writereg(state, 0xf5, 1); + state->is_qam_locked = 0; + state->qam_state = QAM_STATE_UNTUNED; + return 0; +} + +#define S5H1409_VSB_IF_FREQ 5380 +#define S5H1409_QAM_IF_FREQ (state->config->qam_if) + +static int s5h1409_set_if_freq(struct dvb_frontend *fe, int KHz) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(%d KHz)\n", __func__, KHz); + + switch (KHz) { + case 4000: + s5h1409_writereg(state, 0x87, 0x014b); + s5h1409_writereg(state, 0x88, 0x0cb5); + s5h1409_writereg(state, 0x89, 0x03e2); + break; + case 5380: + case 44000: + default: + s5h1409_writereg(state, 0x87, 0x01be); + s5h1409_writereg(state, 0x88, 0x0436); + s5h1409_writereg(state, 0x89, 0x054d); + break; + } + state->if_freq = KHz; + + return 0; +} + +static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, inverted); + + if (inverted == 1) + return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */ + else + return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */ +} + +static int s5h1409_enable_modulation(struct dvb_frontend *fe, + enum fe_modulation m) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(0x%08x)\n", __func__, m); + + switch (m) { + case VSB_8: + dprintk("%s() VSB_8\n", __func__); + if (state->if_freq != S5H1409_VSB_IF_FREQ) + s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ); + s5h1409_writereg(state, 0xf4, 0); + break; + case QAM_64: + case QAM_256: + case QAM_AUTO: + dprintk("%s() QAM_AUTO (64/256)\n", __func__); + if (state->if_freq != S5H1409_QAM_IF_FREQ) + s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ); + s5h1409_writereg(state, 0xf4, 1); + s5h1409_writereg(state, 0x85, 0x110); + break; + default: + dprintk("%s() Invalid modulation\n", __func__); + return -EINVAL; + } + + state->current_modulation = m; + s5h1409_softreset(fe); + + return 0; +} + +static int s5h1409_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return s5h1409_writereg(state, 0xf3, 1); + else + return s5h1409_writereg(state, 0xf3, 0); +} + +static int s5h1409_set_gpio(struct dvb_frontend *fe, int enable) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return s5h1409_writereg(state, 0xe3, + s5h1409_readreg(state, 0xe3) | 0x1100); + else + return s5h1409_writereg(state, 0xe3, + s5h1409_readreg(state, 0xe3) & 0xfeff); +} + +static int s5h1409_sleep(struct dvb_frontend *fe, int enable) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + return s5h1409_writereg(state, 0xf2, enable); +} + +static int s5h1409_register_reset(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + return s5h1409_writereg(state, 0xfa, 0); +} + +static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg; + + if (state->qam_state < QAM_STATE_INTERLEAVE_SET) { + /* We should not perform amhum optimization until + the interleave mode has been configured */ + return; + } + + if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) { + /* We've already reached the maximum optimization level, so + don't bother banging on the status registers */ + return; + } + + /* QAM EQ lock check */ + reg = s5h1409_readreg(state, 0xf0); + + if ((reg >> 13) & 0x1) { + reg &= 0xff; + + s5h1409_writereg(state, 0x96, 0x000c); + if (reg < 0x68) { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) { + dprintk("%s() setting QAM state to OPT_L3\n", + __func__); + s5h1409_writereg(state, 0x93, 0x3130); + s5h1409_writereg(state, 0x9e, 0x2836); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3; + } + } else { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) { + dprintk("%s() setting QAM state to OPT_L2\n", + __func__); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2; + } + } + + } else { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) { + dprintk("%s() setting QAM state to OPT_L1\n", __func__); + s5h1409_writereg(state, 0x96, 0x0008); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1; + } + } +} + +static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg; + + if (state->is_qam_locked) + return; + + /* QAM EQ lock check */ + reg = s5h1409_readreg(state, 0xf0); + + if ((reg >> 13) & 0x1) { + + state->is_qam_locked = 1; + reg &= 0xff; + + s5h1409_writereg(state, 0x96, 0x00c); + if ((reg < 0x38) || (reg > 0x68)) { + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + } else { + s5h1409_writereg(state, 0x93, 0x3130); + s5h1409_writereg(state, 0x9e, 0x2836); + } + + } else { + s5h1409_writereg(state, 0x96, 0x0008); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + } +} + +static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg, reg1, reg2; + + if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) { + /* We've done the optimization already */ + return; + } + + reg = s5h1409_readreg(state, 0xf1); + + /* Master lock */ + if ((reg >> 15) & 0x1) { + if (state->qam_state == QAM_STATE_UNTUNED || + state->qam_state == QAM_STATE_TUNING_STARTED) { + dprintk("%s() setting QAM state to INTERLEAVE_SET\n", + __func__); + reg1 = s5h1409_readreg(state, 0xb2); + reg2 = s5h1409_readreg(state, 0xad); + + s5h1409_writereg(state, 0x96, 0x0020); + s5h1409_writereg(state, 0xad, + (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); + state->qam_state = QAM_STATE_INTERLEAVE_SET; + } + } else { + if (state->qam_state == QAM_STATE_UNTUNED) { + dprintk("%s() setting QAM state to TUNING_STARTED\n", + __func__); + s5h1409_writereg(state, 0x96, 0x08); + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) | 0x1001); + state->qam_state = QAM_STATE_TUNING_STARTED; + } + } +} + +static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg, reg1, reg2; + + reg = s5h1409_readreg(state, 0xf1); + + /* Master lock */ + if ((reg >> 15) & 0x1) { + if (state->qam_state != 2) { + state->qam_state = 2; + reg1 = s5h1409_readreg(state, 0xb2); + reg2 = s5h1409_readreg(state, 0xad); + + s5h1409_writereg(state, 0x96, 0x20); + s5h1409_writereg(state, 0xad, + (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) & 0xeffe); + } + } else { + if (state->qam_state != 1) { + state->qam_state = 1; + s5h1409_writereg(state, 0x96, 0x08); + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) | 0x1001); + } + } +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int s5h1409_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct s5h1409_state *state = fe->demodulator_priv; + + dprintk("%s(frequency=%d)\n", __func__, p->frequency); + + s5h1409_softreset(fe); + + state->current_frequency = p->frequency; + + s5h1409_enable_modulation(fe, p->modulation); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Issue a reset to the demod so it knows to resync against the + newly tuned frequency */ + s5h1409_softreset(fe); + + /* Optimize the demod for QAM */ + if (state->current_modulation != VSB_8) { + /* This almost certainly applies to all boards, but for now + only do it for the HVR-1600. Once the other boards are + tested, the "legacy" versions can just go away */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + s5h1409_set_qam_interleave_mode(fe); + s5h1409_set_qam_amhum_mode(fe); + } else { + s5h1409_set_qam_amhum_mode_legacy(fe); + s5h1409_set_qam_interleave_mode_legacy(fe); + } + } + + return 0; +} + +static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, mode); + + val = s5h1409_readreg(state, 0xac) & 0xcfff; + switch (mode) { + case S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK: + val |= 0x0000; + break; + case S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK: + dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); + val |= 0x1000; + break; + case S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK: + val |= 0x2000; + break; + case S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK: + val |= 0x3000; + break; + default: + return -EINVAL; + } + + /* Configure MPEG Signal Timing charactistics */ + return s5h1409_writereg(state, 0xac, val); +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int s5h1409_init(struct dvb_frontend *fe) +{ + int i; + + struct s5h1409_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + s5h1409_sleep(fe, 0); + s5h1409_register_reset(fe); + + for (i = 0; i < ARRAY_SIZE(init_tab); i++) + s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data); + + /* The datasheet says that after initialisation, VSB is default */ + state->current_modulation = VSB_8; + + /* Optimize for the HVR-1600 if appropriate. Note that some of these + may get folded into the generic case after testing with other + devices */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + /* VSB AGC REF */ + s5h1409_writereg(state, 0x09, 0x0050); + + /* Unknown but Windows driver does it... */ + s5h1409_writereg(state, 0x21, 0x0001); + s5h1409_writereg(state, 0x50, 0x030e); + + /* QAM AGC REF */ + s5h1409_writereg(state, 0x82, 0x0800); + } + + if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) | 0x100); /* Serial */ + else + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */ + + s5h1409_set_spectralinversion(fe, state->config->inversion); + s5h1409_set_if_freq(fe, state->if_freq); + s5h1409_set_gpio(fe, state->config->gpio); + s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing); + s5h1409_softreset(fe); + + /* Note: Leaving the I2C gate closed. */ + s5h1409_i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int s5h1409_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg; + u32 tuner_status = 0; + + *status = 0; + + /* Optimize the demod for QAM */ + if (state->current_modulation != VSB_8) { + /* This almost certainly applies to all boards, but for now + only do it for the HVR-1600. Once the other boards are + tested, the "legacy" versions can just go away */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + s5h1409_set_qam_interleave_mode(fe); + s5h1409_set_qam_amhum_mode(fe); + } + } + + /* Get the demodulator status */ + reg = s5h1409_readreg(state, 0xf1); + if (reg & 0x1000) + *status |= FE_HAS_VITERBI; + if (reg & 0x8000) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + + switch (state->config->status_mode) { + case S5H1409_DEMODLOCKING: + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case S5H1409_TUNERLOCKING: + /* Get the tuner status */ + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + + dprintk("%s() status 0x%08x\n", __func__, *status); + + return 0; +} + +static int s5h1409_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { + if (v < qam256_snr_tab[i].val) { + *snr = qam256_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1409_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { + if (v < qam64_snr_tab[i].val) { + *snr = qam64_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1409_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { + if (v > vsb_snr_tab[i].val) { + *snr = vsb_snr_tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __func__, *snr); + return ret; +} + +static int s5h1409_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg; + dprintk("%s()\n", __func__); + + switch (state->current_modulation) { + case QAM_64: + reg = s5h1409_readreg(state, 0xf0) & 0xff; + return s5h1409_qam64_lookup_snr(fe, snr, reg); + case QAM_256: + reg = s5h1409_readreg(state, 0xf0) & 0xff; + return s5h1409_qam256_lookup_snr(fe, snr, reg); + case VSB_8: + reg = s5h1409_readreg(state, 0xf1) & 0x3ff; + return s5h1409_vsb_lookup_snr(fe, snr, reg); + default: + break; + } + + return -EINVAL; +} + +static int s5h1409_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + u16 snr; + u32 tmp; + int ret = s5h1409_read_snr(fe, &snr); + + *signal_strength = 0; + + if (0 == ret) { + /* The following calculation method was chosen + * purely for the sake of code re-use from the + * other demod drivers that use this method */ + + /* Convert from SNR in dB * 10 to 8.24 fixed-point */ + tmp = (snr * ((1 << 24) / 10)); + + /* Convert from 8.24 fixed-point to + * scale the range 0 - 35*2^24 into 0 - 65535*/ + if (tmp >= 8960 * 0x10000) + *signal_strength = 0xffff; + else + *signal_strength = tmp / 8960; + } + + return ret; +} + +static int s5h1409_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + *ucblocks = s5h1409_readreg(state, 0xb5); + + return 0; +} + +static int s5h1409_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return s5h1409_read_ucblocks(fe, ber); +} + +static int s5h1409_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct s5h1409_state *state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + p->modulation = state->current_modulation; + + return 0; +} + +static int s5h1409_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void s5h1409_release(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops s5h1409_ops; + +struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, + struct i2c_adapter *i2c) +{ + struct s5h1409_state *state = NULL; + u16 reg; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->current_modulation = 0; + state->if_freq = S5H1409_VSB_IF_FREQ; + + /* check if the demod exists */ + reg = s5h1409_readreg(state, 0x04); + if ((reg != 0x0066) && (reg != 0x007f)) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s5h1409_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + if (s5h1409_init(&state->frontend) != 0) { + printk(KERN_ERR "%s: Failed to initialize correctly\n", + __func__); + goto error; + } + + /* Note: Leaving the I2C gate open here. */ + s5h1409_i2c_gate_ctrl(&state->frontend, 1); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(s5h1409_attach); + +static const struct dvb_frontend_ops s5h1409_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Samsung S5H1409 QAM/8VSB Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = s5h1409_init, + .i2c_gate_ctrl = s5h1409_i2c_gate_ctrl, + .set_frontend = s5h1409_set_frontend, + .get_frontend = s5h1409_get_frontend, + .get_tune_settings = s5h1409_get_tune_settings, + .read_status = s5h1409_read_status, + .read_ber = s5h1409_read_ber, + .read_signal_strength = s5h1409_read_signal_strength, + .read_snr = s5h1409_read_snr, + .read_ucblocks = s5h1409_read_ucblocks, + .release = s5h1409_release, +}; + +MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h new file mode 100644 index 0000000000..f6063e4390 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1409.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Samsung S5H1409 VSB/QAM demodulator driver + + Copyright (C) 2006 Steven Toth <stoth@linuxtv.org> + + +*/ + +#ifndef __S5H1409_H__ +#define __S5H1409_H__ + +#include <linux/dvb/frontend.h> + +struct s5h1409_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* serial/parallel output */ +#define S5H1409_PARALLEL_OUTPUT 0 +#define S5H1409_SERIAL_OUTPUT 1 + u8 output_mode; + + /* GPIO Setting */ +#define S5H1409_GPIO_OFF 0 +#define S5H1409_GPIO_ON 1 + u8 gpio; + + /* IF Freq for QAM in KHz, VSB is hardcoded to 5380 */ + u16 qam_if; + + /* Spectral Inversion */ +#define S5H1409_INVERSION_OFF 0 +#define S5H1409_INVERSION_ON 1 + u8 inversion; + + /* Return lock status based on tuner lock, or demod lock */ +#define S5H1409_TUNERLOCKING 0 +#define S5H1409_DEMODLOCKING 1 + u8 status_mode; + + /* MPEG signal timing */ +#define S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0 +#define S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1 +#define S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2 +#define S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3 + u16 mpeg_timing; + + /* HVR-1600 optimizations (to better work with MXL5005s) + Note: some of these are likely to be folded into the generic driver + after being regression tested with other boards */ +#define S5H1409_HVR1600_NOOPTIMIZE 0 +#define S5H1409_HVR1600_OPTIMIZE 1 + u8 hvr1600_opt; +}; + +#if IS_REACHABLE(CONFIG_DVB_S5H1409) +extern struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *s5h1409_attach( + const struct s5h1409_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_S5H1409 */ + +#endif /* __S5H1409_H__ */ diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c new file mode 100644 index 0000000000..fc48e659c2 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Samsung S5H1411 VSB/QAM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <media/dvb_frontend.h> +#include "s5h1411.h" + +struct s5h1411_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct s5h1411_config *config; + + struct dvb_frontend frontend; + + enum fe_modulation current_modulation; + unsigned int first_tune:1; + + u32 current_frequency; + int if_freq; + + u8 inversion; +}; + +static int debug; + +#define dprintk(arg...) do { \ + if (debug) \ + printk(arg); \ +} while (0) + +/* Register values to initialise the demod, defaults to VSB */ +static struct init_tab { + u8 addr; + u8 reg; + u16 data; +} init_tab[] = { + { S5H1411_I2C_TOP_ADDR, 0x00, 0x0071, }, + { S5H1411_I2C_TOP_ADDR, 0x08, 0x0047, }, + { S5H1411_I2C_TOP_ADDR, 0x1c, 0x0400, }, + { S5H1411_I2C_TOP_ADDR, 0x1e, 0x0370, }, + { S5H1411_I2C_TOP_ADDR, 0x1f, 0x342c, }, + { S5H1411_I2C_TOP_ADDR, 0x24, 0x0231, }, + { S5H1411_I2C_TOP_ADDR, 0x25, 0x1011, }, + { S5H1411_I2C_TOP_ADDR, 0x26, 0x0f07, }, + { S5H1411_I2C_TOP_ADDR, 0x27, 0x0f04, }, + { S5H1411_I2C_TOP_ADDR, 0x28, 0x070f, }, + { S5H1411_I2C_TOP_ADDR, 0x29, 0x2820, }, + { S5H1411_I2C_TOP_ADDR, 0x2a, 0x102e, }, + { S5H1411_I2C_TOP_ADDR, 0x2b, 0x0220, }, + { S5H1411_I2C_TOP_ADDR, 0x2e, 0x0d0e, }, + { S5H1411_I2C_TOP_ADDR, 0x2f, 0x1013, }, + { S5H1411_I2C_TOP_ADDR, 0x31, 0x171b, }, + { S5H1411_I2C_TOP_ADDR, 0x32, 0x0e0f, }, + { S5H1411_I2C_TOP_ADDR, 0x33, 0x0f10, }, + { S5H1411_I2C_TOP_ADDR, 0x34, 0x170e, }, + { S5H1411_I2C_TOP_ADDR, 0x35, 0x4b10, }, + { S5H1411_I2C_TOP_ADDR, 0x36, 0x0f17, }, + { S5H1411_I2C_TOP_ADDR, 0x3c, 0x1577, }, + { S5H1411_I2C_TOP_ADDR, 0x3d, 0x081a, }, + { S5H1411_I2C_TOP_ADDR, 0x3e, 0x77ee, }, + { S5H1411_I2C_TOP_ADDR, 0x40, 0x1e09, }, + { S5H1411_I2C_TOP_ADDR, 0x41, 0x0f0c, }, + { S5H1411_I2C_TOP_ADDR, 0x42, 0x1f10, }, + { S5H1411_I2C_TOP_ADDR, 0x4d, 0x0509, }, + { S5H1411_I2C_TOP_ADDR, 0x4e, 0x0a00, }, + { S5H1411_I2C_TOP_ADDR, 0x50, 0x0000, }, + { S5H1411_I2C_TOP_ADDR, 0x5b, 0x0000, }, + { S5H1411_I2C_TOP_ADDR, 0x5c, 0x0008, }, + { S5H1411_I2C_TOP_ADDR, 0x57, 0x1101, }, + { S5H1411_I2C_TOP_ADDR, 0x65, 0x007c, }, + { S5H1411_I2C_TOP_ADDR, 0x68, 0x0512, }, + { S5H1411_I2C_TOP_ADDR, 0x69, 0x0258, }, + { S5H1411_I2C_TOP_ADDR, 0x70, 0x0004, }, + { S5H1411_I2C_TOP_ADDR, 0x71, 0x0007, }, + { S5H1411_I2C_TOP_ADDR, 0x76, 0x00a9, }, + { S5H1411_I2C_TOP_ADDR, 0x78, 0x3141, }, + { S5H1411_I2C_TOP_ADDR, 0x7a, 0x3141, }, + { S5H1411_I2C_TOP_ADDR, 0xb3, 0x8003, }, + { S5H1411_I2C_TOP_ADDR, 0xb5, 0xa6bb, }, + { S5H1411_I2C_TOP_ADDR, 0xb6, 0x0609, }, + { S5H1411_I2C_TOP_ADDR, 0xb7, 0x2f06, }, + { S5H1411_I2C_TOP_ADDR, 0xb8, 0x003f, }, + { S5H1411_I2C_TOP_ADDR, 0xb9, 0x2700, }, + { S5H1411_I2C_TOP_ADDR, 0xba, 0xfac8, }, + { S5H1411_I2C_TOP_ADDR, 0xbe, 0x1003, }, + { S5H1411_I2C_TOP_ADDR, 0xbf, 0x103f, }, + { S5H1411_I2C_TOP_ADDR, 0xce, 0x2000, }, + { S5H1411_I2C_TOP_ADDR, 0xcf, 0x0800, }, + { S5H1411_I2C_TOP_ADDR, 0xd0, 0x0800, }, + { S5H1411_I2C_TOP_ADDR, 0xd1, 0x0400, }, + { S5H1411_I2C_TOP_ADDR, 0xd2, 0x0800, }, + { S5H1411_I2C_TOP_ADDR, 0xd3, 0x2000, }, + { S5H1411_I2C_TOP_ADDR, 0xd4, 0x3000, }, + { S5H1411_I2C_TOP_ADDR, 0xdb, 0x4a9b, }, + { S5H1411_I2C_TOP_ADDR, 0xdc, 0x1000, }, + { S5H1411_I2C_TOP_ADDR, 0xde, 0x0001, }, + { S5H1411_I2C_TOP_ADDR, 0xdf, 0x0000, }, + { S5H1411_I2C_TOP_ADDR, 0xe3, 0x0301, }, + { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0000, }, + { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0001, }, + { S5H1411_I2C_QAM_ADDR, 0x08, 0x0600, }, + { S5H1411_I2C_QAM_ADDR, 0x18, 0x4201, }, + { S5H1411_I2C_QAM_ADDR, 0x1e, 0x6476, }, + { S5H1411_I2C_QAM_ADDR, 0x21, 0x0830, }, + { S5H1411_I2C_QAM_ADDR, 0x0c, 0x5679, }, + { S5H1411_I2C_QAM_ADDR, 0x0d, 0x579b, }, + { S5H1411_I2C_QAM_ADDR, 0x24, 0x0102, }, + { S5H1411_I2C_QAM_ADDR, 0x31, 0x7488, }, + { S5H1411_I2C_QAM_ADDR, 0x32, 0x0a08, }, + { S5H1411_I2C_QAM_ADDR, 0x3d, 0x8689, }, + { S5H1411_I2C_QAM_ADDR, 0x49, 0x0048, }, + { S5H1411_I2C_QAM_ADDR, 0x57, 0x2012, }, + { S5H1411_I2C_QAM_ADDR, 0x5d, 0x7676, }, + { S5H1411_I2C_QAM_ADDR, 0x04, 0x0400, }, + { S5H1411_I2C_QAM_ADDR, 0x58, 0x00c0, }, + { S5H1411_I2C_QAM_ADDR, 0x5b, 0x0100, }, +}; + +/* VSB SNR lookup table */ +static struct vsb_snr_tab { + u16 val; + u16 data; +} vsb_snr_tab[] = { + { 0x39f, 300, }, + { 0x39b, 295, }, + { 0x397, 290, }, + { 0x394, 285, }, + { 0x38f, 280, }, + { 0x38b, 275, }, + { 0x387, 270, }, + { 0x382, 265, }, + { 0x37d, 260, }, + { 0x377, 255, }, + { 0x370, 250, }, + { 0x36a, 245, }, + { 0x364, 240, }, + { 0x35b, 235, }, + { 0x353, 230, }, + { 0x349, 225, }, + { 0x340, 220, }, + { 0x337, 215, }, + { 0x327, 210, }, + { 0x31b, 205, }, + { 0x310, 200, }, + { 0x302, 195, }, + { 0x2f3, 190, }, + { 0x2e4, 185, }, + { 0x2d7, 180, }, + { 0x2cd, 175, }, + { 0x2bb, 170, }, + { 0x2a9, 165, }, + { 0x29e, 160, }, + { 0x284, 155, }, + { 0x27a, 150, }, + { 0x260, 145, }, + { 0x23a, 140, }, + { 0x224, 135, }, + { 0x213, 130, }, + { 0x204, 125, }, + { 0x1fe, 120, }, + { 0, 0, }, +}; + +/* QAM64 SNR lookup table */ +static struct qam64_snr_tab { + u16 val; + u16 data; +} qam64_snr_tab[] = { + { 0x0001, 0, }, + { 0x0af0, 300, }, + { 0x0d80, 290, }, + { 0x10a0, 280, }, + { 0x14b5, 270, }, + { 0x1590, 268, }, + { 0x1680, 266, }, + { 0x17b0, 264, }, + { 0x18c0, 262, }, + { 0x19b0, 260, }, + { 0x1ad0, 258, }, + { 0x1d00, 256, }, + { 0x1da0, 254, }, + { 0x1ef0, 252, }, + { 0x2050, 250, }, + { 0x20f0, 249, }, + { 0x21d0, 248, }, + { 0x22b0, 247, }, + { 0x23a0, 246, }, + { 0x2470, 245, }, + { 0x24f0, 244, }, + { 0x25a0, 243, }, + { 0x26c0, 242, }, + { 0x27b0, 241, }, + { 0x28d0, 240, }, + { 0x29b0, 239, }, + { 0x2ad0, 238, }, + { 0x2ba0, 237, }, + { 0x2c80, 236, }, + { 0x2d20, 235, }, + { 0x2e00, 234, }, + { 0x2f10, 233, }, + { 0x3050, 232, }, + { 0x3190, 231, }, + { 0x3300, 230, }, + { 0x3340, 229, }, + { 0x3200, 228, }, + { 0x3550, 227, }, + { 0x3610, 226, }, + { 0x3600, 225, }, + { 0x3700, 224, }, + { 0x3800, 223, }, + { 0x3920, 222, }, + { 0x3a20, 221, }, + { 0x3b30, 220, }, + { 0x3d00, 219, }, + { 0x3e00, 218, }, + { 0x4000, 217, }, + { 0x4100, 216, }, + { 0x4300, 215, }, + { 0x4400, 214, }, + { 0x4600, 213, }, + { 0x4700, 212, }, + { 0x4800, 211, }, + { 0x4a00, 210, }, + { 0x4b00, 209, }, + { 0x4d00, 208, }, + { 0x4f00, 207, }, + { 0x5050, 206, }, + { 0x5200, 205, }, + { 0x53c0, 204, }, + { 0x5450, 203, }, + { 0x5650, 202, }, + { 0x5820, 201, }, + { 0x6000, 200, }, + { 0xffff, 0, }, +}; + +/* QAM256 SNR lookup table */ +static struct qam256_snr_tab { + u16 val; + u16 data; +} qam256_snr_tab[] = { + { 0x0001, 0, }, + { 0x0970, 400, }, + { 0x0a90, 390, }, + { 0x0b90, 380, }, + { 0x0d90, 370, }, + { 0x0ff0, 360, }, + { 0x1240, 350, }, + { 0x1345, 348, }, + { 0x13c0, 346, }, + { 0x14c0, 344, }, + { 0x1500, 342, }, + { 0x1610, 340, }, + { 0x1700, 338, }, + { 0x1800, 336, }, + { 0x18b0, 334, }, + { 0x1900, 332, }, + { 0x1ab0, 330, }, + { 0x1bc0, 328, }, + { 0x1cb0, 326, }, + { 0x1db0, 324, }, + { 0x1eb0, 322, }, + { 0x2030, 320, }, + { 0x2200, 318, }, + { 0x2280, 316, }, + { 0x2410, 314, }, + { 0x25b0, 312, }, + { 0x27a0, 310, }, + { 0x2840, 308, }, + { 0x29d0, 306, }, + { 0x2b10, 304, }, + { 0x2d30, 302, }, + { 0x2f20, 300, }, + { 0x30c0, 298, }, + { 0x3260, 297, }, + { 0x32c0, 296, }, + { 0x3300, 295, }, + { 0x33b0, 294, }, + { 0x34b0, 293, }, + { 0x35a0, 292, }, + { 0x3650, 291, }, + { 0x3800, 290, }, + { 0x3900, 289, }, + { 0x3a50, 288, }, + { 0x3b30, 287, }, + { 0x3cb0, 286, }, + { 0x3e20, 285, }, + { 0x3fa0, 284, }, + { 0x40a0, 283, }, + { 0x41c0, 282, }, + { 0x42f0, 281, }, + { 0x44a0, 280, }, + { 0x4600, 279, }, + { 0x47b0, 278, }, + { 0x4900, 277, }, + { 0x4a00, 276, }, + { 0x4ba0, 275, }, + { 0x4d00, 274, }, + { 0x4f00, 273, }, + { 0x5000, 272, }, + { 0x51f0, 272, }, + { 0x53a0, 270, }, + { 0x5520, 269, }, + { 0x5700, 268, }, + { 0x5800, 267, }, + { 0x5a00, 266, }, + { 0x5c00, 265, }, + { 0x5d00, 264, }, + { 0x5f00, 263, }, + { 0x6000, 262, }, + { 0x6200, 261, }, + { 0x6400, 260, }, + { 0xffff, 0, }, +}; + +/* 8 bit registers, 16 bit values */ +static int s5h1411_writereg(struct s5h1411_state *state, + u8 addr, u8 reg, u16 data) +{ + int ret; + u8 buf[] = { reg, data >> 8, data & 0xff }; + + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, ret == %i)\n", + __func__, addr, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u16 s5h1411_readreg(struct s5h1411_state *state, u8 addr, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0, 0 }; + + struct i2c_msg msg[] = { + { .addr = addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return (b1[0] << 8) | b1[1]; +} + +static int s5h1411_softreset(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 0); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 1); + return 0; +} + +static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(%d KHz)\n", __func__, KHz); + + switch (KHz) { + case 3250: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x10d5); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x5342); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x10d9); + break; + case 3500: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1225); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x1e96); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1225); + break; + case 4000: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x14bc); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0xb53e); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x14bd); + break; + default: + dprintk("%s(%d KHz) Invalid, defaulting to 5380\n", + __func__, KHz); + fallthrough; + case 5380: + case 44000: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x3655); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1be4); + break; + } + + state->if_freq = KHz; + + return 0; +} + +static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, mode); + + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff; + switch (mode) { + case S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK: + val |= 0x0000; + break; + case S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK: + dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); + val |= 0x1000; + break; + case S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK: + val |= 0x2000; + break; + case S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK: + val |= 0x3000; + break; + default: + return -EINVAL; + } + + /* Configure MPEG Signal Timing charactistics */ + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbe, val); +} + +static int s5h1411_set_spectralinversion(struct dvb_frontend *fe, int inversion) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, inversion); + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x24) & ~0x1000; + + if (inversion == 1) + val |= 0x1000; /* Inverted */ + + state->inversion = inversion; + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x24, val); +} + +static int s5h1411_set_serialmode(struct dvb_frontend *fe, int serial) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, serial); + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbd) & ~0x100; + + if (serial == 1) + val |= 0x100; + + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbd, val); +} + +static int s5h1411_enable_modulation(struct dvb_frontend *fe, + enum fe_modulation m) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(0x%08x)\n", __func__, m); + + if ((state->first_tune == 0) && (m == state->current_modulation)) { + dprintk("%s() Already at desired modulation. Skipping...\n", + __func__); + return 0; + } + + switch (m) { + case VSB_8: + dprintk("%s() VSB_8\n", __func__); + s5h1411_set_if_freq(fe, state->config->vsb_if); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x71); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x00); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0xf1); + break; + case QAM_64: + case QAM_256: + case QAM_AUTO: + dprintk("%s() QAM_AUTO (64/256)\n", __func__); + s5h1411_set_if_freq(fe, state->config->qam_if); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x0171); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x0001); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x16, 0x1101); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0x00f0); + break; + default: + dprintk("%s() Invalid modulation\n", __func__); + return -EINVAL; + } + + state->current_modulation = m; + state->first_tune = 0; + s5h1411_softreset(fe); + + return 0; +} + +static int s5h1411_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); + else + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 0); +} + +static int s5h1411_set_gpio(struct dvb_frontend *fe, int enable) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, enable); + + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xe0) & ~0x02; + + if (enable) + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, + val | 0x02); + else + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, val); +} + +static int s5h1411_set_powerstate(struct dvb_frontend *fe, int enable) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 1); + else { + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 0); + s5h1411_softreset(fe); + } + + return 0; +} + +static int s5h1411_sleep(struct dvb_frontend *fe) +{ + return s5h1411_set_powerstate(fe, 1); +} + +static int s5h1411_register_reset(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf3, 0); +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int s5h1411_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(frequency=%d)\n", __func__, p->frequency); + + s5h1411_softreset(fe); + + state->current_frequency = p->frequency; + + s5h1411_enable_modulation(fe, p->modulation); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.set_params(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Issue a reset to the demod so it knows to resync against the + newly tuned frequency */ + s5h1411_softreset(fe); + + return 0; +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int s5h1411_init(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + int i; + + dprintk("%s()\n", __func__); + + s5h1411_set_powerstate(fe, 0); + s5h1411_register_reset(fe); + + for (i = 0; i < ARRAY_SIZE(init_tab); i++) + s5h1411_writereg(state, init_tab[i].addr, + init_tab[i].reg, + init_tab[i].data); + + /* The datasheet says that after initialisation, VSB is default */ + state->current_modulation = VSB_8; + + /* Although the datasheet says it's in VSB, empirical evidence + shows problems getting lock on the first tuning request. Make + sure we call enable_modulation the first time around */ + state->first_tune = 1; + + if (state->config->output_mode == S5H1411_SERIAL_OUTPUT) + /* Serial */ + s5h1411_set_serialmode(fe, 1); + else + /* Parallel */ + s5h1411_set_serialmode(fe, 0); + + s5h1411_set_spectralinversion(fe, state->config->inversion); + s5h1411_set_if_freq(fe, state->config->vsb_if); + s5h1411_set_gpio(fe, state->config->gpio); + s5h1411_set_mpeg_timing(fe, state->config->mpeg_timing); + s5h1411_softreset(fe); + + /* Note: Leaving the I2C gate closed. */ + s5h1411_i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int s5h1411_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 reg; + u32 tuner_status = 0; + + *status = 0; + + /* Register F2 bit 15 = Master Lock, removed */ + + switch (state->current_modulation) { + case QAM_64: + case QAM_256: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf0); + if (reg & 0x10) /* QAM FEC Lock */ + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + if (reg & 0x100) /* QAM EQ Lock */ + *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; + + break; + case VSB_8: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf2); + if (reg & 0x1000) /* FEC Lock */ + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + if (reg & 0x2000) /* EQ Lock */ + *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; + + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x53); + if (reg & 0x1) /* AFC Lock */ + *status |= FE_HAS_SIGNAL; + + break; + default: + return -EINVAL; + } + + switch (state->config->status_mode) { + case S5H1411_DEMODLOCKING: + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case S5H1411_TUNERLOCKING: + /* Get the tuner status */ + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + + dprintk("%s() status 0x%08x\n", __func__, *status); + + return 0; +} + +static int s5h1411_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { + if (v < qam256_snr_tab[i].val) { + *snr = qam256_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1411_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { + if (v < qam64_snr_tab[i].val) { + *snr = qam64_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1411_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { + if (v > vsb_snr_tab[i].val) { + *snr = vsb_snr_tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __func__, *snr); + return ret; +} + +static int s5h1411_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 reg; + dprintk("%s()\n", __func__); + + switch (state->current_modulation) { + case QAM_64: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); + return s5h1411_qam64_lookup_snr(fe, snr, reg); + case QAM_256: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); + return s5h1411_qam256_lookup_snr(fe, snr, reg); + case VSB_8: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, + 0xf2) & 0x3ff; + return s5h1411_vsb_lookup_snr(fe, snr, reg); + default: + break; + } + + return -EINVAL; +} + +static int s5h1411_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + u16 snr; + u32 tmp; + int ret = s5h1411_read_snr(fe, &snr); + + *signal_strength = 0; + + if (0 == ret) { + /* The following calculation method was chosen + * purely for the sake of code re-use from the + * other demod drivers that use this method */ + + /* Convert from SNR in dB * 10 to 8.24 fixed-point */ + tmp = (snr * ((1 << 24) / 10)); + + /* Convert from 8.24 fixed-point to + * scale the range 0 - 35*2^24 into 0 - 65535*/ + if (tmp >= 8960 * 0x10000) + *signal_strength = 0xffff; + else + *signal_strength = tmp / 8960; + } + + return ret; +} + +static int s5h1411_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + *ucblocks = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xc9); + + return 0; +} + +static int s5h1411_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return s5h1411_read_ucblocks(fe, ber); +} + +static int s5h1411_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + p->modulation = state->current_modulation; + + return 0; +} + +static int s5h1411_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void s5h1411_release(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops s5h1411_ops; + +struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, + struct i2c_adapter *i2c) +{ + struct s5h1411_state *state = NULL; + u16 reg; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->current_modulation = VSB_8; + state->inversion = state->config->inversion; + + /* check if the demod exists */ + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x05); + if (reg != 0x0066) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s5h1411_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + + if (s5h1411_init(&state->frontend) != 0) { + printk(KERN_ERR "%s: Failed to initialize correctly\n", + __func__); + goto error; + } + + /* Note: Leaving the I2C gate open here. */ + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); + + /* Put the device into low-power mode until first use */ + s5h1411_set_powerstate(&state->frontend, 1); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(s5h1411_attach); + +static const struct dvb_frontend_ops s5h1411_ops = { + .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, + .info = { + .name = "Samsung S5H1411 QAM/8VSB Frontend", + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = s5h1411_init, + .sleep = s5h1411_sleep, + .i2c_gate_ctrl = s5h1411_i2c_gate_ctrl, + .set_frontend = s5h1411_set_frontend, + .get_frontend = s5h1411_get_frontend, + .get_tune_settings = s5h1411_get_tune_settings, + .read_status = s5h1411_read_status, + .read_ber = s5h1411_read_ber, + .read_signal_strength = s5h1411_read_signal_strength, + .read_snr = s5h1411_read_snr, + .read_ucblocks = s5h1411_read_ucblocks, + .release = s5h1411_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h new file mode 100644 index 0000000000..939bac35c8 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1411.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Samsung S5H1411 VSB/QAM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> + + +*/ + +#ifndef __S5H1411_H__ +#define __S5H1411_H__ + +#include <linux/dvb/frontend.h> + +#define S5H1411_I2C_TOP_ADDR (0x32 >> 1) +#define S5H1411_I2C_QAM_ADDR (0x34 >> 1) + +struct s5h1411_config { + + /* serial/parallel output */ +#define S5H1411_PARALLEL_OUTPUT 0 +#define S5H1411_SERIAL_OUTPUT 1 + u8 output_mode; + + /* GPIO Setting */ +#define S5H1411_GPIO_OFF 0 +#define S5H1411_GPIO_ON 1 + u8 gpio; + + /* MPEG signal timing */ +#define S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0 +#define S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1 +#define S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2 +#define S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3 + u16 mpeg_timing; + + /* IF Freq for QAM and VSB in KHz */ +#define S5H1411_IF_3250 3250 +#define S5H1411_IF_3500 3500 +#define S5H1411_IF_4000 4000 +#define S5H1411_IF_5380 5380 +#define S5H1411_IF_44000 44000 +#define S5H1411_VSB_IF_DEFAULT S5H1411_IF_44000 +#define S5H1411_QAM_IF_DEFAULT S5H1411_IF_44000 + u16 qam_if; + u16 vsb_if; + + /* Spectral Inversion */ +#define S5H1411_INVERSION_OFF 0 +#define S5H1411_INVERSION_ON 1 + u8 inversion; + + /* Return lock status based on tuner lock, or demod lock */ +#define S5H1411_TUNERLOCKING 0 +#define S5H1411_DEMODLOCKING 1 + u8 status_mode; +}; + +#if IS_REACHABLE(CONFIG_DVB_S5H1411) +extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *s5h1411_attach( + const struct s5h1411_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_S5H1411 */ + +#endif /* __S5H1411_H__ */ diff --git a/drivers/media/dvb-frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c new file mode 100644 index 0000000000..d700de1ea6 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1420.c @@ -0,0 +1,964 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for + * Samsung S5H1420 and + * PnpNetwork PN1010 QPSK Demodulator + * + * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> + * Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <asm/div64.h> + +#include <linux/i2c.h> + + +#include <media/dvb_frontend.h> +#include "s5h1420.h" +#include "s5h1420_priv.h" + +#define TONE_FREQ 22000 + +struct s5h1420_state { + struct i2c_adapter* i2c; + const struct s5h1420_config* config; + + struct dvb_frontend frontend; + struct i2c_adapter tuner_i2c_adapter; + + u8 CON_1_val; + + u8 postlocked:1; + u32 fclk; + u32 tunedfreq; + enum fe_code_rate fec_inner; + u32 symbol_rate; + + /* FIXME: ugly workaround for flexcop's incapable i2c-controller + * it does not support repeated-start, workaround: write addr-1 + * and then read + */ + u8 shadow[256]; +}; + +static u32 s5h1420_getsymbolrate(struct s5h1420_state* state); +static int s5h1420_get_tune_settings(struct dvb_frontend* fe, + struct dvb_frontend_tune_settings* fesettings); + + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debugging"); + +#define dprintk(x...) do { \ + if (debug) \ + printk(KERN_DEBUG "S5H1420: " x); \ +} while (0) + +static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg) +{ + int ret; + u8 b[2]; + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 2 }, + { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = 1 }, + }; + + b[0] = (reg - 1) & 0xff; + b[1] = state->shadow[(reg - 1) & 0xff]; + + if (state->config->repeated_start_workaround) { + ret = i2c_transfer(state->i2c, msg, 3); + if (ret != 3) + return ret; + } else { + ret = i2c_transfer(state->i2c, &msg[1], 1); + if (ret != 1) + return ret; + ret = i2c_transfer(state->i2c, &msg[2], 1); + if (ret != 1) + return ret; + } + + /* dprintk("rd(%02x): %02x %02x\n", state->config->demod_address, reg, b[0]); */ + + return b[0]; +} + +static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int err; + + /* dprintk("wr(%02x): %02x %02x\n", state->config->demod_address, reg, data); */ + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + state->shadow[reg] = data; + + return 0; +} + +static int s5h1420_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + dprintk("enter %s\n", __func__); + + switch(voltage) { + case SEC_VOLTAGE_13: + s5h1420_writereg(state, 0x3c, + (s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02); + break; + + case SEC_VOLTAGE_18: + s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03); + break; + + case SEC_VOLTAGE_OFF: + s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd); + break; + } + + dprintk("leave %s\n", __func__); + return 0; +} + +static int s5h1420_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + dprintk("enter %s\n", __func__); + switch(tone) { + case SEC_TONE_ON: + s5h1420_writereg(state, 0x3b, + (s5h1420_readreg(state, 0x3b) & 0x74) | 0x08); + break; + + case SEC_TONE_OFF: + s5h1420_writereg(state, 0x3b, + (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01); + break; + } + dprintk("leave %s\n", __func__); + + return 0; +} + +static int s5h1420_send_master_cmd (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct s5h1420_state* state = fe->demodulator_priv; + u8 val; + int i; + unsigned long timeout; + int result = 0; + + dprintk("enter %s\n", __func__); + if (cmd->msg_len > sizeof(cmd->msg)) + return -EINVAL; + + /* setup for DISEQC */ + val = s5h1420_readreg(state, 0x3b); + s5h1420_writereg(state, 0x3b, 0x02); + msleep(15); + + /* write the DISEQC command bytes */ + for(i=0; i< cmd->msg_len; i++) { + s5h1420_writereg(state, 0x3d + i, cmd->msg[i]); + } + + /* kick off transmission */ + s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | + ((cmd->msg_len-1) << 4) | 0x08); + + /* wait for transmission to complete */ + timeout = jiffies + ((100*HZ) / 1000); + while(time_before(jiffies, timeout)) { + if (!(s5h1420_readreg(state, 0x3b) & 0x08)) + break; + + msleep(5); + } + if (time_after(jiffies, timeout)) + result = -ETIMEDOUT; + + /* restore original settings */ + s5h1420_writereg(state, 0x3b, val); + msleep(15); + dprintk("leave %s\n", __func__); + return result; +} + +static int s5h1420_recv_slave_reply (struct dvb_frontend* fe, + struct dvb_diseqc_slave_reply* reply) +{ + struct s5h1420_state* state = fe->demodulator_priv; + u8 val; + int i; + int length; + unsigned long timeout; + int result = 0; + + /* setup for DISEQC receive */ + val = s5h1420_readreg(state, 0x3b); + s5h1420_writereg(state, 0x3b, 0x82); /* FIXME: guess - do we need to set DIS_RDY(0x08) in receive mode? */ + msleep(15); + + /* wait for reception to complete */ + timeout = jiffies + ((reply->timeout*HZ) / 1000); + while(time_before(jiffies, timeout)) { + if (!(s5h1420_readreg(state, 0x3b) & 0x80)) /* FIXME: do we test DIS_RDY(0x08) or RCV_EN(0x80)? */ + break; + + msleep(5); + } + if (time_after(jiffies, timeout)) { + result = -ETIMEDOUT; + goto exit; + } + + /* check error flag - FIXME: not sure what this does - docs do not describe + * beyond "error flag for diseqc receive data :( */ + if (s5h1420_readreg(state, 0x49)) { + result = -EIO; + goto exit; + } + + /* check length */ + length = (s5h1420_readreg(state, 0x3b) & 0x70) >> 4; + if (length > sizeof(reply->msg)) { + result = -EOVERFLOW; + goto exit; + } + reply->msg_len = length; + + /* extract data */ + for(i=0; i< length; i++) { + reply->msg[i] = s5h1420_readreg(state, 0x3d + i); + } + +exit: + /* restore original settings */ + s5h1420_writereg(state, 0x3b, val); + msleep(15); + return result; +} + +static int s5h1420_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct s5h1420_state* state = fe->demodulator_priv; + u8 val; + int result = 0; + unsigned long timeout; + + /* setup for tone burst */ + val = s5h1420_readreg(state, 0x3b); + s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x70) | 0x01); + + /* set value for B position if requested */ + if (minicmd == SEC_MINI_B) { + s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x04); + } + msleep(15); + + /* start transmission */ + s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x08); + + /* wait for transmission to complete */ + timeout = jiffies + ((100*HZ) / 1000); + while(time_before(jiffies, timeout)) { + if (!(s5h1420_readreg(state, 0x3b) & 0x08)) + break; + + msleep(5); + } + if (time_after(jiffies, timeout)) + result = -ETIMEDOUT; + + /* restore original settings */ + s5h1420_writereg(state, 0x3b, val); + msleep(15); + return result; +} + +static enum fe_status s5h1420_get_status_bits(struct s5h1420_state *state) +{ + u8 val; + enum fe_status status = 0; + + val = s5h1420_readreg(state, 0x14); + if (val & 0x02) + status |= FE_HAS_SIGNAL; + if (val & 0x01) + status |= FE_HAS_CARRIER; + val = s5h1420_readreg(state, 0x36); + if (val & 0x01) + status |= FE_HAS_VITERBI; + if (val & 0x20) + status |= FE_HAS_SYNC; + if (status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC)) + status |= FE_HAS_LOCK; + + return status; +} + +static int s5h1420_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct s5h1420_state* state = fe->demodulator_priv; + u8 val; + + dprintk("enter %s\n", __func__); + + if (status == NULL) + return -EINVAL; + + /* determine lock state */ + *status = s5h1420_get_status_bits(state); + + /* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert + the inversion, wait a bit and check again */ + if (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI)) { + val = s5h1420_readreg(state, Vit10); + if ((val & 0x07) == 0x03) { + if (val & 0x08) + s5h1420_writereg(state, Vit09, 0x13); + else + s5h1420_writereg(state, Vit09, 0x1b); + + /* wait a bit then update lock status */ + mdelay(200); + *status = s5h1420_get_status_bits(state); + } + } + + /* perform post lock setup */ + if ((*status & FE_HAS_LOCK) && !state->postlocked) { + + /* calculate the data rate */ + u32 tmp = s5h1420_getsymbolrate(state); + switch (s5h1420_readreg(state, Vit10) & 0x07) { + case 0: tmp = (tmp * 2 * 1) / 2; break; + case 1: tmp = (tmp * 2 * 2) / 3; break; + case 2: tmp = (tmp * 2 * 3) / 4; break; + case 3: tmp = (tmp * 2 * 5) / 6; break; + case 4: tmp = (tmp * 2 * 6) / 7; break; + case 5: tmp = (tmp * 2 * 7) / 8; break; + } + + if (tmp == 0) { + printk(KERN_ERR "s5h1420: avoided division by 0\n"); + tmp = 1; + } + tmp = state->fclk / tmp; + + + /* set the MPEG_CLK_INTL for the calculated data rate */ + if (tmp < 2) + val = 0x00; + else if (tmp < 5) + val = 0x01; + else if (tmp < 9) + val = 0x02; + else if (tmp < 13) + val = 0x03; + else if (tmp < 17) + val = 0x04; + else if (tmp < 25) + val = 0x05; + else if (tmp < 33) + val = 0x06; + else + val = 0x07; + dprintk("for MPEG_CLK_INTL %d %x\n", tmp, val); + + s5h1420_writereg(state, FEC01, 0x18); + s5h1420_writereg(state, FEC01, 0x10); + s5h1420_writereg(state, FEC01, val); + + /* Enable "MPEG_Out" */ + val = s5h1420_readreg(state, Mpeg02); + s5h1420_writereg(state, Mpeg02, val | (1 << 6)); + + /* kicker disable */ + val = s5h1420_readreg(state, QPSK01) & 0x7f; + s5h1420_writereg(state, QPSK01, val); + + /* DC freeze TODO it was never activated by default or it can stay activated */ + + if (s5h1420_getsymbolrate(state) >= 20000000) { + s5h1420_writereg(state, Loop04, 0x8a); + s5h1420_writereg(state, Loop05, 0x6a); + } else { + s5h1420_writereg(state, Loop04, 0x58); + s5h1420_writereg(state, Loop05, 0x27); + } + + /* post-lock processing has been done! */ + state->postlocked = 1; + } + + dprintk("leave %s\n", __func__); + + return 0; +} + +static int s5h1420_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + s5h1420_writereg(state, 0x46, 0x1d); + mdelay(25); + + *ber = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47); + + return 0; +} + +static int s5h1420_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + u8 val = s5h1420_readreg(state, 0x15); + + *strength = (u16) ((val << 8) | val); + + return 0; +} + +static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + s5h1420_writereg(state, 0x46, 0x1f); + mdelay(25); + + *ucblocks = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47); + + return 0; +} + +static void s5h1420_reset(struct s5h1420_state* state) +{ + dprintk("%s\n", __func__); + s5h1420_writereg (state, 0x01, 0x08); + s5h1420_writereg (state, 0x01, 0x00); + udelay(10); +} + +static void s5h1420_setsymbolrate(struct s5h1420_state* state, + struct dtv_frontend_properties *p) +{ + u8 v; + u64 val; + + dprintk("enter %s\n", __func__); + + val = ((u64) p->symbol_rate / 1000ULL) * (1ULL<<24); + if (p->symbol_rate < 29000000) + val *= 2; + do_div(val, (state->fclk / 1000)); + + dprintk("symbol rate register: %06llx\n", (unsigned long long)val); + + v = s5h1420_readreg(state, Loop01); + s5h1420_writereg(state, Loop01, v & 0x7f); + s5h1420_writereg(state, Tnco01, val >> 16); + s5h1420_writereg(state, Tnco02, val >> 8); + s5h1420_writereg(state, Tnco03, val & 0xff); + s5h1420_writereg(state, Loop01, v | 0x80); + dprintk("leave %s\n", __func__); +} + +static u32 s5h1420_getsymbolrate(struct s5h1420_state* state) +{ + return state->symbol_rate; +} + +static void s5h1420_setfreqoffset(struct s5h1420_state* state, int freqoffset) +{ + int val; + u8 v; + + dprintk("enter %s\n", __func__); + + /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so + * divide fclk by 1000000 to get the correct value. */ + val = -(int) ((freqoffset * (1<<24)) / (state->fclk / 1000000)); + + dprintk("phase rotator/freqoffset: %d %06x\n", freqoffset, val); + + v = s5h1420_readreg(state, Loop01); + s5h1420_writereg(state, Loop01, v & 0xbf); + s5h1420_writereg(state, Pnco01, val >> 16); + s5h1420_writereg(state, Pnco02, val >> 8); + s5h1420_writereg(state, Pnco03, val & 0xff); + s5h1420_writereg(state, Loop01, v | 0x40); + dprintk("leave %s\n", __func__); +} + +static int s5h1420_getfreqoffset(struct s5h1420_state* state) +{ + int val; + + s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08); + val = s5h1420_readreg(state, 0x0e) << 16; + val |= s5h1420_readreg(state, 0x0f) << 8; + val |= s5h1420_readreg(state, 0x10); + s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7); + + if (val & 0x800000) + val |= 0xff000000; + + /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so + * divide fclk by 1000000 to get the correct value. */ + val = (((-val) * (state->fclk/1000000)) / (1<<24)); + + return val; +} + +static void s5h1420_setfec_inversion(struct s5h1420_state* state, + struct dtv_frontend_properties *p) +{ + u8 inversion = 0; + u8 vit08, vit09; + + dprintk("enter %s\n", __func__); + + if (p->inversion == INVERSION_OFF) + inversion = state->config->invert ? 0x08 : 0; + else if (p->inversion == INVERSION_ON) + inversion = state->config->invert ? 0 : 0x08; + + if ((p->fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) { + vit08 = 0x3f; + vit09 = 0; + } else { + switch (p->fec_inner) { + case FEC_1_2: + vit08 = 0x01; + vit09 = 0x10; + break; + + case FEC_2_3: + vit08 = 0x02; + vit09 = 0x11; + break; + + case FEC_3_4: + vit08 = 0x04; + vit09 = 0x12; + break; + + case FEC_5_6: + vit08 = 0x08; + vit09 = 0x13; + break; + + case FEC_6_7: + vit08 = 0x10; + vit09 = 0x14; + break; + + case FEC_7_8: + vit08 = 0x20; + vit09 = 0x15; + break; + + default: + return; + } + } + vit09 |= inversion; + dprintk("fec: %02x %02x\n", vit08, vit09); + s5h1420_writereg(state, Vit08, vit08); + s5h1420_writereg(state, Vit09, vit09); + dprintk("leave %s\n", __func__); +} + +static enum fe_code_rate s5h1420_getfec(struct s5h1420_state *state) +{ + switch(s5h1420_readreg(state, 0x32) & 0x07) { + case 0: + return FEC_1_2; + + case 1: + return FEC_2_3; + + case 2: + return FEC_3_4; + + case 3: + return FEC_5_6; + + case 4: + return FEC_6_7; + + case 5: + return FEC_7_8; + } + + return FEC_NONE; +} + +static enum fe_spectral_inversion +s5h1420_getinversion(struct s5h1420_state *state) +{ + if (s5h1420_readreg(state, 0x32) & 0x08) + return INVERSION_ON; + + return INVERSION_OFF; +} + +static int s5h1420_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct s5h1420_state* state = fe->demodulator_priv; + int frequency_delta; + struct dvb_frontend_tune_settings fesettings; + + dprintk("enter %s\n", __func__); + + /* check if we should do a fast-tune */ + s5h1420_get_tune_settings(fe, &fesettings); + frequency_delta = p->frequency - state->tunedfreq; + if ((frequency_delta > -fesettings.max_drift) && + (frequency_delta < fesettings.max_drift) && + (frequency_delta != 0) && + (state->fec_inner == p->fec_inner) && + (state->symbol_rate == p->symbol_rate)) { + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + if (fe->ops.tuner_ops.get_frequency) { + u32 tmp; + fe->ops.tuner_ops.get_frequency(fe, &tmp); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + s5h1420_setfreqoffset(state, p->frequency - tmp); + } else { + s5h1420_setfreqoffset(state, 0); + } + dprintk("simple tune\n"); + return 0; + } + dprintk("tuning demod\n"); + + /* first of all, software reset */ + s5h1420_reset(state); + + /* set s5h1420 fclk PLL according to desired symbol rate */ + if (p->symbol_rate > 33000000) + state->fclk = 80000000; + else if (p->symbol_rate > 28500000) + state->fclk = 59000000; + else if (p->symbol_rate > 25000000) + state->fclk = 86000000; + else if (p->symbol_rate > 1900000) + state->fclk = 88000000; + else + state->fclk = 44000000; + + dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); + s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8); + s5h1420_writereg(state, PLL02, 0x40); + s5h1420_writereg(state, DiS01, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); + + /* TODO DC offset removal, config parameter ? */ + if (p->symbol_rate > 29000000) + s5h1420_writereg(state, QPSK01, 0xae | 0x10); + else + s5h1420_writereg(state, QPSK01, 0xac | 0x10); + + /* set misc registers */ + s5h1420_writereg(state, CON_1, 0x00); + s5h1420_writereg(state, QPSK02, 0x00); + s5h1420_writereg(state, Pre01, 0xb0); + + s5h1420_writereg(state, Loop01, 0xF0); + s5h1420_writereg(state, Loop02, 0x2a); /* e7 for s5h1420 */ + s5h1420_writereg(state, Loop03, 0x79); /* 78 for s5h1420 */ + if (p->symbol_rate > 20000000) + s5h1420_writereg(state, Loop04, 0x79); + else + s5h1420_writereg(state, Loop04, 0x58); + s5h1420_writereg(state, Loop05, 0x6b); + + if (p->symbol_rate >= 8000000) + s5h1420_writereg(state, Post01, (0 << 6) | 0x10); + else if (p->symbol_rate >= 4000000) + s5h1420_writereg(state, Post01, (1 << 6) | 0x10); + else + s5h1420_writereg(state, Post01, (3 << 6) | 0x10); + + s5h1420_writereg(state, Monitor12, 0x00); /* unfreeze DC compensation */ + + s5h1420_writereg(state, Sync01, 0x33); + s5h1420_writereg(state, Mpeg01, state->config->cdclk_polarity); + s5h1420_writereg(state, Mpeg02, 0x3d); /* Parallel output more, disabled -> enabled later */ + s5h1420_writereg(state, Err01, 0x03); /* 0x1d for s5h1420 */ + + s5h1420_writereg(state, Vit06, 0x6e); /* 0x8e for s5h1420 */ + s5h1420_writereg(state, DiS03, 0x00); + s5h1420_writereg(state, Rf01, 0x61); /* Tuner i2c address - for the gate controller */ + + /* set tuner PLL */ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + s5h1420_setfreqoffset(state, 0); + } + + /* set the reset of the parameters */ + s5h1420_setsymbolrate(state, p); + s5h1420_setfec_inversion(state, p); + + /* start QPSK */ + s5h1420_writereg(state, QPSK01, s5h1420_readreg(state, QPSK01) | 1); + + state->fec_inner = p->fec_inner; + state->symbol_rate = p->symbol_rate; + state->postlocked = 0; + state->tunedfreq = p->frequency; + + dprintk("leave %s\n", __func__); + return 0; +} + +static int s5h1420_get_frontend(struct dvb_frontend* fe, + struct dtv_frontend_properties *p) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state); + p->inversion = s5h1420_getinversion(state); + p->symbol_rate = s5h1420_getsymbolrate(state); + p->fec_inner = s5h1420_getfec(state); + + return 0; +} + +static int s5h1420_get_tune_settings(struct dvb_frontend* fe, + struct dvb_frontend_tune_settings* fesettings) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + if (p->symbol_rate > 20000000) { + fesettings->min_delay_ms = 50; + fesettings->step_size = 2000; + fesettings->max_drift = 8000; + } else if (p->symbol_rate > 12000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 1500; + fesettings->max_drift = 9000; + } else if (p->symbol_rate > 8000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 1000; + fesettings->max_drift = 8000; + } else if (p->symbol_rate > 4000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 500; + fesettings->max_drift = 7000; + } else if (p->symbol_rate > 2000000) { + fesettings->min_delay_ms = 200; + fesettings->step_size = (p->symbol_rate / 8000); + fesettings->max_drift = 14 * fesettings->step_size; + } else { + fesettings->min_delay_ms = 200; + fesettings->step_size = (p->symbol_rate / 8000); + fesettings->max_drift = 18 * fesettings->step_size; + } + + return 0; +} + +static int s5h1420_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + if (enable) + return s5h1420_writereg(state, 0x02, state->CON_1_val | 1); + else + return s5h1420_writereg(state, 0x02, state->CON_1_val & 0xfe); +} + +static int s5h1420_init (struct dvb_frontend* fe) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + /* disable power down and do reset */ + state->CON_1_val = state->config->serial_mpeg << 4; + s5h1420_writereg(state, 0x02, state->CON_1_val); + msleep(10); + s5h1420_reset(state); + + return 0; +} + +static int s5h1420_sleep(struct dvb_frontend* fe) +{ + struct s5h1420_state* state = fe->demodulator_priv; + state->CON_1_val = 0x12; + return s5h1420_writereg(state, 0x02, state->CON_1_val); +} + +static void s5h1420_release(struct dvb_frontend* fe) +{ + struct s5h1420_state* state = fe->demodulator_priv; + i2c_del_adapter(&state->tuner_i2c_adapter); + kfree(state); +} + +static u32 s5h1420_tuner_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct s5h1420_state *state = i2c_get_adapdata(i2c_adap); + struct i2c_msg m[3]; + u8 tx_open[2] = { CON_1, state->CON_1_val | 1 }; /* repeater stops once there was a stop condition */ + + if (1 + num > ARRAY_SIZE(m)) { + printk(KERN_WARNING + "%s: i2c xfer: num=%d is too big!\n", + KBUILD_MODNAME, num); + return -EOPNOTSUPP; + } + + memset(m, 0, sizeof(struct i2c_msg) * (1 + num)); + + m[0].addr = state->config->demod_address; + m[0].buf = tx_open; + m[0].len = 2; + + memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); + + return i2c_transfer(state->i2c, m, 1 + num) == 1 + num ? num : -EIO; +} + +static const struct i2c_algorithm s5h1420_tuner_i2c_algo = { + .master_xfer = s5h1420_tuner_i2c_tuner_xfer, + .functionality = s5h1420_tuner_i2c_func, +}; + +struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + struct s5h1420_state *state = fe->demodulator_priv; + return &state->tuner_i2c_adapter; +} +EXPORT_SYMBOL(s5h1420_get_tuner_i2c_adapter); + +static const struct dvb_frontend_ops s5h1420_ops; + +struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, + struct i2c_adapter *i2c) +{ + /* allocate memory for the internal state */ + struct s5h1420_state *state = kzalloc(sizeof(struct s5h1420_state), GFP_KERNEL); + u8 i; + + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->postlocked = 0; + state->fclk = 88000000; + state->tunedfreq = 0; + state->fec_inner = FEC_NONE; + state->symbol_rate = 0; + + /* check if the demod is there + identify it */ + i = s5h1420_readreg(state, ID01); + if (i != 0x03) + goto error; + + memset(state->shadow, 0xff, sizeof(state->shadow)); + + for (i = 0; i < 0x50; i++) + state->shadow[i] = s5h1420_readreg(state, i); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s5h1420_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* create tuner i2c adapter */ + strscpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus", + sizeof(state->tuner_i2c_adapter.name)); + state->tuner_i2c_adapter.algo = &s5h1420_tuner_i2c_algo; + state->tuner_i2c_adapter.algo_data = NULL; + i2c_set_adapdata(&state->tuner_i2c_adapter, state); + if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { + printk(KERN_ERR "S5H1420/PN1010: tuner i2c bus could not be initialized\n"); + goto error; + } + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(s5h1420_attach); + +static const struct dvb_frontend_ops s5h1420_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .frequency_tolerance_hz = 29500 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = s5h1420_release, + + .init = s5h1420_init, + .sleep = s5h1420_sleep, + .i2c_gate_ctrl = s5h1420_i2c_gate_ctrl, + + .set_frontend = s5h1420_set_frontend, + .get_frontend = s5h1420_get_frontend, + .get_tune_settings = s5h1420_get_tune_settings, + + .read_status = s5h1420_read_status, + .read_ber = s5h1420_read_ber, + .read_signal_strength = s5h1420_read_signal_strength, + .read_ucblocks = s5h1420_read_ucblocks, + + .diseqc_send_master_cmd = s5h1420_send_master_cmd, + .diseqc_recv_slave_reply = s5h1420_recv_slave_reply, + .diseqc_send_burst = s5h1420_send_burst, + .set_tone = s5h1420_set_tone, + .set_voltage = s5h1420_set_voltage, +}; + +MODULE_DESCRIPTION("Samsung S5H1420/PnpNetwork PN1010 DVB-S Demodulator driver"); +MODULE_AUTHOR("Andrew de Quincey, Patrick Boettcher"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h new file mode 100644 index 0000000000..f5da808df0 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1420.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for + * Samsung S5H1420 and + * PnpNetwork PN1010 QPSK Demodulator + * + * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> + * Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org> + */ +#ifndef S5H1420_H +#define S5H1420_H + +#include <linux/dvb/frontend.h> + +struct s5h1420_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* does the inversion require inversion? */ + u8 invert:1; + + u8 repeated_start_workaround:1; + u8 cdclk_polarity:1; /* 1 == falling edge, 0 == raising edge */ + + u8 serial_mpeg:1; +}; + +#if IS_REACHABLE(CONFIG_DVB_S5H1420) +extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, + struct i2c_adapter *i2c); +extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe); +#else +static inline struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + return NULL; +} +#endif // CONFIG_DVB_S5H1420 + +#endif // S5H1420_H diff --git a/drivers/media/dvb-frontends/s5h1420_priv.h b/drivers/media/dvb-frontends/s5h1420_priv.h new file mode 100644 index 0000000000..1a699027a0 --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1420_priv.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for + * Samsung S5H1420 and + * PnpNetwork PN1010 QPSK Demodulator + * + * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> + * Copyright (C) 2005 Patrick Boettcher <pb@linuxtv.org> + */ +#ifndef S5H1420_PRIV +#define S5H1420_PRIV + +#include <asm/types.h> + +enum s5h1420_register { + ID01 = 0x00, + CON_0 = 0x01, + CON_1 = 0x02, + PLL01 = 0x03, + PLL02 = 0x04, + QPSK01 = 0x05, + QPSK02 = 0x06, + Pre01 = 0x07, + Post01 = 0x08, + Loop01 = 0x09, + Loop02 = 0x0a, + Loop03 = 0x0b, + Loop04 = 0x0c, + Loop05 = 0x0d, + Pnco01 = 0x0e, + Pnco02 = 0x0f, + Pnco03 = 0x10, + Tnco01 = 0x11, + Tnco02 = 0x12, + Tnco03 = 0x13, + Monitor01 = 0x14, + Monitor02 = 0x15, + Monitor03 = 0x16, + Monitor04 = 0x17, + Monitor05 = 0x18, + Monitor06 = 0x19, + Monitor07 = 0x1a, + Monitor12 = 0x1f, + + FEC01 = 0x22, + Soft01 = 0x23, + Soft02 = 0x24, + Soft03 = 0x25, + Soft04 = 0x26, + Soft05 = 0x27, + Soft06 = 0x28, + Vit01 = 0x29, + Vit02 = 0x2a, + Vit03 = 0x2b, + Vit04 = 0x2c, + Vit05 = 0x2d, + Vit06 = 0x2e, + Vit07 = 0x2f, + Vit08 = 0x30, + Vit09 = 0x31, + Vit10 = 0x32, + Vit11 = 0x33, + Vit12 = 0x34, + Sync01 = 0x35, + Sync02 = 0x36, + Rs01 = 0x37, + Mpeg01 = 0x38, + Mpeg02 = 0x39, + DiS01 = 0x3a, + DiS02 = 0x3b, + DiS03 = 0x3c, + DiS04 = 0x3d, + DiS05 = 0x3e, + DiS06 = 0x3f, + DiS07 = 0x40, + DiS08 = 0x41, + DiS09 = 0x42, + DiS10 = 0x43, + DiS11 = 0x44, + Rf01 = 0x45, + Err01 = 0x46, + Err02 = 0x47, + Err03 = 0x48, + Err04 = 0x49, +}; + + +#endif diff --git a/drivers/media/dvb-frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c new file mode 100644 index 0000000000..ff5d3bdf3b --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1432.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Samsung s5h1432 DVB-T demodulator driver + * + * Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <media/dvb_frontend.h> +#include "s5h1432.h" + +struct s5h1432_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct s5h1432_config *config; + + struct dvb_frontend frontend; + + enum fe_modulation current_modulation; + unsigned int first_tune:1; + + u32 current_frequency; + int if_freq; + + u8 inversion; +}; + +static int debug; + +#define dprintk(arg...) do { \ + if (debug) \ + printk(arg); \ + } while (0) + +static int s5h1432_writereg(struct s5h1432_state *state, + u8 addr, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + + struct i2c_msg msg = {.addr = addr, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, ret == %i)\n", + __func__, addr, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u8 s5h1432_readreg(struct s5h1432_state *state, u8 addr, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + {.addr = addr, .flags = 0, .buf = b0, .len = 1}, + {.addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 1} + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} + +static int s5h1432_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int s5h1432_set_channel_bandwidth(struct dvb_frontend *fe, + u32 bandwidth) +{ + struct s5h1432_state *state = fe->demodulator_priv; + + u8 reg = 0; + + /* Register [0x2E] bit 3:2 : 8MHz = 0; 7MHz = 1; 6MHz = 2 */ + reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x2E); + reg &= ~(0x0C); + switch (bandwidth) { + case 6: + reg |= 0x08; + break; + case 7: + reg |= 0x04; + break; + case 8: + reg |= 0x00; + break; + default: + return 0; + } + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2E, reg); + return 1; +} + +static int s5h1432_set_IF(struct dvb_frontend *fe, u32 ifFreqHz) +{ + struct s5h1432_state *state = fe->demodulator_priv; + + switch (ifFreqHz) { + case TAIWAN_HI_IF_FREQ_44_MHZ: + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x15); + break; + case EUROPE_HI_IF_FREQ_36_MHZ: + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x40); + break; + case IF_FREQ_6_MHZ: + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xe0); + break; + case IF_FREQ_3point3_MHZ: + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); + break; + case IF_FREQ_3point5_MHZ: + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xED); + break; + case IF_FREQ_4_MHZ: + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0xAA); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0xAA); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEA); + break; + default: + { + u32 value = 0; + value = (u32) (((48000 - (ifFreqHz / 1000)) * 512 * + (u32) 32768) / (48 * 1000)); + printk(KERN_INFO + "Default IFFreq %d :reg value = 0x%x\n", + ifFreqHz, value); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, + (u8) value & 0xFF); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, + (u8) (value >> 8) & 0xFF); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, + (u8) (value >> 16) & 0xFF); + break; + } + + } + + return 1; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int s5h1432_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 dvb_bandwidth = 8; + struct s5h1432_state *state = fe->demodulator_priv; + + if (p->frequency == state->current_frequency) { + /*current_frequency = p->frequency; */ + /*state->current_frequency = p->frequency; */ + } else { + fe->ops.tuner_ops.set_params(fe); + msleep(300); + s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); + switch (p->bandwidth_hz) { + case 6000000: + dvb_bandwidth = 6; + s5h1432_set_IF(fe, IF_FREQ_4_MHZ); + break; + case 7000000: + dvb_bandwidth = 7; + s5h1432_set_IF(fe, IF_FREQ_4_MHZ); + break; + case 8000000: + dvb_bandwidth = 8; + s5h1432_set_IF(fe, IF_FREQ_4_MHZ); + break; + default: + return 0; + } + /*fe->ops.tuner_ops.set_params(fe); */ +/*Soft Reset chip*/ + msleep(30); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); + msleep(30); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); + + s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); + switch (p->bandwidth_hz) { + case 6000000: + dvb_bandwidth = 6; + s5h1432_set_IF(fe, IF_FREQ_4_MHZ); + break; + case 7000000: + dvb_bandwidth = 7; + s5h1432_set_IF(fe, IF_FREQ_4_MHZ); + break; + case 8000000: + dvb_bandwidth = 8; + s5h1432_set_IF(fe, IF_FREQ_4_MHZ); + break; + default: + return 0; + } + /*fe->ops.tuner_ops.set_params(fe); */ + /*Soft Reset chip*/ + msleep(30); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); + msleep(30); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); + + } + + state->current_frequency = p->frequency; + + return 0; +} + +static int s5h1432_init(struct dvb_frontend *fe) +{ + struct s5h1432_state *state = fe->demodulator_priv; + + u8 reg = 0; + state->current_frequency = 0; + printk(KERN_INFO " s5h1432_init().\n"); + + /*Set VSB mode as default, this also does a soft reset */ + /*Initialize registers */ + + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x04, 0xa8); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x05, 0x01); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x07, 0x70); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x19, 0x80); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1b, 0x9D); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1c, 0x30); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1d, 0x20); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x1B); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2e, 0x40); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, 0x84); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x50, 0x5a); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x5a, 0xd3); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x68, 0x50); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xb8, 0x3c); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xc4, 0x10); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xcc, 0x9c); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xDA, 0x00); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe1, 0x94); + /* s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf4, 0xa1); */ + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf9, 0x00); + + /*For NXP tuner*/ + + /*Set 3.3MHz as default IF frequency */ + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); + /* Set reg 0x1E to get the full dynamic range */ + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x31); + + /* Mode setting in demod */ + reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x42); + reg |= 0x80; + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, reg); + /* Serial mode */ + + /* Soft Reset chip */ + + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); + msleep(30); + s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); + + + return 0; +} + +static int s5h1432_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + return 0; +} + +static int s5h1432_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + return 0; +} + +static int s5h1432_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + return 0; +} + +static int s5h1432_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + + return 0; +} + +static int s5h1432_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return 0; +} + +static int s5h1432_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + return 0; +} + +static void s5h1432_release(struct dvb_frontend *fe) +{ + struct s5h1432_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops s5h1432_ops; + +struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, + struct i2c_adapter *i2c) +{ + struct s5h1432_state *state = NULL; + + printk(KERN_INFO " Enter s5h1432_attach(). attach success!\n"); + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct s5h1432_state), GFP_KERNEL); + if (!state) + return NULL; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->current_modulation = QAM_16; + state->inversion = state->config->inversion; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s5h1432_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + + return &state->frontend; +} +EXPORT_SYMBOL_GPL(s5h1432_attach); + +static const struct dvb_frontend_ops s5h1432_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Samsung s5h1432 DVB-T Frontend", + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER}, + + .init = s5h1432_init, + .sleep = s5h1432_sleep, + .set_frontend = s5h1432_set_frontend, + .get_tune_settings = s5h1432_get_tune_settings, + .read_status = s5h1432_read_status, + .read_ber = s5h1432_read_ber, + .read_signal_strength = s5h1432_read_signal_strength, + .read_snr = s5h1432_read_snr, + .read_ucblocks = s5h1432_read_ucblocks, + .release = s5h1432_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Samsung s5h1432 DVB-T Demodulator driver"); +MODULE_AUTHOR("Bill Liu"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h new file mode 100644 index 0000000000..f031c0654e --- /dev/null +++ b/drivers/media/dvb-frontends/s5h1432.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Samsung s5h1432 VSB/QAM demodulator driver + * + * Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com> + */ + +#ifndef __S5H1432_H__ +#define __S5H1432_H__ + +#include <linux/dvb/frontend.h> + +#define S5H1432_I2C_TOP_ADDR (0x02 >> 1) + +#define TAIWAN_HI_IF_FREQ_44_MHZ 44000000 +#define EUROPE_HI_IF_FREQ_36_MHZ 36000000 +#define IF_FREQ_6_MHZ 6000000 +#define IF_FREQ_3point3_MHZ 3300000 +#define IF_FREQ_3point5_MHZ 3500000 +#define IF_FREQ_4_MHZ 4000000 + +struct s5h1432_config { + + /* serial/parallel output */ +#define S5H1432_PARALLEL_OUTPUT 0 +#define S5H1432_SERIAL_OUTPUT 1 + u8 output_mode; + + /* GPIO Setting */ +#define S5H1432_GPIO_OFF 0 +#define S5H1432_GPIO_ON 1 + u8 gpio; + + /* MPEG signal timing */ +#define S5H1432_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0 +#define S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1 +#define S5H1432_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2 +#define S5H1432_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3 + u16 mpeg_timing; + + /* IF Freq for QAM and VSB in KHz */ +#define S5H1432_IF_3250 3250 +#define S5H1432_IF_3500 3500 +#define S5H1432_IF_4000 4000 +#define S5H1432_IF_5380 5380 +#define S5H1432_IF_44000 44000 +#define S5H1432_VSB_IF_DEFAULT s5h1432_IF_44000 +#define S5H1432_QAM_IF_DEFAULT s5h1432_IF_44000 + u16 qam_if; + u16 vsb_if; + + /* Spectral Inversion */ +#define S5H1432_INVERSION_OFF 0 +#define S5H1432_INVERSION_ON 1 + u8 inversion; + + /* Return lock status based on tuner lock, or demod lock */ +#define S5H1432_TUNERLOCKING 0 +#define S5H1432_DEMODLOCKING 1 + u8 status_mode; +}; + +#if IS_REACHABLE(CONFIG_DVB_S5H1432) +extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *s5h1432_attach(const struct s5h1432_config + *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_s5h1432 */ + +#endif /* __s5h1432_H__ */ diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c new file mode 100644 index 0000000000..7e461ac159 --- /dev/null +++ b/drivers/media/dvb-frontends/s921.c @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Sharp VA3A5JZ921 One Seg Broadcast Module driver + * This device is labeled as just S. 921 at the top of the frontend can + * + * Copyright (C) 2009-2010 Mauro Carvalho Chehab + * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com> + * + * Developed for Leadership SBTVD 1seg device sold in Brazil + * + * Frontend module based on cx24123 driver, getting some info from + * the old s921 driver. + * + * FIXME: Need to port to DVB v5.2 API + */ + +#include <linux/kernel.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "s921.h" + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +#define rc(args...) do { \ + printk(KERN_ERR "s921: " args); \ +} while (0) + +#define dprintk(args...) \ + do { \ + if (debug) { \ + printk(KERN_DEBUG "s921: %s: ", __func__); \ + printk(args); \ + } \ + } while (0) + +struct s921_state { + struct i2c_adapter *i2c; + const struct s921_config *config; + + struct dvb_frontend frontend; + + /* The Demod can't easily provide these, we cache them */ + u32 currentfreq; +}; + +/* + * Various tuner defaults need to be established for a given frequency kHz. + * fixme: The bounds on the bands do not match the doc in real life. + * fixme: Some of them have been moved, other might need adjustment. + */ +static struct s921_bandselect_val { + u32 freq_low; + u8 band_reg; +} s921_bandselect[] = { + { 0, 0x7b }, + { 485140000, 0x5b }, + { 515140000, 0x3b }, + { 545140000, 0x1b }, + { 599140000, 0xfb }, + { 623140000, 0xdb }, + { 659140000, 0xbb }, + { 713140000, 0x9b }, +}; + +struct regdata { + u8 reg; + u8 data; +}; + +static struct regdata s921_init[] = { + { 0x01, 0x80 }, /* Probably, a reset sequence */ + { 0x01, 0x40 }, + { 0x01, 0x80 }, + { 0x01, 0x40 }, + + { 0x02, 0x00 }, + { 0x03, 0x40 }, + { 0x04, 0x01 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x0a, 0x00 }, + { 0x0b, 0x5a }, + { 0x0c, 0x00 }, + { 0x0d, 0x00 }, + { 0x0f, 0x00 }, + { 0x13, 0x1b }, + { 0x14, 0x80 }, + { 0x15, 0x40 }, + { 0x17, 0x70 }, + { 0x18, 0x01 }, + { 0x19, 0x12 }, + { 0x1a, 0x01 }, + { 0x1b, 0x12 }, + { 0x1c, 0xa0 }, + { 0x1d, 0x00 }, + { 0x1e, 0x0a }, + { 0x1f, 0x08 }, + { 0x20, 0x40 }, + { 0x21, 0xff }, + { 0x22, 0x4c }, + { 0x23, 0x4e }, + { 0x24, 0x4c }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0xf4 }, + { 0x28, 0x60 }, + { 0x29, 0x88 }, + { 0x2a, 0x40 }, + { 0x2b, 0x40 }, + { 0x2c, 0xff }, + { 0x2d, 0x00 }, + { 0x2e, 0xff }, + { 0x2f, 0x00 }, + { 0x30, 0x20 }, + { 0x31, 0x06 }, + { 0x32, 0x0c }, + { 0x34, 0x0f }, + { 0x37, 0xfe }, + { 0x38, 0x00 }, + { 0x39, 0x63 }, + { 0x3a, 0x10 }, + { 0x3b, 0x10 }, + { 0x47, 0x00 }, + { 0x49, 0xe5 }, + { 0x4b, 0x00 }, + { 0x50, 0xc0 }, + { 0x52, 0x20 }, + { 0x54, 0x5a }, + { 0x55, 0x5b }, + { 0x56, 0x40 }, + { 0x57, 0x70 }, + { 0x5c, 0x50 }, + { 0x5d, 0x00 }, + { 0x62, 0x17 }, + { 0x63, 0x2f }, + { 0x64, 0x6f }, + { 0x68, 0x00 }, + { 0x69, 0x89 }, + { 0x6a, 0x00 }, + { 0x6b, 0x00 }, + { 0x6c, 0x00 }, + { 0x6d, 0x00 }, + { 0x6e, 0x00 }, + { 0x70, 0x10 }, + { 0x71, 0x00 }, + { 0x75, 0x00 }, + { 0x76, 0x30 }, + { 0x77, 0x01 }, + { 0xaf, 0x00 }, + { 0xb0, 0xa0 }, + { 0xb2, 0x3d }, + { 0xb3, 0x25 }, + { 0xb4, 0x8b }, + { 0xb5, 0x4b }, + { 0xb6, 0x3f }, + { 0xb7, 0xff }, + { 0xb8, 0xff }, + { 0xb9, 0xfc }, + { 0xba, 0x00 }, + { 0xbb, 0x00 }, + { 0xbc, 0x00 }, + { 0xd0, 0x30 }, + { 0xe4, 0x84 }, + { 0xf0, 0x48 }, + { 0xf1, 0x19 }, + { 0xf2, 0x5a }, + { 0xf3, 0x8e }, + { 0xf4, 0x2d }, + { 0xf5, 0x07 }, + { 0xf6, 0x5a }, + { 0xf7, 0xba }, + { 0xf8, 0xd7 }, +}; + +static struct regdata s921_prefreq[] = { + { 0x47, 0x60 }, + { 0x68, 0x00 }, + { 0x69, 0x89 }, + { 0xf0, 0x48 }, + { 0xf1, 0x19 }, +}; + +static struct regdata s921_postfreq[] = { + { 0xf5, 0xae }, + { 0xf6, 0xb7 }, + { 0xf7, 0xba }, + { 0xf8, 0xd7 }, + { 0x68, 0x0a }, + { 0x69, 0x09 }, +}; + +static int s921_i2c_writereg(struct s921_state *state, + u8 i2c_addr, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 + }; + int rc; + + rc = i2c_transfer(state->i2c, &msg, 1); + if (rc != 1) { + printk("%s: writereg rcor(rc == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, rc, reg, data); + return rc; + } + + return 0; +} + +static int s921_i2c_writeregdata(struct s921_state *state, u8 i2c_addr, + struct regdata *rd, int size) +{ + int i, rc; + + for (i = 0; i < size; i++) { + rc = s921_i2c_writereg(state, i2c_addr, rd[i].reg, rd[i].data); + if (rc < 0) + return rc; + } + return 0; +} + +static int s921_i2c_readreg(struct s921_state *state, u8 i2c_addr, u8 reg) +{ + u8 val; + int rc; + struct i2c_msg msg[] = { + { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } + }; + + rc = i2c_transfer(state->i2c, msg, 2); + + if (rc != 2) { + rc("%s: reg=0x%x (rcor=%d)\n", __func__, reg, rc); + return rc; + } + + return val; +} + +#define s921_readreg(state, reg) \ + s921_i2c_readreg(state, state->config->demod_address, reg) +#define s921_writereg(state, reg, val) \ + s921_i2c_writereg(state, state->config->demod_address, reg, val) +#define s921_writeregdata(state, regdata) \ + s921_i2c_writeregdata(state, state->config->demod_address, \ + regdata, ARRAY_SIZE(regdata)) + +static int s921_pll_tune(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct s921_state *state = fe->demodulator_priv; + int band, rc, i; + unsigned long f_offset; + u8 f_switch; + u64 offset; + + dprintk("frequency=%i\n", p->frequency); + + for (band = 0; band < ARRAY_SIZE(s921_bandselect); band++) + if (p->frequency < s921_bandselect[band].freq_low) + break; + band--; + + if (band < 0) { + rc("%s: frequency out of range\n", __func__); + return -EINVAL; + } + + f_switch = s921_bandselect[band].band_reg; + + offset = ((u64)p->frequency) * 258; + do_div(offset, 6000000); + f_offset = ((unsigned long)offset) + 2321; + + rc = s921_writeregdata(state, s921_prefreq); + if (rc < 0) + return rc; + + rc = s921_writereg(state, 0xf2, (f_offset >> 8) & 0xff); + if (rc < 0) + return rc; + + rc = s921_writereg(state, 0xf3, f_offset & 0xff); + if (rc < 0) + return rc; + + rc = s921_writereg(state, 0xf4, f_switch); + if (rc < 0) + return rc; + + rc = s921_writeregdata(state, s921_postfreq); + if (rc < 0) + return rc; + + for (i = 0 ; i < 6; i++) { + rc = s921_readreg(state, 0x80); + dprintk("status 0x80: %02x\n", rc); + } + rc = s921_writereg(state, 0x01, 0x40); + if (rc < 0) + return rc; + + rc = s921_readreg(state, 0x01); + dprintk("status 0x01: %02x\n", rc); + + rc = s921_readreg(state, 0x80); + dprintk("status 0x80: %02x\n", rc); + + rc = s921_readreg(state, 0x80); + dprintk("status 0x80: %02x\n", rc); + + rc = s921_readreg(state, 0x32); + dprintk("status 0x32: %02x\n", rc); + + dprintk("pll tune band=%d, pll=%d\n", f_switch, (int)f_offset); + + return 0; +} + +static int s921_initfe(struct dvb_frontend *fe) +{ + struct s921_state *state = fe->demodulator_priv; + int rc; + + dprintk("\n"); + + rc = s921_writeregdata(state, s921_init); + if (rc < 0) + return rc; + + return 0; +} + +static int s921_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct s921_state *state = fe->demodulator_priv; + int regstatus, rc; + + *status = 0; + + rc = s921_readreg(state, 0x81); + if (rc < 0) + return rc; + + regstatus = rc << 8; + + rc = s921_readreg(state, 0x82); + if (rc < 0) + return rc; + + regstatus |= rc; + + dprintk("status = %04x\n", regstatus); + + /* Full Sync - We don't know what each bit means on regs 0x81/0x82 */ + if ((regstatus & 0xff) == 0x40) { + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } else if (regstatus & 0x40) { + /* This is close to Full Sync, but not enough to get useful info */ + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + } + + return 0; +} + +static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + enum fe_status status; + struct s921_state *state = fe->demodulator_priv; + int rc; + + /* FIXME: Use the proper register for it... 0x80? */ + rc = s921_read_status(fe, &status); + if (rc < 0) + return rc; + + *strength = (status & FE_HAS_LOCK) ? 0xffff : 0; + + dprintk("strength = 0x%04x\n", *strength); + + rc = s921_readreg(state, 0x01); + dprintk("status 0x01: %02x\n", rc); + + rc = s921_readreg(state, 0x80); + dprintk("status 0x80: %02x\n", rc); + + rc = s921_readreg(state, 0x32); + dprintk("status 0x32: %02x\n", rc); + + return 0; +} + +static int s921_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct s921_state *state = fe->demodulator_priv; + int rc; + + dprintk("\n"); + + /* FIXME: We don't know how to use non-auto mode */ + + rc = s921_pll_tune(fe); + if (rc < 0) + return rc; + + state->currentfreq = p->frequency; + + return 0; +} + +static int s921_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct s921_state *state = fe->demodulator_priv; + + /* FIXME: Probably it is possible to get it from regs f1 and f2 */ + p->frequency = state->currentfreq; + p->delivery_system = SYS_ISDBT; + + return 0; +} + +static int s921_tune(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int rc = 0; + + dprintk("\n"); + + if (re_tune) + rc = s921_set_frontend(fe); + + if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) + s921_read_status(fe, status); + + return rc; +} + +static enum dvbfe_algo s921_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static void s921_release(struct dvb_frontend *fe) +{ + struct s921_state *state = fe->demodulator_priv; + + dprintk("\n"); + kfree(state); +} + +static const struct dvb_frontend_ops s921_ops; + +struct dvb_frontend *s921_attach(const struct s921_config *config, + struct i2c_adapter *i2c) +{ + /* allocate memory for the internal state */ + struct s921_state *state = + kzalloc(sizeof(struct s921_state), GFP_KERNEL); + + dprintk("\n"); + if (!state) { + rc("Unable to kzalloc\n"); + return NULL; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s921_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +} +EXPORT_SYMBOL_GPL(s921_attach); + +static const struct dvb_frontend_ops s921_ops = { + .delsys = { SYS_ISDBT }, + /* Use dib8000 values per default */ + .info = { + .name = "Sharp S921", + .frequency_min_hz = 470 * MHz, + /* + * Max should be 770MHz instead, according with Sharp docs, + * but Leadership doc says it works up to 806 MHz. This is + * required to get channel 69, used in Brazil + */ + .frequency_max_hz = 806 * MHz, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = s921_release, + + .init = s921_initfe, + .set_frontend = s921_set_frontend, + .get_frontend = s921_get_frontend, + .read_status = s921_read_status, + .read_signal_strength = s921_read_signal_strength, + .tune = s921_tune, + .get_frontend_algo = s921_get_algo, +}; + +MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h new file mode 100644 index 0000000000..436fc79214 --- /dev/null +++ b/drivers/media/dvb-frontends/s921.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Sharp s921 driver + * + * Copyright (C) 2009 Mauro Carvalho Chehab + * Copyright (C) 2009 Douglas Landgraf <dougsland@redhat.com> + */ + +#ifndef S921_H +#define S921_H + +#include <linux/dvb/frontend.h> + +struct s921_config { + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_S921) +extern struct dvb_frontend *s921_attach(const struct s921_config *config, + struct i2c_adapter *i2c); +extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *); +#else +static inline struct dvb_frontend *s921_attach( + const struct s921_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct i2c_adapter * + s921_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* S921_H */ diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c new file mode 100644 index 0000000000..434d003bf3 --- /dev/null +++ b/drivers/media/dvb-frontends/si2165.c @@ -0,0 +1,1305 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator + * + * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org> + * + * References: + * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/firmware.h> +#include <linux/regmap.h> + +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "si2165_priv.h" +#include "si2165.h" + +/* + * Hauppauge WinTV-HVR-930C-HD B130 / PCTV QuatroStick 521e 1113xx + * uses 16 MHz xtal + * + * Hauppauge WinTV-HVR-930C-HD B131 / PCTV QuatroStick 522e 1114xx + * uses 24 MHz clock provided by tuner + */ + +struct si2165_state { + struct i2c_client *client; + + struct regmap *regmap; + + struct dvb_frontend fe; + + struct si2165_config config; + + u8 chip_revcode; + u8 chip_type; + + /* calculated by xtal and div settings */ + u32 fvco_hz; + u32 sys_clk; + u32 adc_clk; + + /* DVBv3 stats */ + u64 ber_prev; + + bool has_dvbc; + bool has_dvbt; + bool firmware_loaded; +}; + +static int si2165_write(struct si2165_state *state, const u16 reg, + const u8 *src, const int count) +{ + int ret; + + dev_dbg(&state->client->dev, "i2c write: reg: 0x%04x, data: %*ph\n", + reg, count, src); + + ret = regmap_bulk_write(state->regmap, reg, src, count); + + if (ret) + dev_err(&state->client->dev, "%s: ret == %d\n", __func__, ret); + + return ret; +} + +static int si2165_read(struct si2165_state *state, + const u16 reg, u8 *val, const int count) +{ + int ret = regmap_bulk_read(state->regmap, reg, val, count); + + if (ret) { + dev_err(&state->client->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n", + __func__, state->config.i2c_addr, reg, ret); + return ret; + } + + dev_dbg(&state->client->dev, "i2c read: reg: 0x%04x, data: %*ph\n", + reg, count, val); + + return 0; +} + +static int si2165_readreg8(struct si2165_state *state, + const u16 reg, u8 *val) +{ + unsigned int val_tmp; + int ret = regmap_read(state->regmap, reg, &val_tmp); + *val = (u8)val_tmp; + dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%02x\n", reg, *val); + return ret; +} + +static int si2165_readreg16(struct si2165_state *state, + const u16 reg, u16 *val) +{ + u8 buf[2]; + + int ret = si2165_read(state, reg, buf, 2); + *val = buf[0] | buf[1] << 8; + dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%04x\n", reg, *val); + return ret; +} + +static int si2165_readreg24(struct si2165_state *state, + const u16 reg, u32 *val) +{ + u8 buf[3]; + + int ret = si2165_read(state, reg, buf, 3); + *val = buf[0] | buf[1] << 8 | buf[2] << 16; + dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%06x\n", reg, *val); + return ret; +} + +static int si2165_writereg8(struct si2165_state *state, const u16 reg, u8 val) +{ + return regmap_write(state->regmap, reg, val); +} + +static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val) +{ + u8 buf[2] = { val & 0xff, (val >> 8) & 0xff }; + + return si2165_write(state, reg, buf, 2); +} + +static int si2165_writereg24(struct si2165_state *state, const u16 reg, u32 val) +{ + u8 buf[3] = { val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff }; + + return si2165_write(state, reg, buf, 3); +} + +static int si2165_writereg32(struct si2165_state *state, const u16 reg, u32 val) +{ + u8 buf[4] = { + val & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff + }; + return si2165_write(state, reg, buf, 4); +} + +static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg, + u8 val, u8 mask) +{ + if (mask != 0xff) { + u8 tmp; + int ret = si2165_readreg8(state, reg, &tmp); + + if (ret < 0) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + return si2165_writereg8(state, reg, val); +} + +#define REG16(reg, val) \ + { (reg), (val) & 0xff }, \ + { (reg) + 1, (val) >> 8 & 0xff } +struct si2165_reg_value_pair { + u16 reg; + u8 val; +}; + +static int si2165_write_reg_list(struct si2165_state *state, + const struct si2165_reg_value_pair *regs, + int count) +{ + int i; + int ret; + + for (i = 0; i < count; i++) { + ret = si2165_writereg8(state, regs[i].reg, regs[i].val); + if (ret < 0) + return ret; + } + return 0; +} + +static int si2165_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + return 0; +} + +static int si2165_init_pll(struct si2165_state *state) +{ + u32 ref_freq_hz = state->config.ref_freq_hz; + u8 divr = 1; /* 1..7 */ + u8 divp = 1; /* only 1 or 4 */ + u8 divn = 56; /* 1..63 */ + u8 divm = 8; + u8 divl = 12; + u8 buf[4]; + + /* + * hardcoded values can be deleted if calculation is verified + * or it yields the same values as the windows driver + */ + switch (ref_freq_hz) { + case 16000000u: + divn = 56; + break; + case 24000000u: + divr = 2; + divp = 4; + divn = 19; + break; + default: + /* ref_freq / divr must be between 4 and 16 MHz */ + if (ref_freq_hz > 16000000u) + divr = 2; + + /* + * now select divn and divp such that + * fvco is in 1624..1824 MHz + */ + if (1624000000u * divr > ref_freq_hz * 2u * 63u) + divp = 4; + + /* is this already correct regarding rounding? */ + divn = 1624000000u * divr / (ref_freq_hz * 2u * divp); + break; + } + + /* adc_clk and sys_clk depend on xtal and pll settings */ + state->fvco_hz = ref_freq_hz / divr + * 2u * divn * divp; + state->adc_clk = state->fvco_hz / (divm * 4u); + state->sys_clk = state->fvco_hz / (divl * 2u); + + /* write all 4 pll registers 0x00a0..0x00a3 at once */ + buf[0] = divl; + buf[1] = divm; + buf[2] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80; + buf[3] = divr; + return si2165_write(state, REG_PLL_DIVL, buf, 4); +} + +static int si2165_adjust_pll_divl(struct si2165_state *state, u8 divl) +{ + state->sys_clk = state->fvco_hz / (divl * 2u); + return si2165_writereg8(state, REG_PLL_DIVL, divl); +} + +static u32 si2165_get_fe_clk(struct si2165_state *state) +{ + /* assume Oversampling mode Ovr4 is used */ + return state->adc_clk; +} + +static int si2165_wait_init_done(struct si2165_state *state) +{ + int ret; + u8 val = 0; + int i; + + for (i = 0; i < 3; ++i) { + ret = si2165_readreg8(state, REG_INIT_DONE, &val); + if (ret < 0) + return ret; + if (val == 0x01) + return 0; + usleep_range(1000, 50000); + } + dev_err(&state->client->dev, "init_done was not set\n"); + return -EINVAL; +} + +static int si2165_upload_firmware_block(struct si2165_state *state, + const u8 *data, u32 len, u32 *poffset, + u32 block_count) +{ + int ret; + u8 buf_ctrl[4] = { 0x00, 0x00, 0x00, 0xc0 }; + u8 wordcount; + u32 cur_block = 0; + u32 offset = poffset ? *poffset : 0; + + if (len < 4) + return -EINVAL; + if (len % 4 != 0) + return -EINVAL; + + dev_dbg(&state->client->dev, + "fw load: %s: called with len=0x%x offset=0x%x blockcount=0x%x\n", + __func__, len, offset, block_count); + while (offset + 12 <= len && cur_block < block_count) { + dev_dbg(&state->client->dev, + "fw load: %s: in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n", + __func__, len, offset, cur_block, block_count); + wordcount = data[offset]; + if (wordcount < 1 || data[offset + 1] || + data[offset + 2] || data[offset + 3]) { + dev_warn(&state->client->dev, + "bad fw data[0..3] = %*ph\n", + 4, data); + return -EINVAL; + } + + if (offset + 8 + wordcount * 4 > len) { + dev_warn(&state->client->dev, + "len is too small for block len=%d, wordcount=%d\n", + len, wordcount); + return -EINVAL; + } + + buf_ctrl[0] = wordcount - 1; + + ret = si2165_write(state, REG_DCOM_CONTROL_BYTE, buf_ctrl, 4); + if (ret < 0) + goto error; + ret = si2165_write(state, REG_DCOM_ADDR, data + offset + 4, 4); + if (ret < 0) + goto error; + + offset += 8; + + while (wordcount > 0) { + ret = si2165_write(state, REG_DCOM_DATA, + data + offset, 4); + if (ret < 0) + goto error; + wordcount--; + offset += 4; + } + cur_block++; + } + + dev_dbg(&state->client->dev, + "fw load: %s: after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n", + __func__, len, offset, cur_block, block_count); + + if (poffset) + *poffset = offset; + + dev_dbg(&state->client->dev, + "fw load: %s: returned offset=0x%x\n", + __func__, offset); + + return 0; +error: + return ret; +} + +static int si2165_upload_firmware(struct si2165_state *state) +{ + /* int ret; */ + u8 val[3]; + u16 val16; + int ret; + + const struct firmware *fw = NULL; + u8 *fw_file; + const u8 *data; + u32 len; + u32 offset; + u8 patch_version; + u8 block_count; + u16 crc_expected; + + switch (state->chip_revcode) { + case 0x03: /* revision D */ + fw_file = SI2165_FIRMWARE_REV_D; + break; + default: + dev_info(&state->client->dev, "no firmware file for revision=%d\n", + state->chip_revcode); + return 0; + } + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &state->client->dev); + if (ret) { + dev_warn(&state->client->dev, "firmware file '%s' not found\n", + fw_file); + goto error; + } + + data = fw->data; + len = fw->size; + + dev_info(&state->client->dev, "downloading firmware from file '%s' size=%d\n", + fw_file, len); + + if (len % 4 != 0) { + dev_warn(&state->client->dev, "firmware size is not multiple of 4\n"); + ret = -EINVAL; + goto error; + } + + /* check header (8 bytes) */ + if (len < 8) { + dev_warn(&state->client->dev, "firmware header is missing\n"); + ret = -EINVAL; + goto error; + } + + if (data[0] != 1 || data[1] != 0) { + dev_warn(&state->client->dev, "firmware file version is wrong\n"); + ret = -EINVAL; + goto error; + } + + patch_version = data[2]; + block_count = data[4]; + crc_expected = data[7] << 8 | data[6]; + + /* start uploading fw */ + /* boot/wdog status */ + ret = si2165_writereg8(state, REG_WDOG_AND_BOOT, 0x00); + if (ret < 0) + goto error; + /* reset */ + ret = si2165_writereg8(state, REG_RST_ALL, 0x00); + if (ret < 0) + goto error; + /* boot/wdog status */ + ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val); + if (ret < 0) + goto error; + + /* enable reset on error */ + ret = si2165_readreg8(state, REG_EN_RST_ERROR, val); + if (ret < 0) + goto error; + ret = si2165_readreg8(state, REG_EN_RST_ERROR, val); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, REG_EN_RST_ERROR, 0x02); + if (ret < 0) + goto error; + + /* start right after the header */ + offset = 8; + + dev_info(&state->client->dev, "%s: extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n", + __func__, patch_version, block_count, crc_expected); + + ret = si2165_upload_firmware_block(state, data, len, &offset, 1); + if (ret < 0) + goto error; + + ret = si2165_writereg8(state, REG_PATCH_VERSION, patch_version); + if (ret < 0) + goto error; + + /* reset crc */ + ret = si2165_writereg8(state, REG_RST_CRC, 0x01); + if (ret) + goto error; + + ret = si2165_upload_firmware_block(state, data, len, + &offset, block_count); + if (ret < 0) { + dev_err(&state->client->dev, + "firmware could not be uploaded\n"); + goto error; + } + + /* read crc */ + ret = si2165_readreg16(state, REG_CRC, &val16); + if (ret) + goto error; + + if (val16 != crc_expected) { + dev_err(&state->client->dev, + "firmware crc mismatch %04x != %04x\n", + val16, crc_expected); + ret = -EINVAL; + goto error; + } + + ret = si2165_upload_firmware_block(state, data, len, &offset, 5); + if (ret) + goto error; + + if (len != offset) { + dev_err(&state->client->dev, + "firmware len mismatch %04x != %04x\n", + len, offset); + ret = -EINVAL; + goto error; + } + + /* reset watchdog error register */ + ret = si2165_writereg_mask8(state, REG_WDOG_AND_BOOT, 0x02, 0x02); + if (ret < 0) + goto error; + + /* enable reset on error */ + ret = si2165_writereg_mask8(state, REG_EN_RST_ERROR, 0x01, 0x01); + if (ret < 0) + goto error; + + dev_info(&state->client->dev, "fw load finished\n"); + + ret = 0; + state->firmware_loaded = true; +error: + if (fw) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int si2165_init(struct dvb_frontend *fe) +{ + int ret = 0; + struct si2165_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 val; + u8 patch_version = 0x00; + + dev_dbg(&state->client->dev, "%s: called\n", __func__); + + /* powerup */ + ret = si2165_writereg8(state, REG_CHIP_MODE, state->config.chip_mode); + if (ret < 0) + goto error; + /* dsp_clock_enable */ + ret = si2165_writereg8(state, REG_DSP_CLOCK, 0x01); + if (ret < 0) + goto error; + /* verify chip_mode */ + ret = si2165_readreg8(state, REG_CHIP_MODE, &val); + if (ret < 0) + goto error; + if (val != state->config.chip_mode) { + dev_err(&state->client->dev, "could not set chip_mode\n"); + return -EINVAL; + } + + /* agc */ + ret = si2165_writereg8(state, REG_AGC_IF_TRI, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, REG_AGC_IF_SLR, 0x01); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, REG_AGC2_OUTPUT, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, REG_AGC2_CLKDIV, 0x07); + if (ret < 0) + goto error; + /* rssi pad */ + ret = si2165_writereg8(state, REG_RSSI_PAD_CTRL, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, REG_RSSI_ENABLE, 0x00); + if (ret < 0) + goto error; + + ret = si2165_init_pll(state); + if (ret < 0) + goto error; + + /* enable chip_init */ + ret = si2165_writereg8(state, REG_CHIP_INIT, 0x01); + if (ret < 0) + goto error; + /* set start_init */ + ret = si2165_writereg8(state, REG_START_INIT, 0x01); + if (ret < 0) + goto error; + ret = si2165_wait_init_done(state); + if (ret < 0) + goto error; + + /* disable chip_init */ + ret = si2165_writereg8(state, REG_CHIP_INIT, 0x00); + if (ret < 0) + goto error; + + /* ber_pkt - default 65535 */ + ret = si2165_writereg16(state, REG_BER_PKT, + STATISTICS_PERIOD_PKT_COUNT); + if (ret < 0) + goto error; + + ret = si2165_readreg8(state, REG_PATCH_VERSION, &patch_version); + if (ret < 0) + goto error; + + ret = si2165_writereg8(state, REG_AUTO_RESET, 0x00); + if (ret < 0) + goto error; + + /* dsp_addr_jump */ + ret = si2165_writereg32(state, REG_ADDR_JUMP, 0xf4000000); + if (ret < 0) + goto error; + /* boot/wdog status */ + ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, &val); + if (ret < 0) + goto error; + + if (patch_version == 0x00) { + ret = si2165_upload_firmware(state); + if (ret < 0) + goto error; + } + + /* ts output config */ + ret = si2165_writereg8(state, REG_TS_DATA_MODE, 0x20); + if (ret < 0) + return ret; + ret = si2165_writereg16(state, REG_TS_TRI, 0x00fe); + if (ret < 0) + return ret; + ret = si2165_writereg24(state, REG_TS_SLR, 0x555555); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, REG_TS_CLK_MODE, 0x01); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, REG_TS_PARALLEL_MODE, 0x00); + if (ret < 0) + return ret; + + c = &state->fe.dtv_property_cache; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +error: + return ret; +} + +static int si2165_sleep(struct dvb_frontend *fe) +{ + int ret; + struct si2165_state *state = fe->demodulator_priv; + + /* dsp clock disable */ + ret = si2165_writereg8(state, REG_DSP_CLOCK, 0x00); + if (ret < 0) + return ret; + /* chip mode */ + ret = si2165_writereg8(state, REG_CHIP_MODE, SI2165_MODE_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int si2165_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + int ret; + u8 u8tmp; + u32 u32tmp; + struct si2165_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + + *status = 0; + + switch (delsys) { + case SYS_DVBT: + /* check fast signal type */ + ret = si2165_readreg8(state, REG_CHECK_SIGNAL, &u8tmp); + if (ret < 0) + return ret; + switch (u8tmp & 0x3) { + case 0: /* searching */ + case 1: /* nothing */ + break; + case 2: /* digital signal */ + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + break; + } + break; + case SYS_DVBC_ANNEX_A: + /* check packet sync lock */ + ret = si2165_readreg8(state, REG_PS_LOCK, &u8tmp); + if (ret < 0) + return ret; + if (u8tmp & 0x01) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + } + break; + } + + /* check fec_lock */ + ret = si2165_readreg8(state, REG_FEC_LOCK, &u8tmp); + if (ret < 0) + return ret; + if (u8tmp & 0x01) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + } + + /* CNR */ + if (delsys == SYS_DVBC_ANNEX_A && *status & FE_HAS_VITERBI) { + ret = si2165_readreg24(state, REG_C_N, &u32tmp); + if (ret < 0) + return ret; + /* + * svalue = + * 1000 * c_n/dB = + * 1000 * 10 * log10(2^24 / regval) = + * 1000 * 10 * (log10(2^24) - log10(regval)) = + * 1000 * 10 * (intlog10(2^24) - intlog10(regval)) / 2^24 + * + * intlog10(x) = log10(x) * 2^24 + * intlog10(2^24) = log10(2^24) * 2^24 = 121210686 + */ + u32tmp = (1000 * 10 * (121210686 - (u64)intlog10(u32tmp))) + >> 24; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = u32tmp; + } else + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* BER */ + if (*status & FE_HAS_VITERBI) { + if (c->post_bit_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + /* start new sampling period to get rid of old data*/ + ret = si2165_writereg8(state, REG_BER_RST, 0x01); + if (ret < 0) + return ret; + + /* set scale to enter read code on next call */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_count.stat[0].uvalue = 0; + + /* + * reset DVBv3 value to deliver a good result + * for the first call + */ + state->ber_prev = 0; + + } else { + ret = si2165_readreg8(state, REG_BER_AVAIL, &u8tmp); + if (ret < 0) + return ret; + + if (u8tmp & 1) { + u32 biterrcnt; + + ret = si2165_readreg24(state, REG_BER_BIT, + &biterrcnt); + if (ret < 0) + return ret; + + c->post_bit_error.stat[0].uvalue += + biterrcnt; + c->post_bit_count.stat[0].uvalue += + STATISTICS_PERIOD_BIT_COUNT; + + /* start new sampling period */ + ret = si2165_writereg8(state, + REG_BER_RST, 0x01); + if (ret < 0) + return ret; + + dev_dbg(&state->client->dev, + "post_bit_error=%u post_bit_count=%u\n", + biterrcnt, STATISTICS_PERIOD_BIT_COUNT); + } + } + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +} + +static int si2165_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + return 0; +} + +static int si2165_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct si2165_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) { + *ber = 0; + return 0; + } + + *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev; + state->ber_prev = c->post_bit_error.stat[0].uvalue; + + return 0; +} + +static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate) +{ + u64 oversamp; + u32 reg_value; + + if (!dvb_rate) + return -EINVAL; + + oversamp = si2165_get_fe_clk(state); + oversamp <<= 23; + do_div(oversamp, dvb_rate); + reg_value = oversamp & 0x3fffffff; + + dev_dbg(&state->client->dev, "Write oversamp=%#x\n", reg_value); + return si2165_writereg32(state, REG_OVERSAMP, reg_value); +} + +static int si2165_set_if_freq_shift(struct si2165_state *state) +{ + struct dvb_frontend *fe = &state->fe; + u64 if_freq_shift; + s32 reg_value = 0; + u32 fe_clk = si2165_get_fe_clk(state); + u32 IF = 0; + + if (!fe->ops.tuner_ops.get_if_frequency) { + dev_err(&state->client->dev, + "Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); + return -EINVAL; + } + + if (!fe_clk) + return -EINVAL; + + fe->ops.tuner_ops.get_if_frequency(fe, &IF); + if_freq_shift = IF; + if_freq_shift <<= 29; + + do_div(if_freq_shift, fe_clk); + reg_value = (s32)if_freq_shift; + + if (state->config.inversion) + reg_value = -reg_value; + + reg_value = reg_value & 0x1fffffff; + + /* if_freq_shift, usbdump contained 0x023ee08f; */ + return si2165_writereg32(state, REG_IF_FREQ_SHIFT, reg_value); +} + +static const struct si2165_reg_value_pair dvbt_regs[] = { + /* standard = DVB-T */ + { REG_DVB_STANDARD, 0x01 }, + /* impulsive_noise_remover */ + { REG_IMPULSIVE_NOISE_REM, 0x01 }, + { REG_AUTO_RESET, 0x00 }, + /* agc2 */ + { REG_AGC2_MIN, 0x41 }, + { REG_AGC2_KACQ, 0x0e }, + { REG_AGC2_KLOC, 0x10 }, + /* agc */ + { REG_AGC_UNFREEZE_THR, 0x03 }, + { REG_AGC_CRESTF_DBX8, 0x78 }, + /* agc */ + { REG_AAF_CRESTF_DBX8, 0x78 }, + { REG_ACI_CRESTF_DBX8, 0x68 }, + /* freq_sync_range */ + REG16(REG_FREQ_SYNC_RANGE, 0x0064), + /* gp_reg0 */ + { REG_GP_REG0_MSB, 0x00 } +}; + +static int si2165_set_frontend_dvbt(struct dvb_frontend *fe) +{ + int ret; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct si2165_state *state = fe->demodulator_priv; + u32 dvb_rate = 0; + u16 bw10k; + u32 bw_hz = p->bandwidth_hz; + + dev_dbg(&state->client->dev, "%s: called\n", __func__); + + if (!state->has_dvbt) + return -EINVAL; + + /* no bandwidth auto-detection */ + if (bw_hz == 0) + return -EINVAL; + + dvb_rate = bw_hz * 8 / 7; + bw10k = bw_hz / 10000; + + ret = si2165_adjust_pll_divl(state, 12); + if (ret < 0) + return ret; + + /* bandwidth in 10KHz steps */ + ret = si2165_writereg16(state, REG_T_BANDWIDTH, bw10k); + if (ret < 0) + return ret; + ret = si2165_set_oversamp(state, dvb_rate); + if (ret < 0) + return ret; + + ret = si2165_write_reg_list(state, dvbt_regs, ARRAY_SIZE(dvbt_regs)); + if (ret < 0) + return ret; + + return 0; +} + +static const struct si2165_reg_value_pair dvbc_regs[] = { + /* standard = DVB-C */ + { REG_DVB_STANDARD, 0x05 }, + + /* agc2 */ + { REG_AGC2_MIN, 0x50 }, + { REG_AGC2_KACQ, 0x0e }, + { REG_AGC2_KLOC, 0x10 }, + /* agc */ + { REG_AGC_UNFREEZE_THR, 0x03 }, + { REG_AGC_CRESTF_DBX8, 0x68 }, + /* agc */ + { REG_AAF_CRESTF_DBX8, 0x68 }, + { REG_ACI_CRESTF_DBX8, 0x50 }, + + { REG_EQ_AUTO_CONTROL, 0x0d }, + + { REG_KP_LOCK, 0x05 }, + { REG_CENTRAL_TAP, 0x09 }, + REG16(REG_UNKNOWN_350, 0x3e80), + + { REG_AUTO_RESET, 0x01 }, + REG16(REG_UNKNOWN_24C, 0x0000), + REG16(REG_UNKNOWN_27C, 0x0000), + { REG_SWEEP_STEP, 0x03 }, + { REG_AGC_IF_TRI, 0x00 }, +}; + +static int si2165_set_frontend_dvbc(struct dvb_frontend *fe) +{ + struct si2165_state *state = fe->demodulator_priv; + int ret; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + const u32 dvb_rate = p->symbol_rate; + u8 u8tmp; + + if (!state->has_dvbc) + return -EINVAL; + + if (dvb_rate == 0) + return -EINVAL; + + ret = si2165_adjust_pll_divl(state, 14); + if (ret < 0) + return ret; + + /* Oversampling */ + ret = si2165_set_oversamp(state, dvb_rate); + if (ret < 0) + return ret; + + switch (p->modulation) { + case QPSK: + u8tmp = 0x3; + break; + case QAM_16: + u8tmp = 0x7; + break; + case QAM_32: + u8tmp = 0x8; + break; + case QAM_64: + u8tmp = 0x9; + break; + case QAM_128: + u8tmp = 0xa; + break; + case QAM_256: + default: + u8tmp = 0xb; + break; + } + ret = si2165_writereg8(state, REG_REQ_CONSTELLATION, u8tmp); + if (ret < 0) + return ret; + + ret = si2165_writereg32(state, REG_LOCK_TIMEOUT, 0x007a1200); + if (ret < 0) + return ret; + + ret = si2165_write_reg_list(state, dvbc_regs, ARRAY_SIZE(dvbc_regs)); + if (ret < 0) + return ret; + + return 0; +} + +static const struct si2165_reg_value_pair adc_rewrite[] = { + { REG_ADC_RI1, 0x46 }, + { REG_ADC_RI3, 0x00 }, + { REG_ADC_RI5, 0x0a }, + { REG_ADC_RI6, 0xff }, + { REG_ADC_RI8, 0x70 } +}; + +static int si2165_set_frontend(struct dvb_frontend *fe) +{ + struct si2165_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 delsys = p->delivery_system; + int ret; + u8 val[3]; + + /* initial setting of if freq shift */ + ret = si2165_set_if_freq_shift(state); + if (ret < 0) + return ret; + + switch (delsys) { + case SYS_DVBT: + ret = si2165_set_frontend_dvbt(fe); + if (ret < 0) + return ret; + break; + case SYS_DVBC_ANNEX_A: + ret = si2165_set_frontend_dvbc(fe); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + /* dsp_addr_jump */ + ret = si2165_writereg32(state, REG_ADDR_JUMP, 0xf4000000); + if (ret < 0) + return ret; + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* recalc if_freq_shift if IF might has changed */ + ret = si2165_set_if_freq_shift(state); + if (ret < 0) + return ret; + + /* boot/wdog status */ + ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, REG_WDOG_AND_BOOT, 0x00); + if (ret < 0) + return ret; + + /* reset all */ + ret = si2165_writereg8(state, REG_RST_ALL, 0x00); + if (ret < 0) + return ret; + /* gp_reg0 */ + ret = si2165_writereg32(state, REG_GP_REG0_LSB, 0x00000000); + if (ret < 0) + return ret; + + /* write adc values after each reset*/ + ret = si2165_write_reg_list(state, adc_rewrite, + ARRAY_SIZE(adc_rewrite)); + if (ret < 0) + return ret; + + /* start_synchro */ + ret = si2165_writereg8(state, REG_START_SYNCHRO, 0x01); + if (ret < 0) + return ret; + /* boot/wdog status */ + ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val); + if (ret < 0) + return ret; + + return 0; +} + +static const struct dvb_frontend_ops si2165_ops = { + .info = { + .name = "Silicon Labs ", + /* For DVB-C */ + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, + /* For DVB-T */ + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_RECOVER + }, + + .get_tune_settings = si2165_get_tune_settings, + + .init = si2165_init, + .sleep = si2165_sleep, + + .set_frontend = si2165_set_frontend, + .read_status = si2165_read_status, + .read_snr = si2165_read_snr, + .read_ber = si2165_read_ber, +}; + +static int si2165_probe(struct i2c_client *client) +{ + struct si2165_state *state = NULL; + struct si2165_platform_data *pdata = client->dev.platform_data; + int n; + int ret = 0; + u8 val; + char rev_char; + const char *chip_name; + static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x08ff, + }; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto error; + } + + /* create regmap */ + state->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(state->regmap)) { + ret = PTR_ERR(state->regmap); + goto error; + } + + /* setup the state */ + state->client = client; + state->config.i2c_addr = client->addr; + state->config.chip_mode = pdata->chip_mode; + state->config.ref_freq_hz = pdata->ref_freq_hz; + state->config.inversion = pdata->inversion; + + if (state->config.ref_freq_hz < 4000000 || + state->config.ref_freq_hz > 27000000) { + dev_err(&state->client->dev, "ref_freq of %d Hz not supported by this driver\n", + state->config.ref_freq_hz); + ret = -EINVAL; + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->fe.ops, &si2165_ops, + sizeof(struct dvb_frontend_ops)); + state->fe.ops.release = NULL; + state->fe.demodulator_priv = state; + i2c_set_clientdata(client, state); + + /* powerup */ + ret = si2165_writereg8(state, REG_CHIP_MODE, state->config.chip_mode); + if (ret < 0) + goto nodev_error; + + ret = si2165_readreg8(state, REG_CHIP_MODE, &val); + if (ret < 0) + goto nodev_error; + if (val != state->config.chip_mode) + goto nodev_error; + + ret = si2165_readreg8(state, REG_CHIP_REVCODE, &state->chip_revcode); + if (ret < 0) + goto nodev_error; + + ret = si2165_readreg8(state, REV_CHIP_TYPE, &state->chip_type); + if (ret < 0) + goto nodev_error; + + /* powerdown */ + ret = si2165_writereg8(state, REG_CHIP_MODE, SI2165_MODE_OFF); + if (ret < 0) + goto nodev_error; + + if (state->chip_revcode < 26) + rev_char = 'A' + state->chip_revcode; + else + rev_char = '?'; + + switch (state->chip_type) { + case 0x06: + chip_name = "Si2161"; + state->has_dvbt = true; + break; + case 0x07: + chip_name = "Si2165"; + state->has_dvbt = true; + state->has_dvbc = true; + break; + default: + dev_err(&state->client->dev, "Unsupported Silicon Labs chip (type %d, rev %d)\n", + state->chip_type, state->chip_revcode); + goto nodev_error; + } + + dev_info(&state->client->dev, + "Detected Silicon Labs %s-%c (type %d, rev %d)\n", + chip_name, rev_char, state->chip_type, + state->chip_revcode); + + strlcat(state->fe.ops.info.name, chip_name, + sizeof(state->fe.ops.info.name)); + + n = 0; + if (state->has_dvbt) { + state->fe.ops.delsys[n++] = SYS_DVBT; + strlcat(state->fe.ops.info.name, " DVB-T", + sizeof(state->fe.ops.info.name)); + } + if (state->has_dvbc) { + state->fe.ops.delsys[n++] = SYS_DVBC_ANNEX_A; + strlcat(state->fe.ops.info.name, " DVB-C", + sizeof(state->fe.ops.info.name)); + } + + /* return fe pointer */ + *pdata->fe = &state->fe; + + return 0; + +nodev_error: + ret = -ENODEV; +error: + kfree(state); + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void si2165_remove(struct i2c_client *client) +{ + struct si2165_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(state); +} + +static const struct i2c_device_id si2165_id_table[] = { + {"si2165", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, si2165_id_table); + +static struct i2c_driver si2165_driver = { + .driver = { + .name = "si2165", + }, + .probe = si2165_probe, + .remove = si2165_remove, + .id_table = si2165_id_table, +}; + +module_i2c_driver(si2165_driver); + +MODULE_DESCRIPTION("Silicon Labs Si2165 DVB-C/-T Demodulator driver"); +MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2165_FIRMWARE_REV_D); diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h new file mode 100644 index 0000000000..adc5e18754 --- /dev/null +++ b/drivers/media/dvb-frontends/si2165.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Silicon Labs SI2165 DVB-C/-T Demodulator + * + * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org> + * + * References: + * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf + */ + +#ifndef _DVB_SI2165_H +#define _DVB_SI2165_H + +#include <linux/dvb/frontend.h> + +enum { + SI2165_MODE_OFF = 0x00, + SI2165_MODE_PLL_EXT = 0x20, + SI2165_MODE_PLL_XTAL = 0x21 +}; + +/* I2C addresses + * possible values: 0x64,0x65,0x66,0x67 + */ +struct si2165_platform_data { + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; + + /* external clock or XTAL */ + u8 chip_mode; + + /* frequency of external clock or xtal in Hz + * possible values: 4000000, 16000000, 20000000, 240000000, 27000000 + */ + u32 ref_freq_hz; + + /* invert the spectrum */ + bool inversion; +}; + +#endif /* _DVB_SI2165_H */ diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h new file mode 100644 index 0000000000..869699d840 --- /dev/null +++ b/drivers/media/dvb-frontends/si2165_priv.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Silicon Labs SI2165 DVB-C/-T Demodulator + * + * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org> + */ + +#ifndef _DVB_SI2165_PRIV +#define _DVB_SI2165_PRIV + +#define SI2165_FIRMWARE_REV_D "dvb-demod-si2165.fw" + +struct si2165_config { + /* i2c addr + * possible values: 0x64,0x65,0x66,0x67 + */ + u8 i2c_addr; + + /* external clock or XTAL */ + u8 chip_mode; + + /* frequency of external clock or xtal in Hz + * possible values: 4000000, 16000000, 20000000, 240000000, 27000000 + */ + u32 ref_freq_hz; + + /* invert the spectrum */ + bool inversion; +}; + +#define STATISTICS_PERIOD_PKT_COUNT 30000u +#define STATISTICS_PERIOD_BIT_COUNT (STATISTICS_PERIOD_PKT_COUNT * 204 * 8) + +#define REG_CHIP_MODE 0x0000 +#define REG_CHIP_REVCODE 0x0023 +#define REV_CHIP_TYPE 0x0118 +#define REG_CHIP_INIT 0x0050 +#define REG_INIT_DONE 0x0054 +#define REG_START_INIT 0x0096 +#define REG_PLL_DIVL 0x00a0 +#define REG_RST_ALL 0x00c0 +#define REG_LOCK_TIMEOUT 0x00c4 +#define REG_AUTO_RESET 0x00cb +#define REG_OVERSAMP 0x00e4 +#define REG_IF_FREQ_SHIFT 0x00e8 +#define REG_DVB_STANDARD 0x00ec +#define REG_DSP_CLOCK 0x0104 +#define REG_ADC_RI8 0x0123 +#define REG_ADC_RI1 0x012a +#define REG_ADC_RI2 0x012b +#define REG_ADC_RI3 0x012c +#define REG_ADC_RI4 0x012d +#define REG_ADC_RI5 0x012e +#define REG_ADC_RI6 0x012f +#define REG_AGC_CRESTF_DBX8 0x0150 +#define REG_AGC_UNFREEZE_THR 0x015b +#define REG_AGC2_MIN 0x016e +#define REG_AGC2_KACQ 0x016c +#define REG_AGC2_KLOC 0x016d +#define REG_AGC2_OUTPUT 0x0170 +#define REG_AGC2_CLKDIV 0x0171 +#define REG_AGC_IF_TRI 0x018b +#define REG_AGC_IF_SLR 0x0190 +#define REG_AAF_CRESTF_DBX8 0x01a0 +#define REG_ACI_CRESTF_DBX8 0x01c8 +#define REG_SWEEP_STEP 0x0232 +#define REG_KP_LOCK 0x023a +#define REG_UNKNOWN_24C 0x024c +#define REG_CENTRAL_TAP 0x0261 +#define REG_C_N 0x026c +#define REG_EQ_AUTO_CONTROL 0x0278 +#define REG_UNKNOWN_27C 0x027c +#define REG_START_SYNCHRO 0x02e0 +#define REG_REQ_CONSTELLATION 0x02f4 +#define REG_T_BANDWIDTH 0x0308 +#define REG_FREQ_SYNC_RANGE 0x030c +#define REG_IMPULSIVE_NOISE_REM 0x031c +#define REG_WDOG_AND_BOOT 0x0341 +#define REG_PATCH_VERSION 0x0344 +#define REG_ADDR_JUMP 0x0348 +#define REG_UNKNOWN_350 0x0350 +#define REG_EN_RST_ERROR 0x035c +#define REG_DCOM_CONTROL_BYTE 0x0364 +#define REG_DCOM_ADDR 0x0368 +#define REG_DCOM_DATA 0x036c +#define REG_RST_CRC 0x0379 +#define REG_GP_REG0_LSB 0x0384 +#define REG_GP_REG0_MSB 0x0387 +#define REG_CRC 0x037a +#define REG_CHECK_SIGNAL 0x03a8 +#define REG_CBER_RST 0x0424 +#define REG_CBER_BIT 0x0428 +#define REG_CBER_ERR 0x0430 +#define REG_CBER_AVAIL 0x0434 +#define REG_PS_LOCK 0x0440 +#define REG_UNCOR_CNT 0x0468 +#define REG_BER_RST 0x046c +#define REG_BER_PKT 0x0470 +#define REG_BER_BIT 0x0478 +#define REG_BER_AVAIL 0x047c +#define REG_FEC_LOCK 0x04e0 +#define REG_TS_DATA_MODE 0x04e4 +#define REG_TS_CLK_MODE 0x04e5 +#define REG_TS_TRI 0x04ef +#define REG_TS_SLR 0x04f4 +#define REG_RSSI_ENABLE 0x0641 +#define REG_RSSI_PAD_CTRL 0x0646 +#define REG_TS_PARALLEL_MODE 0x08f8 + +#endif /* _DVB_SI2165_PRIV */ diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c new file mode 100644 index 0000000000..dae1f2153e --- /dev/null +++ b/drivers/media/dvb-frontends/si2168.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Silicon Labs Si2168 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + */ + +#include <linux/delay.h> + +#include "si2168_priv.h" + +static const struct dvb_frontend_ops si2168_ops; + +static void cmd_init(struct si2168_cmd *cmd, const u8 *buf, int wlen, int rlen) +{ + memcpy(cmd->args, buf, wlen); + cmd->wlen = wlen; + cmd->rlen = rlen; +} + +/* execute firmware command */ +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) +{ + struct si2168_dev *dev = i2c_get_clientdata(client); + int ret; + unsigned long timeout; + + mutex_lock(&dev->i2c_mutex); + + if (cmd->wlen) { + /* write cmd and args for firmware */ + ret = i2c_master_send(client, cmd->args, cmd->wlen); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->wlen) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + } + + if (cmd->rlen) { + /* wait cmd execution terminate */ + #define TIMEOUT 70 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = i2c_master_recv(client, cmd->args, cmd->rlen); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->rlen) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* firmware ready? */ + if ((cmd->args[0] >> 7) & 0x01) + break; + } + + dev_dbg(&client->dev, "cmd execution took %d ms\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT)); + + /* error bit set? */ + if ((cmd->args[0] >> 6) & 0x01) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + if (!((cmd->args[0] >> 7) & 0x01)) { + ret = -ETIMEDOUT; + goto err_mutex_unlock; + } + } + + mutex_unlock(&dev->i2c_mutex); + return 0; +err_mutex_unlock: + mutex_unlock(&dev->i2c_mutex); + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2168_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + struct si2168_cmd cmd; + int ret = 0; + + dev_dbg(&client->dev, "%s acquire: %d\n", __func__, acquire); + + /* set manual value */ + if (dev->ts_mode & SI2168_TS_CLK_MANUAL) { + cmd_init(&cmd, "\x14\x00\x0d\x10\xe8\x03", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + return ret; + } + /* set TS_MODE property */ + cmd_init(&cmd, "\x14\x00\x01\x10\x10\x00", 6, 4); + if (dev->ts_mode & SI2168_TS_CLK_MANUAL) + cmd.args[4] = SI2168_TS_CLK_MANUAL; + if (acquire) + cmd.args[4] |= dev->ts_mode; + else + cmd.args[4] |= SI2168_TS_TRISTATE; + if (dev->ts_clock_gapped) + cmd.args[4] |= 0x40; + ret = si2168_cmd_execute(client, &cmd); + + return ret; +} + +static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + unsigned int utmp, utmp1, utmp2; + struct si2168_cmd cmd; + + *status = 0; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + cmd_init(&cmd, "\xa0\x01", 2, 13); + break; + case SYS_DVBC_ANNEX_A: + cmd_init(&cmd, "\x90\x01", 2, 9); + break; + case SYS_DVBT2: + cmd_init(&cmd, "\x50\x01", 2, 14); + break; + default: + ret = -EINVAL; + goto err; + } + + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + switch ((cmd.args[2] >> 1) & 0x03) { + case 0x01: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + break; + case 0x03: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + break; + } + + dev->fe_status = *status; + + if (*status & FE_HAS_LOCK) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + dev_dbg(&client->dev, "status=%02x args=%*ph\n", + *status, cmd.rlen, cmd.args); + + /* BER */ + if (*status & FE_HAS_VITERBI) { + cmd_init(&cmd, "\x82\x00", 2, 3); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* + * Firmware returns [0, 255] mantissa and [0, 8] exponent. + * Convert to DVB API: mantissa * 10^(8 - exponent) / 10^8 + */ + utmp = clamp(8 - cmd.args[1], 0, 8); + for (i = 0, utmp1 = 1; i < utmp; i++) + utmp1 = utmp1 * 10; + + utmp1 = cmd.args[2] * utmp1; + utmp2 = 100000000; /* 10^8 */ + + dev_dbg(&client->dev, + "post_bit_error=%u post_bit_count=%u ber=%u*10^-%u\n", + utmp1, utmp2, cmd.args[2], cmd.args[1]); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += utmp1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp2; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* UCB */ + if (*status & FE_HAS_SYNC) { + cmd_init(&cmd, "\x84\x01", 2, 3); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + utmp1 = cmd.args[2] << 8 | cmd.args[1] << 0; + dev_dbg(&client->dev, "block_error=%u\n", utmp1); + + /* Sometimes firmware returns bogus value */ + if (utmp1 == 0xffff) + utmp1 = 0; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += utmp1; + } else { + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2168_set_frontend(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + struct si2168_cmd cmd; + u8 bandwidth, delivery_system; + + dev_dbg(&client->dev, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%u\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, + c->stream_id); + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + delivery_system = 0x20; + break; + case SYS_DVBC_ANNEX_A: + delivery_system = 0x30; + break; + case SYS_DVBT2: + delivery_system = 0x70; + break; + default: + ret = -EINVAL; + goto err; + } + + if (c->bandwidth_hz == 0) { + ret = -EINVAL; + goto err; + } else if (c->bandwidth_hz <= 2000000) + bandwidth = 0x02; + else if (c->bandwidth_hz <= 5000000) + bandwidth = 0x05; + else if (c->bandwidth_hz <= 6000000) + bandwidth = 0x06; + else if (c->bandwidth_hz <= 7000000) + bandwidth = 0x07; + else if (c->bandwidth_hz <= 8000000) + bandwidth = 0x08; + else if (c->bandwidth_hz <= 9000000) + bandwidth = 0x09; + else if (c->bandwidth_hz <= 10000000) + bandwidth = 0x0a; + else + bandwidth = 0x0f; + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + cmd_init(&cmd, "\x88\x02\x02\x02\x02", 5, 5); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* that has no big effect */ + if (c->delivery_system == SYS_DVBT) + cmd_init(&cmd, "\x89\x21\x06\x11\xff\x98", 6, 3); + else if (c->delivery_system == SYS_DVBC_ANNEX_A) + cmd_init(&cmd, "\x89\x21\x06\x11\x89\xf0", 6, 3); + else if (c->delivery_system == SYS_DVBT2) + cmd_init(&cmd, "\x89\x21\x06\x11\x89\x20", 6, 3); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + if (c->delivery_system == SYS_DVBT2) { + /* select PLP */ + cmd.args[0] = 0x52; + cmd.args[1] = c->stream_id & 0xff; + cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1; + cmd.wlen = 3; + cmd.rlen = 1; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + } + + cmd_init(&cmd, "\x51\x03", 2, 12); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x12\x08\x04", 3, 3); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x0c\x10\x12\x00", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x06\x10\x24\x00", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x07\x10\x00\x24", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x0a\x10\x00\x00", 6, 4); + cmd.args[4] = delivery_system | bandwidth; + if (dev->spectral_inversion) + cmd.args[5] |= 1; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* set DVB-C symbol rate */ + if (c->delivery_system == SYS_DVBC_ANNEX_A) { + cmd_init(&cmd, "\x14\x00\x02\x11\x00\x00", 6, 4); + cmd.args[4] = ((c->symbol_rate / 1000) >> 0) & 0xff; + cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + } + + cmd_init(&cmd, "\x14\x00\x0f\x10\x10\x00", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x09\x10\xe3\x08", 6, 4); + cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x08\x10\xd7\x05", 6, 4); + cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10; + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x01\x12\x00\x00", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x14\x00\x01\x03\x0c\x00", 6, 4); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + cmd_init(&cmd, "\x85", 1, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + dev->delivery_system = c->delivery_system; + + /* enable ts bus */ + ret = si2168_ts_bus_ctrl(fe, 1); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2168_init(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, len, remaining; + const struct firmware *fw; + struct si2168_cmd cmd; + + dev_dbg(&client->dev, "\n"); + + /* initialize */ + cmd_init(&cmd, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", + 13, 0); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + if (dev->warm) { + /* resume */ + cmd_init(&cmd, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + udelay(100); + cmd_init(&cmd, "\x85", 1, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + goto warm; + } + + /* power up */ + cmd_init(&cmd, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, dev->firmware_name, &client->dev); + if (ret) { + dev_err(&client->dev, + "firmware file '%s' not found\n", + dev->firmware_name); + goto err_release_firmware; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", + dev->firmware_name); + + if ((fw->size % 17 == 0) && (fw->data[0] > 5)) { + /* firmware is in the new format */ + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; + if (len > SI2168_ARGLEN) { + ret = -EINVAL; + break; + } + cmd_init(&cmd, &fw->data[(fw->size - remaining) + 1], + len, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + break; + } + } else if (fw->size % 8 == 0) { + /* firmware is in the old format */ + for (remaining = fw->size; remaining > 0; remaining -= 8) { + cmd_init(&cmd, &fw->data[fw->size - remaining], 8, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + break; + } + } else { + /* bad or unknown firmware format */ + ret = -EINVAL; + } + + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", ret); + goto err_release_firmware; + } + + release_firmware(fw); + + cmd_init(&cmd, "\x01\x01", 2, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* query firmware version */ + cmd_init(&cmd, "\x11", 1, 10); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 | + (cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0; + dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", + dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, + dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); + + /* set ts mode */ + ret = si2168_ts_bus_ctrl(fe, 1); + if (ret) + goto err; + + dev->warm = true; + dev->initialized = true; +warm: + /* Init stats here to indicate which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dev->active = true; + + return 0; +err_release_firmware: + release_firmware(fw); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2168_resume(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + + /* + * check whether si2168_init() has been called successfully + * outside of a resume cycle. Only call it (and load firmware) + * in this case. si2168_init() is only called during resume + * once the device has actually been used. Otherwise, leave the + * device untouched. + */ + if (dev->initialized) { + dev_dbg(&client->dev, "previously initialized, call si2168_init()\n"); + return si2168_init(fe); + } + dev_dbg(&client->dev, "not initialized yet, skipping init on resume\n"); + return 0; +} + +static int si2168_sleep(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); + int ret; + struct si2168_cmd cmd; + + dev_dbg(&client->dev, "\n"); + + dev->active = false; + + /* tri-state data bus */ + ret = si2168_ts_bus_ctrl(fe, 0); + if (ret) + goto err; + + /* Firmware later than B 4.0-11 loses warm state during sleep */ + if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0)) + dev->warm = false; + + cmd_init(&cmd, "\x13", 1, 0); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2168_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 900; + + return 0; +} + +static int si2168_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + int ret; + struct si2168_cmd cmd; + + /* open I2C gate */ + cmd_init(&cmd, "\xc0\x0d\x01", 3, 0); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + int ret; + struct si2168_cmd cmd; + + /* close I2C gate */ + cmd_init(&cmd, "\xc0\x0d\x00", 3, 0); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops si2168_ops = { + .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, + .info = { + .name = "Silicon Labs Si2168", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 870 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM + }, + + .get_tune_settings = si2168_get_tune_settings, + + .init = si2168_init, + .sleep = si2168_sleep, + .resume = si2168_resume, + + .set_frontend = si2168_set_frontend, + + .read_status = si2168_read_status, +}; + +static int si2168_probe(struct i2c_client *client) +{ + struct si2168_config *config = client->dev.platform_data; + struct si2168_dev *dev; + int ret; + struct si2168_cmd cmd; + + dev_dbg(&client->dev, "\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, dev); + mutex_init(&dev->i2c_mutex); + + /* Initialize */ + cmd_init(&cmd, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", + 13, 0); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err_kfree; + + /* Power up */ + cmd_init(&cmd, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8, 1); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err_kfree; + + /* Query chip revision */ + cmd_init(&cmd, "\x02", 1, 13); + ret = si2168_cmd_execute(client, &cmd); + if (ret) + goto err_kfree; + + dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | + cmd.args[3] << 8 | cmd.args[4] << 0; + + switch (dev->chip_id) { + case SI2168_CHIP_ID_A20: + dev->firmware_name = SI2168_A20_FIRMWARE; + break; + case SI2168_CHIP_ID_A30: + dev->firmware_name = SI2168_A30_FIRMWARE; + break; + case SI2168_CHIP_ID_B40: + dev->firmware_name = SI2168_B40_FIRMWARE; + break; + case SI2168_CHIP_ID_D60: + dev->firmware_name = SI2168_D60_FIRMWARE; + break; + default: + dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", + cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); + ret = -ENODEV; + goto err_kfree; + } + + dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 | + (cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0; + + /* create mux i2c adapter for tuner */ + dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, + 1, 0, I2C_MUX_LOCKED, + si2168_select, si2168_deselect); + if (!dev->muxc) { + ret = -ENOMEM; + goto err_kfree; + } + dev->muxc->priv = client; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_kfree; + + /* create dvb_frontend */ + memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = client; + *config->i2c_adapter = dev->muxc->adapter[0]; + *config->fe = &dev->fe; + dev->ts_mode = config->ts_mode; + dev->ts_clock_inv = config->ts_clock_inv; + dev->ts_clock_gapped = config->ts_clock_gapped; + dev->spectral_inversion = config->spectral_inversion; + + dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n", + dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, + dev->version >> 8 & 0xff); + dev_info(&client->dev, "firmware version: %c %d.%d.%d\n", + dev->version >> 24 & 0xff, dev->version >> 16 & 0xff, + dev->version >> 8 & 0xff, dev->version >> 0 & 0xff); + + return 0; +err_kfree: + kfree(dev); +err: + dev_warn(&client->dev, "probe failed = %d\n", ret); + return ret; +} + +static void si2168_remove(struct i2c_client *client) +{ + struct si2168_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + i2c_mux_del_adapters(dev->muxc); + + dev->fe.ops.release = NULL; + dev->fe.demodulator_priv = NULL; + + kfree(dev); +} + +static const struct i2c_device_id si2168_id_table[] = { + {"si2168", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, si2168_id_table); + +static struct i2c_driver si2168_driver = { + .driver = { + .name = "si2168", + .suppress_bind_attrs = true, + }, + .probe = si2168_probe, + .remove = si2168_remove, + .id_table = si2168_id_table, +}; + +module_i2c_driver(si2168_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2168_A20_FIRMWARE); +MODULE_FIRMWARE(SI2168_A30_FIRMWARE); +MODULE_FIRMWARE(SI2168_B40_FIRMWARE); +MODULE_FIRMWARE(SI2168_D60_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h new file mode 100644 index 0000000000..ecd21adf89 --- /dev/null +++ b/drivers/media/dvb-frontends/si2168.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Silicon Labs Si2168 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + */ + +#ifndef SI2168_H +#define SI2168_H + +#include <linux/dvb/frontend.h> +/** + * struct si2168_config - configuration parameters for si2168 + * + * @fe: + * frontend returned by driver + * @i2c_adapter: + * tuner I2C adapter returned by driver + * @ts_mode: + * Transport Stream mode. Can be: + * - %SI2168_TS_PARALLEL + * - %SI2168_TS_SERIAL + * - %SI2168_TS_TRISTATE + * - %SI2168_TS_CLK_MANUAL + * @ts_clock_inv: + * TS clock inverted + * @ts_clock_gapped: + * TS clock gapped + * @spectral_inversion: + * Inverted spectrum + * + * Note: + * The I2C address of this demod is 0x64. + */ +struct si2168_config { + struct dvb_frontend **fe; + struct i2c_adapter **i2c_adapter; + +#define SI2168_TS_PARALLEL 0x06 +#define SI2168_TS_SERIAL 0x03 +#define SI2168_TS_TRISTATE 0x00 +#define SI2168_TS_CLK_MANUAL 0x20 + u8 ts_mode; + + /* Flags */ + unsigned int ts_clock_inv:1; + unsigned int ts_clock_gapped:1; + unsigned int spectral_inversion:1; +}; + +#endif diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h new file mode 100644 index 0000000000..3a8976229a --- /dev/null +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Silicon Labs Si2168 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + */ + +#ifndef SI2168_PRIV_H +#define SI2168_PRIV_H + +#include "si2168.h" +#include <media/dvb_frontend.h> +#include <linux/firmware.h> +#include <linux/i2c-mux.h> +#include <linux/kernel.h> + +#define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw" +#define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" +#define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" +#define SI2168_D60_FIRMWARE "dvb-demod-si2168-d60-01.fw" + +/* state struct */ +struct si2168_dev { + struct mutex i2c_mutex; + struct i2c_mux_core *muxc; + struct dvb_frontend fe; + enum fe_delivery_system delivery_system; + enum fe_status fe_status; + #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) + #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) + #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) + #define SI2168_CHIP_ID_D60 ('D' << 24 | 68 << 16 | '6' << 8 | '0' << 0) + unsigned int chip_id; + unsigned int version; + const char *firmware_name; + u8 ts_mode; + unsigned int active:1; + unsigned int warm:1; + unsigned int initialized:1; + unsigned int ts_clock_inv:1; + unsigned int ts_clock_gapped:1; + unsigned int spectral_inversion:1; +}; + +/* firmware command struct */ +#define SI2168_ARGLEN 30 +struct si2168_cmd { + u8 args[SI2168_ARGLEN]; + unsigned wlen; + unsigned rlen; +}; + +#endif diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c new file mode 100644 index 0000000000..210ccd356e --- /dev/null +++ b/drivers/media/dvb-frontends/si21xx.c @@ -0,0 +1,947 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +*/ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "si21xx.h" + +#define REVISION_REG 0x00 +#define SYSTEM_MODE_REG 0x01 +#define TS_CTRL_REG_1 0x02 +#define TS_CTRL_REG_2 0x03 +#define PIN_CTRL_REG_1 0x04 +#define PIN_CTRL_REG_2 0x05 +#define LOCK_STATUS_REG_1 0x0f +#define LOCK_STATUS_REG_2 0x10 +#define ACQ_STATUS_REG 0x11 +#define ACQ_CTRL_REG_1 0x13 +#define ACQ_CTRL_REG_2 0x14 +#define PLL_DIVISOR_REG 0x15 +#define COARSE_TUNE_REG 0x16 +#define FINE_TUNE_REG_L 0x17 +#define FINE_TUNE_REG_H 0x18 + +#define ANALOG_AGC_POWER_LEVEL_REG 0x28 +#define CFO_ESTIMATOR_CTRL_REG_1 0x29 +#define CFO_ESTIMATOR_CTRL_REG_2 0x2a +#define CFO_ESTIMATOR_CTRL_REG_3 0x2b + +#define SYM_RATE_ESTIMATE_REG_L 0x31 +#define SYM_RATE_ESTIMATE_REG_M 0x32 +#define SYM_RATE_ESTIMATE_REG_H 0x33 + +#define CFO_ESTIMATOR_OFFSET_REG_L 0x36 +#define CFO_ESTIMATOR_OFFSET_REG_H 0x37 +#define CFO_ERROR_REG_L 0x38 +#define CFO_ERROR_REG_H 0x39 +#define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a + +#define SYM_RATE_REG_L 0x3f +#define SYM_RATE_REG_M 0x40 +#define SYM_RATE_REG_H 0x41 +#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42 +#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43 + +#define C_N_ESTIMATOR_CTRL_REG 0x7c +#define C_N_ESTIMATOR_THRSHLD_REG 0x7d +#define C_N_ESTIMATOR_LEVEL_REG_L 0x7e +#define C_N_ESTIMATOR_LEVEL_REG_H 0x7f + +#define BLIND_SCAN_CTRL_REG 0x80 + +#define LSA_CTRL_REG_1 0x8D +#define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f +#define ONE_DB_BNDWDTH_THRSHLD_REG 0x90 +#define TWO_DB_BNDWDTH_THRSHLD_REG 0x91 +#define THREE_DB_BNDWDTH_THRSHLD_REG 0x92 +#define INBAND_POWER_THRSHLD_REG 0x93 +#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94 + +#define VIT_SRCH_CTRL_REG_1 0xa0 +#define VIT_SRCH_CTRL_REG_2 0xa1 +#define VIT_SRCH_CTRL_REG_3 0xa2 +#define VIT_SRCH_STATUS_REG 0xa3 +#define VITERBI_BER_COUNT_REG_L 0xab +#define REED_SOLOMON_CTRL_REG 0xb0 +#define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1 +#define PRBS_CTRL_REG 0xb5 + +#define LNB_CTRL_REG_1 0xc0 +#define LNB_CTRL_REG_2 0xc1 +#define LNB_CTRL_REG_3 0xc2 +#define LNB_CTRL_REG_4 0xc3 +#define LNB_CTRL_STATUS_REG 0xc4 +#define LNB_FIFO_REGS_0 0xc5 +#define LNB_FIFO_REGS_1 0xc6 +#define LNB_FIFO_REGS_2 0xc7 +#define LNB_FIFO_REGS_3 0xc8 +#define LNB_FIFO_REGS_4 0xc9 +#define LNB_FIFO_REGS_5 0xca +#define LNB_SUPPLY_CTRL_REG_1 0xcb +#define LNB_SUPPLY_CTRL_REG_2 0xcc +#define LNB_SUPPLY_CTRL_REG_3 0xcd +#define LNB_SUPPLY_CTRL_REG_4 0xce +#define LNB_SUPPLY_STATUS_REG 0xcf + +#define FAIL -1 +#define PASS 0 + +#define ALLOWABLE_FS_COUNT 10 +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "si21xx: " args); \ + } while (0) + +enum { + ACTIVE_HIGH, + ACTIVE_LOW +}; +enum { + BYTE_WIDE, + BIT_WIDE +}; +enum { + CLK_GAPPED_MODE, + CLK_CONTINUOUS_MODE +}; +enum { + RISING_EDGE, + FALLING_EDGE +}; +enum { + MSB_FIRST, + LSB_FIRST +}; +enum { + SERIAL, + PARALLEL +}; + +struct si21xx_state { + struct i2c_adapter *i2c; + const struct si21xx_config *config; + struct dvb_frontend frontend; + u8 initialised:1; + int errmode; + int fs; /*Sampling rate of the ADC in MHz*/ +}; + +/* register default initialization */ +static u8 serit_sp1511lhb_inittab[] = { + 0x01, 0x28, /* set i2c_inc_disable */ + 0x20, 0x03, + 0x27, 0x20, + 0xe0, 0x45, + 0xe1, 0x08, + 0xfe, 0x01, + 0x01, 0x28, + 0x89, 0x09, + 0x04, 0x80, + 0x05, 0x01, + 0x06, 0x00, + 0x20, 0x03, + 0x24, 0x88, + 0x29, 0x09, + 0x2a, 0x0f, + 0x2c, 0x10, + 0x2d, 0x19, + 0x2e, 0x08, + 0x2f, 0x10, + 0x30, 0x19, + 0x34, 0x20, + 0x35, 0x03, + 0x45, 0x02, + 0x46, 0x45, + 0x47, 0xd0, + 0x48, 0x00, + 0x49, 0x40, + 0x4a, 0x03, + 0x4c, 0xfd, + 0x4f, 0x2e, + 0x50, 0x2e, + 0x51, 0x10, + 0x52, 0x10, + 0x56, 0x92, + 0x59, 0x00, + 0x5a, 0x2d, + 0x5b, 0x33, + 0x5c, 0x1f, + 0x5f, 0x76, + 0x62, 0xc0, + 0x63, 0xc0, + 0x64, 0xf3, + 0x65, 0xf3, + 0x79, 0x40, + 0x6a, 0x40, + 0x6b, 0x0a, + 0x6c, 0x80, + 0x6d, 0x27, + 0x71, 0x06, + 0x75, 0x60, + 0x78, 0x00, + 0x79, 0xb5, + 0x7c, 0x05, + 0x7d, 0x1a, + 0x87, 0x55, + 0x88, 0x72, + 0x8f, 0x08, + 0x90, 0xe0, + 0x94, 0x40, + 0xa0, 0x3f, + 0xa1, 0xc0, + 0xa4, 0xcc, + 0xa5, 0x66, + 0xa6, 0x66, + 0xa7, 0x7b, + 0xa8, 0x7b, + 0xa9, 0x7b, + 0xaa, 0x9a, + 0xed, 0x04, + 0xad, 0x00, + 0xae, 0x03, + 0xcc, 0xab, + 0x01, 0x08, + 0xff, 0xff +}; + +/* low level read/writes */ +static int si21_writeregs(struct si21xx_state *state, u8 reg1, + u8 *data, int len) +{ + int ret; + u8 buf[60];/* = { reg1, data };*/ + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = len + 1 + }; + + if (len > sizeof(buf) - 1) + return -EINVAL; + + msg.buf[0] = reg1; + memcpy(msg.buf + 1, data, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, ret == %i)\n", + __func__, reg1, data[0], ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len) +{ + struct si21xx_state *state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return si21_writereg(state, buf[0], buf[1]); +} + +static u8 si21_readreg(struct si21xx_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = ®1, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b, + .len = len + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (ret == %i)\n", __func__, ret); + + return ret == 2 ? 0 : -1; +} + +static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) +{ + unsigned long start = jiffies; + + dprintk("%s\n", __func__); + + while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { + if (time_is_before_jiffies(start + timeout)) { + dprintk("%s: timeout!!\n", __func__); + return -ETIMEDOUT; + } + msleep(10); + } + + return 0; +} + +static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) +{ + struct si21xx_state *state = fe->demodulator_priv; + u32 sym_rate, data_rate; + int i; + u8 sym_rate_bytes[3]; + + dprintk("%s : srate = %i\n", __func__ , srate); + + if ((srate < 1000000) || (srate > 45000000)) + return -EINVAL; + + data_rate = srate; + sym_rate = 0; + + for (i = 0; i < 4; ++i) { + sym_rate /= 100; + sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / + state->fs; + data_rate /= 100; + } + for (i = 0; i < 3; ++i) + sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); + + si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); + + return 0; +} + +static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 lnb_status; + u8 LNB_CTRL_1; + int status; + + dprintk("%s\n", __func__); + + status = PASS; + LNB_CTRL_1 = 0; + + status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); + status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); + + /*fill the FIFO*/ + status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); + + LNB_CTRL_1 = (lnb_status & 0x70); + LNB_CTRL_1 |= m->msg_len; + + LNB_CTRL_1 |= 0x80; /* begin LNB signaling */ + + status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); + + return status; +} + +static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 val; + + dprintk("%s\n", __func__); + + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; + + val = (0x80 | si21_readreg(state, 0xc1)); + if (si21_writereg(state, LNB_CTRL_REG_1, + burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) + return -EREMOTEIO; + + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; + + if (si21_writereg(state, LNB_CTRL_REG_1, val)) + return -EREMOTEIO; + + return 0; +} +/* 30.06.2008 */ +static int si21xx_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 val; + + dprintk("%s\n", __func__); + val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); + + switch (tone) { + case SEC_TONE_ON: + return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); + + case SEC_TONE_OFF: + return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); + + default: + return -EINVAL; + } +} + +static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt) +{ + struct si21xx_state *state = fe->demodulator_priv; + + u8 val; + dprintk("%s: %s\n", __func__, + volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + + val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); + + switch (volt) { + case SEC_VOLTAGE_18: + return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); + case SEC_VOLTAGE_13: + return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); + default: + return -EINVAL; + } +} + +static int si21xx_init(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + int i; + int status = 0; + u8 reg1; + u8 val; + u8 reg2[2]; + + dprintk("%s\n", __func__); + + for (i = 0; ; i += 2) { + reg1 = serit_sp1511lhb_inittab[i]; + val = serit_sp1511lhb_inittab[i+1]; + if (reg1 == 0xff && val == 0xff) + break; + si21_writeregs(state, reg1, &val, 1); + } + + /*DVB QPSK SYSTEM MODE REG*/ + reg1 = 0x08; + si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); + + /*transport stream config*/ + /* + mode = PARALLEL; + sdata_form = LSB_FIRST; + clk_edge = FALLING_EDGE; + clk_mode = CLK_GAPPED_MODE; + strt_len = BYTE_WIDE; + sync_pol = ACTIVE_HIGH; + val_pol = ACTIVE_HIGH; + err_pol = ACTIVE_HIGH; + sclk_rate = 0x00; + parity = 0x00 ; + data_delay = 0x00; + clk_delay = 0x00; + pclk_smooth = 0x00; + */ + reg2[0] = + PARALLEL + (LSB_FIRST << 1) + + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) + + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) + + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); + + reg2[1] = 0; + /* sclk_rate + (parity << 2) + + (data_delay << 3) + (clk_delay << 4) + + (pclk_smooth << 5); + */ + status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); + if (status != 0) + dprintk(" %s : TS Set Error\n", __func__); + + return 0; + +} + +static int si21_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 regs_read[2]; + u8 reg_read; + u8 i; + u8 lock; + u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); + + si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); + reg_read = 0; + + for (i = 0; i < 7; ++i) + reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); + + lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); + + dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (lock & 0x2) + *status |= FE_HAS_CARRIER; + + if (lock & 0x20) + *status |= FE_HAS_VITERBI; + + if (lock & 0x40) + *status |= FE_HAS_SYNC; + + if ((lock & 0x7b) == 0x7b) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct si21xx_state *state = fe->demodulator_priv; + + /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, + (u8*)agclevel, 0x01);*/ + + u16 signal = (3 * si21_readreg(state, 0x27) * + si21_readreg(state, 0x28)); + + dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, + si21_readreg(state, 0x27), + si21_readreg(state, 0x28), (int) signal); + + signal <<= 4; + *strength = signal; + + return 0; +} + +static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct si21xx_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->errmode != STATUS_BER) + return 0; + + *ber = (si21_readreg(state, 0x1d) << 8) | + si21_readreg(state, 0x1e); + + return 0; +} + +static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct si21xx_state *state = fe->demodulator_priv; + + s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | + si21_readreg(state, 0x25)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + + dprintk("%s\n", __func__); + + return 0; +} + +static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct si21xx_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->errmode != STATUS_UCBLOCKS) + *ucblocks = 0; + else + *ucblocks = (si21_readreg(state, 0x1d) << 8) | + si21_readreg(state, 0x1e); + + return 0; +} + +/* initiates a channel acquisition sequence + using the specified symbol rate and code rate */ +static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, + enum fe_code_rate crate) +{ + + struct si21xx_state *state = fe->demodulator_priv; + u8 coderates[] = { + 0x0, 0x01, 0x02, 0x04, 0x00, + 0x8, 0x10, 0x20, 0x00, 0x3f + }; + + u8 coderate_ptr; + int status; + u8 start_acq = 0x80; + u8 reg, regs[3]; + + dprintk("%s\n", __func__); + + status = PASS; + coderate_ptr = coderates[crate]; + + si21xx_set_symbolrate(fe, symbrate); + + /* write code rates to use in the Viterbi search */ + status |= si21_writeregs(state, + VIT_SRCH_CTRL_REG_1, + &coderate_ptr, 0x01); + + /* clear acq_start bit */ + status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); + reg &= ~start_acq; + status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); + + /* use new Carrier Frequency Offset Estimator (QuickLock) */ + regs[0] = 0xCB; + regs[1] = 0x40; + regs[2] = 0xCB; + + status |= si21_writeregs(state, + TWO_DB_BNDWDTH_THRSHLD_REG, + ®s[0], 0x03); + reg = 0x56; + status |= si21_writeregs(state, + LSA_CTRL_REG_1, ®, 1); + reg = 0x05; + status |= si21_writeregs(state, + BLIND_SCAN_CTRL_REG, ®, 1); + /* start automatic acq */ + status |= si21_writeregs(state, + ACQ_CTRL_REG_2, &start_acq, 0x01); + + return status; +} + +static int si21xx_set_frontend(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz) + datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/ + + /* in MHz */ + unsigned char coarse_tune_freq; + int fine_tune_freq; + unsigned char sample_rate = 0; + /* boolean */ + bool inband_interferer_ind; + + /* INTERMEDIATE VALUES */ + int icoarse_tune_freq; /* MHz */ + int ifine_tune_freq; /* MHz */ + unsigned int band_high; + unsigned int band_low; + unsigned int x1; + unsigned int x2; + int i; + bool inband_interferer_div2[ALLOWABLE_FS_COUNT]; + bool inband_interferer_div4[ALLOWABLE_FS_COUNT]; + int status = 0; + + /* allowable sample rates for ADC in MHz */ + int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, + 196, 204, 205, 206, 207 + }; + /* in MHz */ + int if_limit_high; + int if_limit_low; + int lnb_lo; + int lnb_uncertanity; + + int rf_freq; + int data_rate; + unsigned char regs[4]; + + dprintk("%s : FE_SET_FRONTEND\n", __func__); + + if (c->delivery_system != SYS_DVBS) { + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) + inband_interferer_div2[i] = inband_interferer_div4[i] = false; + + if_limit_high = -700000; + if_limit_low = -100000; + /* in MHz */ + lnb_lo = 0; + lnb_uncertanity = 0; + + rf_freq = 10 * c->frequency ; + data_rate = c->symbol_rate / 100; + + band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) + + (data_rate * 135)) / 200; + + band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) + + (data_rate * 135)) / 200; + + + icoarse_tune_freq = 100000 * + (((rf_freq - lnb_lo) - + (if_limit_low + if_limit_high) / 2) + / 100000); + + ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; + + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * + (afs[i] * 2500) + afs[i] * 2500; + + x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * + (afs[i] * 2500); + + if (((band_low < x1) && (x1 < band_high)) || + ((band_low < x2) && (x2 < band_high))) + inband_interferer_div4[i] = true; + + } + + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * + (afs[i] * 5000) + afs[i] * 5000; + + x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * + (afs[i] * 5000); + + if (((band_low < x1) && (x1 < band_high)) || + ((band_low < x2) && (x2 < band_high))) + inband_interferer_div2[i] = true; + } + + inband_interferer_ind = true; + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + if (inband_interferer_div2[i] || inband_interferer_div4[i]) { + inband_interferer_ind = false; + break; + } + } + + if (inband_interferer_ind) { + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + if (!inband_interferer_div2[i]) { + sample_rate = (u8) afs[i]; + break; + } + } + } else { + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + if ((inband_interferer_div2[i] || + !inband_interferer_div4[i])) { + sample_rate = (u8) afs[i]; + break; + } + } + + } + + if (sample_rate > 207 || sample_rate < 192) + sample_rate = 200; + + fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / + ((sample_rate) * 1000)); + + coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); + + regs[0] = sample_rate; + regs[1] = coarse_tune_freq; + regs[2] = fine_tune_freq & 0xFF; + regs[3] = fine_tune_freq >> 8 & 0xFF; + + status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); + + state->fs = sample_rate;/*ADC MHz*/ + si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); + + if (status) + return -EREMOTEIO; + + return 0; +} + +static int si21xx_sleep(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 regdata; + + dprintk("%s\n", __func__); + + si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); + regdata |= 1 << 6; + si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); + state->initialised = 0; + + return 0; +} + +static void si21xx_release(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + kfree(state); +} + +static const struct dvb_frontend_ops si21xx_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "SL SI21XX DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = si21xx_release, + .init = si21xx_init, + .sleep = si21xx_sleep, + .write = si21_write, + .read_status = si21_read_status, + .read_ber = si21_read_ber, + .read_signal_strength = si21_read_signal_strength, + .read_snr = si21_read_snr, + .read_ucblocks = si21_read_ucblocks, + .diseqc_send_master_cmd = si21xx_send_diseqc_msg, + .diseqc_send_burst = si21xx_send_diseqc_burst, + .set_tone = si21xx_set_tone, + .set_voltage = si21xx_set_voltage, + + .set_frontend = si21xx_set_frontend, +}; + +struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, + struct i2c_adapter *i2c) +{ + struct si21xx_state *state = NULL; + int id; + + dprintk("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + state->errmode = STATUS_BER; + + /* check if the demod is there */ + id = si21_readreg(state, SYSTEM_MODE_REG); + si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ + msleep(200); + id = si21_readreg(state, 0x00); + + /* register 0x00 contains: + 0x34 for SI2107 + 0x24 for SI2108 + 0x14 for SI2109 + 0x04 for SI2110 + */ + if (id != 0x04 && id != 0x14) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &si21xx_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(si21xx_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); +MODULE_AUTHOR("Igor M. Liplianin"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/si21xx.h b/drivers/media/dvb-frontends/si21xx.h new file mode 100644 index 0000000000..12fa1d5798 --- /dev/null +++ b/drivers/media/dvb-frontends/si21xx.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef SI21XX_H +#define SI21XX_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +struct si21xx_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* minimum delay before retuning */ + int min_delay_ms; +}; + +#if IS_REACHABLE(CONFIG_DVB_SI21XX) +extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *si21xx_attach( + const struct si21xx_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val) +{ + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} + +#endif diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c new file mode 100644 index 0000000000..4d7d0b8b51 --- /dev/null +++ b/drivers/media/dvb-frontends/sp2.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver + * + * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> + * + * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual + * DVB-S2 CI card (cimax2) with following copyrights: + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + */ + +#include "sp2_priv.h" + +static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) +{ + int ret; + struct i2c_client *client = s->client; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = ®, + .len = 1 + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len + } + }; + + ret = i2c_transfer(adap, msg, 2); + + if (ret != 2) { + dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n", + reg, ret); + if (ret < 0) + return ret; + else + return -EIO; + } + + dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n", + client->addr, reg, buf[0]); + + return 0; +} + +static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) +{ + int ret; + u8 buffer[35]; + struct i2c_client *client = s->client; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .buf = &buffer[0], + .len = len + 1 + }; + + if ((len + 1) > sizeof(buffer)) { + dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n", + reg, len); + return -EINVAL; + } + + buffer[0] = reg; + memcpy(&buffer[1], buf, len); + + ret = i2c_transfer(adap, &msg, 1); + + if (ret != 1) { + dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n", + reg, ret); + if (ret < 0) + return ret; + else + return -EIO; + } + + dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %*ph\n", + client->addr, reg, len, buf); + + return 0; +} + +static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs, + u8 read, int addr, u8 data) +{ + struct sp2 *s = en50221->data; + u8 store; + int mem, ret; + int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control; + + if (slot != 0) + return -EINVAL; + + /* + * change module access type between IO space and attribute memory + * when needed + */ + if (s->module_access_type != acs) { + ret = sp2_read_i2c(s, 0x00, &store, 1); + + if (ret) + return ret; + + store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0); + store |= acs; + + ret = sp2_write_i2c(s, 0x00, &store, 1); + if (ret) + return ret; + } + + s->module_access_type = acs; + + /* implementation of ci_op_cam is device specific */ + if (ci_op_cam) { + ret = ci_op_cam(s->priv, read, addr, data, &mem); + } else { + dev_err(&s->client->dev, "callback not defined"); + return -EINVAL; + } + + if (ret) + return ret; + + dev_dbg(&s->client->dev, "%s: slot=%d, addr=0x%04x, %s, data=%x", + (read) ? "read" : "write", slot, addr, + (acs == SP2_CI_ATTR_ACS) ? "attr" : "io", + (read) ? mem : data); + + if (read) + return mem; + else + return 0; + +} + +int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, + SP2_CI_RD, addr, 0); +} + +int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, + SP2_CI_WR, addr, data); +} + +int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, + SP2_CI_RD, addr, 0); +} + +int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data) +{ + return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, + SP2_CI_WR, addr, data); +} + +int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +{ + struct sp2 *s = en50221->data; + u8 buf; + int ret; + + dev_dbg(&s->client->dev, "slot: %d\n", slot); + + if (slot != 0) + return -EINVAL; + + /* RST on */ + buf = SP2_MOD_CTL_RST; + ret = sp2_write_i2c(s, 0x00, &buf, 1); + + if (ret) + return ret; + + usleep_range(500, 600); + + /* RST off */ + buf = 0x00; + ret = sp2_write_i2c(s, 0x00, &buf, 1); + + if (ret) + return ret; + + msleep(1000); + + return 0; +} + +int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +{ + struct sp2 *s = en50221->data; + + dev_dbg(&s->client->dev, "slot:%d\n", slot); + + /* not implemented */ + return 0; +} + +int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot) +{ + struct sp2 *s = en50221->data; + u8 buf; + + dev_dbg(&s->client->dev, "slot:%d\n", slot); + + if (slot != 0) + return -EINVAL; + + sp2_read_i2c(s, 0x00, &buf, 1); + + /* disable bypass and enable TS */ + buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN); + return sp2_write_i2c(s, 0, &buf, 1); +} + +int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open) +{ + struct sp2 *s = en50221->data; + u8 buf[2]; + int ret; + + dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open); + + /* + * CAM module INSERT/REMOVE processing. Slow operation because of i2c + * transfers. Throttle read to one per sec. + */ + if (time_after(jiffies, s->next_status_checked_time)) { + ret = sp2_read_i2c(s, 0x00, buf, 1); + s->next_status_checked_time = jiffies + msecs_to_jiffies(1000); + + if (ret) + return 0; + + if (buf[0] & SP2_MOD_CTL_DET) + s->status = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + else + s->status = 0; + } + + return s->status; +} + +static int sp2_init(struct sp2 *s) +{ + int ret = 0; + u8 buf; + u8 cimax_init[34] = { + 0x00, /* module A control*/ + 0x00, /* auto select mask high A */ + 0x00, /* auto select mask low A */ + 0x00, /* auto select pattern high A */ + 0x00, /* auto select pattern low A */ + 0x44, /* memory access time A, 600 ns */ + 0x00, /* invert input A */ + 0x00, /* RFU */ + 0x00, /* RFU */ + 0x00, /* module B control*/ + 0x00, /* auto select mask high B */ + 0x00, /* auto select mask low B */ + 0x00, /* auto select pattern high B */ + 0x00, /* auto select pattern low B */ + 0x44, /* memory access time B, 600 ns */ + 0x00, /* invert input B */ + 0x00, /* RFU */ + 0x00, /* RFU */ + 0x00, /* auto select mask high Ext */ + 0x00, /* auto select mask low Ext */ + 0x00, /* auto select pattern high Ext */ + 0x00, /* auto select pattern low Ext */ + 0x00, /* RFU */ + 0x02, /* destination - module A */ + 0x01, /* power control reg, VCC power on */ + 0x00, /* RFU */ + 0x00, /* int status read only */ + 0x00, /* Interrupt Mask Register */ + 0x05, /* EXTINT=active-high, INT=push-pull */ + 0x00, /* USCG1 */ + 0x04, /* ack active low */ + 0x00, /* LOCK = 0 */ + 0x22, /* unknown */ + 0x00, /* synchronization? */ + }; + + dev_dbg(&s->client->dev, "\n"); + + s->ca.owner = THIS_MODULE; + s->ca.read_attribute_mem = sp2_ci_read_attribute_mem; + s->ca.write_attribute_mem = sp2_ci_write_attribute_mem; + s->ca.read_cam_control = sp2_ci_read_cam_control; + s->ca.write_cam_control = sp2_ci_write_cam_control; + s->ca.slot_reset = sp2_ci_slot_reset; + s->ca.slot_shutdown = sp2_ci_slot_shutdown; + s->ca.slot_ts_enable = sp2_ci_slot_ts_enable; + s->ca.poll_slot_status = sp2_ci_poll_slot_status; + s->ca.data = s; + s->module_access_type = 0; + + /* initialize all regs */ + ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34); + if (ret) + goto err; + + /* lock registers */ + buf = 1; + ret = sp2_write_i2c(s, 0x1f, &buf, 1); + if (ret) + goto err; + + /* power on slots */ + ret = sp2_write_i2c(s, 0x18, &buf, 1); + if (ret) + goto err; + + ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1); + if (ret) + goto err; + + return 0; + +err: + dev_dbg(&s->client->dev, "init failed=%d\n", ret); + return ret; +} + +static int sp2_exit(struct i2c_client *client) +{ + struct sp2 *s; + + dev_dbg(&client->dev, "\n"); + + if (!client) + return 0; + + s = i2c_get_clientdata(client); + if (!s) + return 0; + + if (!s->ca.data) + return 0; + + dvb_ca_en50221_release(&s->ca); + + return 0; +} + +static int sp2_probe(struct i2c_client *client) +{ + struct sp2_config *cfg = client->dev.platform_data; + struct sp2 *s; + int ret; + + dev_dbg(&client->dev, "\n"); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto err; + } + + s->client = client; + s->dvb_adap = cfg->dvb_adap; + s->priv = cfg->priv; + s->ci_control = cfg->ci_control; + + i2c_set_clientdata(client, s); + + ret = sp2_init(s); + if (ret) + goto err; + + dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n"); + return 0; +err: + dev_dbg(&client->dev, "init failed=%d\n", ret); + kfree(s); + + return ret; +} + +static void sp2_remove(struct i2c_client *client) +{ + struct sp2 *s = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + sp2_exit(client); + kfree(s); +} + +static const struct i2c_device_id sp2_id[] = { + {"sp2", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sp2_id); + +static struct i2c_driver sp2_driver = { + .driver = { + .name = "sp2", + }, + .probe = sp2_probe, + .remove = sp2_remove, + .id_table = sp2_id, +}; + +module_i2c_driver(sp2_driver); + +MODULE_DESCRIPTION("CIMaX SP2/HF CI driver"); +MODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/sp2.h b/drivers/media/dvb-frontends/sp2.h new file mode 100644 index 0000000000..b5ace0d76e --- /dev/null +++ b/drivers/media/dvb-frontends/sp2.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * CIMaX SP2/HF CI driver + * + * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> + */ + +#ifndef SP2_H +#define SP2_H + +#include <media/dvb_ca_en50221.h> + +/* + * I2C address + * 0x40 (port 0) + * 0x41 (port 1) + */ +struct sp2_config { + /* dvb_adapter to attach the ci to */ + struct dvb_adapter *dvb_adap; + + /* function ci_control handles the device specific ci ops */ + void *ci_control; + + /* priv is passed back to function ci_control */ + void *priv; +}; + +extern int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr); +extern int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data); +extern int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr); +extern int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data); +extern int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot); +extern int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot); +extern int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot); +extern int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open); + +#endif diff --git a/drivers/media/dvb-frontends/sp2_priv.h b/drivers/media/dvb-frontends/sp2_priv.h new file mode 100644 index 0000000000..9423c5cf06 --- /dev/null +++ b/drivers/media/dvb-frontends/sp2_priv.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * CIMaX SP2/HF CI driver + * + * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> + */ + +#ifndef SP2_PRIV_H +#define SP2_PRIV_H + +#include "sp2.h" +#include <media/dvb_frontend.h> + +/* state struct */ +struct sp2 { + int status; + struct i2c_client *client; + struct dvb_adapter *dvb_adap; + struct dvb_ca_en50221 ca; + int module_access_type; + unsigned long next_status_checked_time; + void *priv; + void *ci_control; +}; + +#define SP2_CI_ATTR_ACS 0x00 +#define SP2_CI_IO_ACS 0x04 +#define SP2_CI_WR 0 +#define SP2_CI_RD 1 + +/* Module control register (0x00 module A, 0x09 module B) bits */ +#define SP2_MOD_CTL_DET 0x01 +#define SP2_MOD_CTL_AUTO 0x02 +#define SP2_MOD_CTL_ACS0 0x04 +#define SP2_MOD_CTL_ACS1 0x08 +#define SP2_MOD_CTL_HAD 0x10 +#define SP2_MOD_CTL_TSIEN 0x20 +#define SP2_MOD_CTL_TSOEN 0x40 +#define SP2_MOD_CTL_RST 0x80 + +#endif diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c new file mode 100644 index 0000000000..f59c0f9641 --- /dev/null +++ b/drivers/media/dvb-frontends/sp887x.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + Driver for the Spase sp887x demodulator +*/ + +/* + * This driver needs external firmware. Please use the command + * "<kerneldir>/scripts/get_dvb_firmware sp887x" to + * download/extract it, and then copy it to /usr/lib/hotplug/firmware + * or /lib/firmware (depending on configuration of firmware hotplug). + */ +#define SP887X_DEFAULT_FIRMWARE "dvb-fe-sp887x.fw" + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "sp887x.h" + + +struct sp887x_state { + struct i2c_adapter* i2c; + const struct sp887x_config* config; + struct dvb_frontend frontend; + + /* demodulator private data */ + u8 initialised:1; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "sp887x: " args); \ + } while (0) + +static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len) +{ + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len }; + int err; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + printk ("%s: i2c write error (addr %02x, err == %i)\n", + __func__, state->config->demod_address, err); + return -EREMOTEIO; + } + + return 0; +} + +static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data) +{ + u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 }; + int ret; + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { + /* + * in case of soft reset we ignore ACK errors... + */ + if (!(reg == 0xf1a && data == 0x000 && + (ret == -EREMOTEIO || ret == -EFAULT))) + { + printk("%s: writereg error (reg %03x, data %03x, ret == %i)\n", + __func__, reg & 0xffff, data & 0xffff, ret); + return ret; + } + } + + return 0; +} + +static int sp887x_readreg (struct sp887x_state* state, u16 reg) +{ + u8 b0 [] = { reg >> 8 , reg & 0xff }; + u8 b1 [2]; + int ret; + struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; + + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { + printk("%s: readreg error (ret == %i)\n", __func__, ret); + return -1; + } + + return (((b1[0] << 8) | b1[1]) & 0xfff); +} + +static void sp887x_microcontroller_stop (struct sp887x_state* state) +{ + dprintk("%s\n", __func__); + sp887x_writereg(state, 0xf08, 0x000); + sp887x_writereg(state, 0xf09, 0x000); + + /* microcontroller STOP */ + sp887x_writereg(state, 0xf00, 0x000); +} + +static void sp887x_microcontroller_start (struct sp887x_state* state) +{ + dprintk("%s\n", __func__); + sp887x_writereg(state, 0xf08, 0x000); + sp887x_writereg(state, 0xf09, 0x000); + + /* microcontroller START */ + sp887x_writereg(state, 0xf00, 0x001); +} + +static void sp887x_setup_agc (struct sp887x_state* state) +{ + /* setup AGC parameters */ + dprintk("%s\n", __func__); + sp887x_writereg(state, 0x33c, 0x054); + sp887x_writereg(state, 0x33b, 0x04c); + sp887x_writereg(state, 0x328, 0x000); + sp887x_writereg(state, 0x327, 0x005); + sp887x_writereg(state, 0x326, 0x001); + sp887x_writereg(state, 0x325, 0x001); + sp887x_writereg(state, 0x324, 0x001); + sp887x_writereg(state, 0x318, 0x050); + sp887x_writereg(state, 0x317, 0x3fe); + sp887x_writereg(state, 0x316, 0x001); + sp887x_writereg(state, 0x313, 0x005); + sp887x_writereg(state, 0x312, 0x002); + sp887x_writereg(state, 0x306, 0x000); + sp887x_writereg(state, 0x303, 0x000); +} + +#define BLOCKSIZE 30 +#define FW_SIZE 0x4000 +/* + * load firmware and setup MPEG interface... + */ +static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw) +{ + struct sp887x_state* state = fe->demodulator_priv; + u8 buf [BLOCKSIZE + 2]; + int i; + int fw_size = fw->size; + const unsigned char *mem = fw->data + 10; + + dprintk("%s\n", __func__); + + /* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */ + if (fw_size < FW_SIZE + 10) + return -ENODEV; + + /* soft reset */ + sp887x_writereg(state, 0xf1a, 0x000); + + sp887x_microcontroller_stop (state); + + printk ("%s: firmware upload... ", __func__); + + /* setup write pointer to -1 (end of memory) */ + /* bit 0x8000 in address is set to enable 13bit mode */ + sp887x_writereg(state, 0x8f08, 0x1fff); + + /* dummy write (wrap around to start of memory) */ + sp887x_writereg(state, 0x8f0a, 0x0000); + + for (i = 0; i < FW_SIZE; i += BLOCKSIZE) { + int c = BLOCKSIZE; + int err; + + if (c > FW_SIZE - i) + c = FW_SIZE - i; + + /* bit 0x8000 in address is set to enable 13bit mode */ + /* bit 0x4000 enables multibyte read/write transfers */ + /* write register is 0xf0a */ + buf[0] = 0xcf; + buf[1] = 0x0a; + + memcpy(&buf[2], mem + i, c); + + if ((err = i2c_writebytes (state, buf, c+2)) < 0) { + printk ("failed.\n"); + printk ("%s: i2c error (err == %i)\n", __func__, err); + return err; + } + } + + /* don't write RS bytes between packets */ + sp887x_writereg(state, 0xc13, 0x001); + + /* suppress clock if (!data_valid) */ + sp887x_writereg(state, 0xc14, 0x000); + + /* setup MPEG interface... */ + sp887x_writereg(state, 0xc1a, 0x872); + sp887x_writereg(state, 0xc1b, 0x001); + sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ + sp887x_writereg(state, 0xc1a, 0x871); + + /* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */ + sp887x_writereg(state, 0x301, 0x002); + + sp887x_setup_agc(state); + + /* bit 0x010: enable data valid signal */ + sp887x_writereg(state, 0xd00, 0x010); + sp887x_writereg(state, 0x0d1, 0x000); + return 0; +}; + +static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) +{ + int known_parameters = 1; + + *reg0xc05 = 0x000; + + switch (p->modulation) { + case QPSK: + break; + case QAM_16: + *reg0xc05 |= (1 << 10); + break; + case QAM_64: + *reg0xc05 |= (2 << 10); + break; + case QAM_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + } + + switch (p->hierarchy) { + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + *reg0xc05 |= (1 << 7); + break; + case HIERARCHY_2: + *reg0xc05 |= (2 << 7); + break; + case HIERARCHY_4: + *reg0xc05 |= (3 << 7); + break; + case HIERARCHY_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + } + + switch (p->code_rate_HP) { + case FEC_1_2: + break; + case FEC_2_3: + *reg0xc05 |= (1 << 3); + break; + case FEC_3_4: + *reg0xc05 |= (2 << 3); + break; + case FEC_5_6: + *reg0xc05 |= (3 << 3); + break; + case FEC_7_8: + *reg0xc05 |= (4 << 3); + break; + case FEC_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + } + + if (known_parameters) + *reg0xc05 |= (2 << 1); /* use specified parameters */ + else + *reg0xc05 |= (1 << 1); /* enable autoprobing */ + + return 0; +} + +/* + * estimates division of two 24bit numbers, + * derived from the ves1820/stv0299 driver code + */ +static void divide (int n, int d, int *quotient_i, int *quotient_f) +{ + unsigned int q, r; + + r = (n % d) << 8; + q = (r / d); + + if (quotient_i) + *quotient_i = q; + + if (quotient_f) { + r = (r % d) << 8; + q = (q << 8) | (r / d); + r = (r % d) << 8; + *quotient_f = (q << 8) | (r / d); + } +} + +static void sp887x_correct_offsets (struct sp887x_state* state, + struct dtv_frontend_properties *p, + int actual_freq) +{ + static const u32 srate_correction [] = { 1879617, 4544878, 8098561 }; + int bw_index; + int freq_offset = actual_freq - p->frequency; + int sysclock = 61003; //[kHz] + int ifreq = 36000000; + int freq; + int frequency_shift; + + switch (p->bandwidth_hz) { + default: + case 8000000: + bw_index = 0; + break; + case 7000000: + bw_index = 1; + break; + case 6000000: + bw_index = 2; + break; + } + + if (p->inversion == INVERSION_ON) + freq = ifreq - freq_offset; + else + freq = ifreq + freq_offset; + + divide(freq / 333, sysclock, NULL, &frequency_shift); + + if (p->inversion == INVERSION_ON) + frequency_shift = -frequency_shift; + + /* sample rate correction */ + sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12); + sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff); + + /* carrier offset correction */ + sp887x_writereg(state, 0x309, frequency_shift >> 12); + sp887x_writereg(state, 0x30a, frequency_shift & 0xfff); +} + +static int sp887x_setup_frontend_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct sp887x_state* state = fe->demodulator_priv; + unsigned actual_freq; + int err; + u16 val, reg0xc05; + + if (p->bandwidth_hz != 8000000 && + p->bandwidth_hz != 7000000 && + p->bandwidth_hz != 6000000) + return -EINVAL; + + if ((err = configure_reg0xc05(p, ®0xc05))) + return err; + + sp887x_microcontroller_stop(state); + + /* setup the PLL */ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + if (fe->ops.tuner_ops.get_frequency) { + fe->ops.tuner_ops.get_frequency(fe, &actual_freq); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } else { + actual_freq = p->frequency; + } + + /* read status reg in order to clear <pending irqs */ + sp887x_readreg(state, 0x200); + + sp887x_correct_offsets(state, p, actual_freq); + + /* filter for 6/7/8 Mhz channel */ + if (p->bandwidth_hz == 6000000) + val = 2; + else if (p->bandwidth_hz == 7000000) + val = 1; + else + val = 0; + + sp887x_writereg(state, 0x311, val); + + /* scan order: 2k first = 0, 8k first = 1 */ + if (p->transmission_mode == TRANSMISSION_MODE_2K) + sp887x_writereg(state, 0x338, 0x000); + else + sp887x_writereg(state, 0x338, 0x001); + + sp887x_writereg(state, 0xc05, reg0xc05); + + if (p->bandwidth_hz == 6000000) + val = 2 << 3; + else if (p->bandwidth_hz == 7000000) + val = 3 << 3; + else + val = 0 << 3; + + /* enable OFDM and SAW bits as lock indicators in sync register 0xf17, + * optimize algorithm for given bandwidth... + */ + sp887x_writereg(state, 0xf14, 0x160 | val); + sp887x_writereg(state, 0xf15, 0x000); + + sp887x_microcontroller_start(state); + return 0; +} + +static int sp887x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct sp887x_state* state = fe->demodulator_priv; + u16 snr12 = sp887x_readreg(state, 0xf16); + u16 sync0x200 = sp887x_readreg(state, 0x200); + u16 sync0xf17 = sp887x_readreg(state, 0xf17); + + *status = 0; + + if (snr12 > 0x00f) + *status |= FE_HAS_SIGNAL; + + //if (sync0x200 & 0x004) + // *status |= FE_HAS_SYNC | FE_HAS_CARRIER; + + //if (sync0x200 & 0x008) + // *status |= FE_HAS_VITERBI; + + if ((sync0xf17 & 0x00f) == 0x002) { + *status |= FE_HAS_LOCK; + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_CARRIER; + } + + if (sync0x200 & 0x001) { /* tuner adjustment requested...*/ + int steps = (sync0x200 >> 4) & 0x00f; + if (steps & 0x008) + steps = -steps; + dprintk("sp887x: implement tuner adjustment (%+i steps)!!\n", + steps); + } + + return 0; +} + +static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct sp887x_state* state = fe->demodulator_priv; + + *ber = (sp887x_readreg(state, 0xc08) & 0x3f) | + (sp887x_readreg(state, 0xc07) << 6); + sp887x_writereg(state, 0xc08, 0x000); + sp887x_writereg(state, 0xc07, 0x000); + if (*ber >= 0x3fff0) + *ber = ~0; + + return 0; +} + +static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct sp887x_state* state = fe->demodulator_priv; + + u16 snr12 = sp887x_readreg(state, 0xf16); + u32 signal = 3 * (snr12 << 4); + *strength = (signal < 0xffff) ? signal : 0xffff; + + return 0; +} + +static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct sp887x_state* state = fe->demodulator_priv; + + u16 snr12 = sp887x_readreg(state, 0xf16); + *snr = (snr12 << 4) | (snr12 >> 8); + + return 0; +} + +static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct sp887x_state* state = fe->demodulator_priv; + + *ucblocks = sp887x_readreg(state, 0xc0c); + if (*ucblocks == 0xfff) + *ucblocks = ~0; + + return 0; +} + +static int sp887x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct sp887x_state* state = fe->demodulator_priv; + + if (enable) { + return sp887x_writereg(state, 0x206, 0x001); + } else { + return sp887x_writereg(state, 0x206, 0x000); + } +} + +static int sp887x_sleep(struct dvb_frontend* fe) +{ + struct sp887x_state* state = fe->demodulator_priv; + + /* tristate TS output and disable interface pins */ + sp887x_writereg(state, 0xc18, 0x000); + + return 0; +} + +static int sp887x_init(struct dvb_frontend* fe) +{ + struct sp887x_state* state = fe->demodulator_priv; + const struct firmware *fw = NULL; + int ret; + + if (!state->initialised) { + /* request the firmware, this will block until someone uploads it */ + printk("sp887x: waiting for firmware upload (%s)...\n", SP887X_DEFAULT_FIRMWARE); + ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE); + if (ret) { + printk("sp887x: no firmware upload (timeout or file not found?)\n"); + return ret; + } + + ret = sp887x_initial_setup(fe, fw); + release_firmware(fw); + if (ret) { + printk("sp887x: writing firmware to device failed\n"); + return ret; + } + printk("sp887x: firmware upload complete\n"); + state->initialised = 1; + } + + /* enable TS output and interface pins */ + sp887x_writereg(state, 0xc18, 0x00d); + + return 0; +} + +static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 350; + fesettings->step_size = 166666*2; + fesettings->max_drift = (166666*2)+1; + return 0; +} + +static void sp887x_release(struct dvb_frontend* fe) +{ + struct sp887x_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops sp887x_ops; + +struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c) +{ + struct sp887x_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + + /* check if the demod is there */ + if (sp887x_readreg(state, 0x0200) < 0) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &sp887x_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops sp887x_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Spase SP887x DVB-T", + .frequency_min_hz = 50500 * kHz, + .frequency_max_hz = 858000 * kHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_RECOVER + }, + + .release = sp887x_release, + + .init = sp887x_init, + .sleep = sp887x_sleep, + .i2c_gate_ctrl = sp887x_i2c_gate_ctrl, + + .set_frontend = sp887x_setup_frontend_parameters, + .get_tune_settings = sp887x_get_tune_settings, + + .read_status = sp887x_read_status, + .read_ber = sp887x_read_ber, + .read_signal_strength = sp887x_read_signal_strength, + .read_snr = sp887x_read_snr, + .read_ucblocks = sp887x_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(sp887x_attach); diff --git a/drivers/media/dvb-frontends/sp887x.h b/drivers/media/dvb-frontends/sp887x.h new file mode 100644 index 0000000000..a680cc2237 --- /dev/null +++ b/drivers/media/dvb-frontends/sp887x.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + Driver for the Spase sp887x demodulator +*/ + +#ifndef SP887X_H +#define SP887X_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct sp887x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +#if IS_REACHABLE(CONFIG_DVB_SP887X) +extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_SP887X + +#endif // SP887X_H diff --git a/drivers/media/dvb-frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c new file mode 100644 index 0000000000..df89c33dac --- /dev/null +++ b/drivers/media/dvb-frontends/stb0899_algo.c @@ -0,0 +1,1522 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#include <linux/bitops.h> +#include "stb0899_drv.h" +#include "stb0899_priv.h" +#include "stb0899_reg.h" + +static inline u32 stb0899_do_div(u64 n, u32 d) +{ + /* wrap do_div() for ease of use */ + + do_div(n, d); + return n; +} + +#if 0 +/* These functions are currently unused */ +/* + * stb0899_calc_srate + * Compute symbol rate + */ +static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr) +{ + u64 tmp; + + /* srate = (SFR * master_clk) >> 20 */ + + /* sfr is of size 20 bit, stored with an offset of 4 bit */ + tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2]; + tmp &= ~0xf; + tmp *= master_clk; + tmp >>= 24; + + return tmp; +} + +/* + * stb0899_get_srate + * Get the current symbol rate + */ +static u32 stb0899_get_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + u8 sfr[3]; + + stb0899_read_regs(state, STB0899_SFRH, sfr, 3); + + return stb0899_calc_srate(internal->master_clk, sfr); +} +#endif + +/* + * stb0899_set_srate + * Set symbol frequency + * MasterClock: master clock frequency (hz) + * SymbolRate: symbol rate (bauds) + * return symbol frequency + */ +static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate) +{ + u32 tmp; + u8 sfr[3]; + + dprintk(state->verbose, FE_DEBUG, 1, "-->"); + /* + * in order to have the maximum precision, the symbol rate entered into + * the chip is computed as the closest value of the "true value". + * In this purpose, the symbol rate value is rounded (1 is added on the bit + * below the LSB ) + * + * srate = (SFR * master_clk) >> 20 + * <=> + * SFR = srate << 20 / master_clk + * + * rounded: + * SFR = (srate << 21 + master_clk) / (2 * master_clk) + * + * stored as 20 bit number with an offset of 4 bit: + * sfr = SFR << 4; + */ + + tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk); + tmp <<= 4; + + sfr[0] = tmp >> 16; + sfr[1] = tmp >> 8; + sfr[2] = tmp; + + stb0899_write_regs(state, STB0899_SFRH, sfr, 3); + + return srate; +} + +/* + * stb0899_calc_derot_time + * Compute the amount of time needed by the derotator to lock + * SymbolRate: Symbol rate + * return: derotator time constant (ms) + */ +static long stb0899_calc_derot_time(long srate) +{ + if (srate > 0) + return (100000 / (srate / 1000)); + else + return 0; +} + +/* + * stb0899_carr_width + * Compute the width of the carrier + * return: width of carrier (kHz or Mhz) + */ +long stb0899_carr_width(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + + return (internal->srate + (internal->srate * internal->rolloff) / 100); +} + +/* + * stb0899_first_subrange + * Compute the first subrange of the search + */ +static void stb0899_first_subrange(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + struct stb0899_config *config = state->config; + + int range = 0; + u32 bandwidth = 0; + + if (config->tuner_get_bandwidth) { + stb0899_i2c_gate_ctrl(&state->frontend, 1); + config->tuner_get_bandwidth(&state->frontend, &bandwidth); + stb0899_i2c_gate_ctrl(&state->frontend, 0); + range = bandwidth - stb0899_carr_width(state) / 2; + } + + if (range > 0) + internal->sub_range = min(internal->srch_range, range); + else + internal->sub_range = 0; + + internal->freq = params->freq; + internal->tuner_offst = 0L; + internal->sub_dir = 1; +} + +/* + * stb0899_check_tmg + * check for timing lock + * internal.Ttiming: time to wait for loop lock + */ +static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + int lock; + u8 reg; + s8 timing; + + msleep(internal->t_derot); + + stb0899_write_reg(state, STB0899_RTF, 0xf2); + reg = stb0899_read_reg(state, STB0899_TLIR); + lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg); + timing = stb0899_read_reg(state, STB0899_RTF); + + if (lock >= 42) { + if ((lock > 48) && (abs(timing) >= 110)) { + internal->status = ANALOGCARRIER; + dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !"); + } else { + internal->status = TIMINGOK; + dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !"); + } + } else { + internal->status = NOTIMING; + dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !"); + } + return internal->status; +} + +/* + * stb0899_search_tmg + * perform a fs/2 zig-zag to find timing + */ +static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + short int derot_step, derot_freq = 0, derot_limit, next_loop = 3; + int index = 0; + u8 cfr[2]; + + internal->status = NOTIMING; + + /* timing loop computation & symbol rate optimisation */ + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_step = (params->srate / 2L) / internal->mclk; + + while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) { + index++; + derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */ + + if (abs(derot_freq) > derot_limit) + next_loop--; + + if (next_loop) { + STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq)); + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + } + internal->direction = -internal->direction; /* Change zigzag direction */ + } + + if (internal->status == TIMINGOK) { + stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ + internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]); + dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq); + } + + return internal->status; +} + +/* + * stb0899_check_carrier + * Check for carrier found + */ +static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + u8 reg; + + msleep(internal->t_derot); /* wait for derotator ok */ + + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + reg = stb0899_read_reg(state, STB0899_DSTATUS); + dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg); + if (STB0899_GETFIELD(CARRIER_FOUND, reg)) { + internal->status = CARRIEROK; + dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !"); + } else { + internal->status = NOCARRIER; + dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !"); + } + + return internal->status; +} + +/* + * stb0899_search_carrier + * Search for a QPSK carrier with the derotator + */ +static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + + short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3; + int index = 0; + u8 cfr[2]; + u8 reg; + + internal->status = NOCARRIER; + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_freq = internal->derot_freq; + + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + do { + dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk); + if (stb0899_check_carrier(state) == NOCARRIER) { + index++; + last_derot_freq = derot_freq; + derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */ + + if(abs(derot_freq) > derot_limit) + next_loop--; + + if (next_loop) { + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq)); + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + } + } + + internal->direction = -internal->direction; /* Change zigzag direction */ + } while ((internal->status != CARRIEROK) && next_loop); + + if (internal->status == CARRIEROK) { + stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ + internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]); + dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq); + } else { + internal->derot_freq = last_derot_freq; + } + + return internal->status; +} + +/* + * stb0899_check_data + * Check for data found + */ +static enum stb0899_status stb0899_check_data(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + int lock = 0, index = 0, dataTime = 500, loop; + u8 reg; + + internal->status = NODATA; + + /* RESET FEC */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESACS, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + msleep(1); + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESACS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + if (params->srate <= 2000000) + dataTime = 2000; + else if (params->srate <= 5000000) + dataTime = 1500; + else if (params->srate <= 15000000) + dataTime = 1000; + else + dataTime = 500; + + /* clear previous failed END_LOOPVIT */ + stb0899_read_reg(state, STB0899_VSTATUS); + + stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ + while (1) { + /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ + reg = stb0899_read_reg(state, STB0899_VSTATUS); + lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg); + loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg); + + if (lock || loop || (index > dataTime)) + break; + index++; + } + + if (lock) { /* DATA LOCK indicator */ + internal->status = DATAOK; + dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !"); + } + + return internal->status; +} + +/* + * stb0899_search_data + * Search for a QPSK carrier with the derotator + */ +static enum stb0899_status stb0899_search_data(struct stb0899_state *state) +{ + short int derot_freq, derot_step, derot_limit, next_loop = 3; + u8 cfr[2]; + u8 reg; + int index = 1; + + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + derot_step = (params->srate / 4L) / internal->mclk; + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_freq = internal->derot_freq; + + do { + if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) { + + derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ + if (abs(derot_freq) > derot_limit) + next_loop--; + + if (next_loop) { + dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk); + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq)); + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + + stb0899_check_carrier(state); + index++; + } + } + internal->direction = -internal->direction; /* change zig zag direction */ + } while ((internal->status != DATAOK) && next_loop); + + if (internal->status == DATAOK) { + stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ + + /* store autodetected IQ swapping as default for DVB-S2 tuning */ + reg = stb0899_read_reg(state, STB0899_IQSWAP); + if (STB0899_GETFIELD(SYM, reg)) + internal->inversion = IQ_SWAP_ON; + else + internal->inversion = IQ_SWAP_OFF; + + internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]); + dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq); + } + + return internal->status; +} + +/* + * stb0899_check_range + * check if the found frequency is in the correct range + */ +static enum stb0899_status stb0899_check_range(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + int range_offst, tp_freq; + + range_offst = internal->srch_range / 2000; + tp_freq = internal->freq - (internal->derot_freq * internal->mclk) / 1000; + + if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) { + internal->status = RANGEOK; + dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !"); + } else { + internal->status = OUTOFRANGE; + dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !"); + } + + return internal->status; +} + +/* + * NextSubRange + * Compute the next subrange of the search + */ +static void next_sub_range(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + long old_sub_range; + + if (internal->sub_dir > 0) { + old_sub_range = internal->sub_range; + internal->sub_range = min((internal->srch_range / 2) - + (internal->tuner_offst + internal->sub_range / 2), + internal->sub_range); + + if (internal->sub_range < 0) + internal->sub_range = 0; + + internal->tuner_offst += (old_sub_range + internal->sub_range) / 2; + } + + internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000; + internal->sub_dir = -internal->sub_dir; +} + +/* + * stb0899_dvbs_algo + * Search for a signal, timing, carrier and data for a + * given frequency in a given range + */ +enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state) +{ + struct stb0899_params *params = &state->params; + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u8 bclc, reg; + u8 cfr[2]; + u8 eq_const[10]; + s32 clnI = 3; + u32 bandwidth = 0; + + /* BETA values rated @ 99MHz */ + s32 betaTab[5][4] = { + /* 5 10 20 30MBps */ + { 37, 34, 32, 31 }, /* QPSK 1/2 */ + { 37, 35, 33, 31 }, /* QPSK 2/3 */ + { 37, 35, 33, 31 }, /* QPSK 3/4 */ + { 37, 36, 33, 32 }, /* QPSK 5/6 */ + { 37, 36, 33, 32 } /* QPSK 7/8 */ + }; + + internal->direction = 1; + + stb0899_set_srate(state, internal->master_clk, params->srate); + /* Carrier loop optimization versus symbol rate for acquisition*/ + if (params->srate <= 5000000) { + stb0899_write_reg(state, STB0899_ACLC, 0x89); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x1c); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 0; + } else if (params->srate <= 15000000) { + stb0899_write_reg(state, STB0899_ACLC, 0xc9); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x22); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 1; + } else if(params->srate <= 25000000) { + stb0899_write_reg(state, STB0899_ACLC, 0x89); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x27); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 2; + } else { + stb0899_write_reg(state, STB0899_ACLC, 0xc8); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x29); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 3; + } + + dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition"); + /* Set the timing loop to acquisition */ + stb0899_write_reg(state, STB0899_RTC, 0x46); + stb0899_write_reg(state, STB0899_CFD, 0xee); + + /* !! WARNING !! + * Do not read any status variables while acquisition, + * If any needed, read before the acquisition starts + * querying status while acquiring causes the + * acquisition to go bad and hence no locks. + */ + dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d", + internal->derot_percent, params->srate, internal->mclk); + + /* Initial calculations */ + internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */ + internal->t_derot = stb0899_calc_derot_time(params->srate); + internal->t_data = 500; + + dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger"); + /* RESET Stream merger */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + /* + * Set KDIVIDER to an intermediate value between + * 1/2 and 7/8 for acquisition + */ + reg = stb0899_read_reg(state, STB0899_DEMAPVIT); + STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60); + stb0899_write_reg(state, STB0899_DEMAPVIT, reg); + + stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */ + stb0899_write_reg(state, STB0899_VITSYNC, 0x19); + + stb0899_first_subrange(state); + do { + /* Initialisations */ + cfr[0] = cfr[1] = 0; + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */ + + stb0899_write_reg(state, STB0899_RTF, 0); + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + internal->derot_freq = 0; + internal->status = NOAGC1; + + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + /* Move tuner to frequency */ + dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency"); + if (state->config->tuner_set_frequency) + state->config->tuner_set_frequency(&state->frontend, internal->freq); + + if (state->config->tuner_get_frequency) + state->config->tuner_get_frequency(&state->frontend, &internal->freq); + + msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */ + dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq); + internal->status = AGC1OK; + + /* There is signal in the band */ + if (config->tuner_get_bandwidth) + config->tuner_get_bandwidth(&state->frontend, &bandwidth); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + + if (params->srate <= bandwidth / 2) + stb0899_search_tmg(state); /* For low rates (SCPC) */ + else + stb0899_check_tmg(state); /* For high rates (MCPC) */ + + if (internal->status == TIMINGOK) { + dprintk(state->verbose, FE_DEBUG, 1, + "TIMING OK ! Derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */ + dprintk(state->verbose, FE_DEBUG, 1, + "CARRIER OK ! Derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + if (stb0899_search_data(state) == DATAOK) { /* Check for data */ + dprintk(state->verbose, FE_DEBUG, 1, + "DATA OK ! Derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + if (stb0899_check_range(state) == RANGEOK) { + dprintk(state->verbose, FE_DEBUG, 1, + "RANGE OK ! derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + internal->freq = params->freq - ((internal->derot_freq * internal->mclk) / 1000); + reg = stb0899_read_reg(state, STB0899_PLPARM); + internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg); + dprintk(state->verbose, FE_DEBUG, 1, + "freq=%d, internal resultant freq=%d", + params->freq, internal->freq); + + dprintk(state->verbose, FE_DEBUG, 1, + "internal puncture rate=%d", + internal->fecrate); + } + } + } + } + if (internal->status != RANGEOK) + next_sub_range(state); + + } while (internal->sub_range && internal->status != RANGEOK); + + /* Set the timing loop to tracking */ + stb0899_write_reg(state, STB0899_RTC, 0x33); + stb0899_write_reg(state, STB0899_CFD, 0xf7); + /* if locked and range ok, set Kdiv */ + if (internal->status == RANGEOK) { + dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !"); + stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */ + stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */ + + /* + * Carrier loop optimization versus + * symbol Rate/Puncture Rate for Tracking + */ + reg = stb0899_read_reg(state, STB0899_BCLC); + switch (internal->fecrate) { + case STB0899_FEC_1_2: /* 13 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_2_3: /* 18 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 44); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_3_4: /* 21 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 60); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_5_6: /* 24 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 75); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_6_7: /* 25 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 88); + stb0899_write_reg(state, STB0899_ACLC, 0x88); + stb0899_write_reg(state, STB0899_BCLC, 0x9a); + break; + case STB0899_FEC_7_8: /* 26 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 94); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate"); + break; + } + /* release stream merger RESET */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + /* disable carrier detector */ + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 0); + stb0899_write_reg(state, STB0899_CFD, reg); + + stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10); + } + + return internal->status; +} + +/* + * stb0899_dvbs2_config_uwp + * Configure UWP state machine + */ +static void stb0899_dvbs2_config_uwp(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + u32 uwp1, uwp2, uwp3, reg; + + uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); + uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2); + uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3); + + STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave); + STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant); + STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof); + + STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse); + STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine); + STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold); + + STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq); + STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track); + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO); + STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg); +} + +/* + * stb0899_dvbs2_config_csm_auto + * Set CSM to AUTO mode + */ +static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state) +{ + u32 reg; + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg); +} + +static long Log2Int(int number) +{ + int i; + + i = 0; + while ((1 << i) <= abs(number)) + i++; + + if (number == 0) + i = 1; + + return i - 1; +} + +/* + * stb0899_dvbs2_calc_srate + * compute BTR_NOM_FREQ for the symbol rate + */ +static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq; + u32 master_clk, srate; + + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + dec_rate = Log2Int(dec_ratio); + decim = 1 << dec_rate; + master_clk = internal->master_clk / 1000; + srate = internal->srate / 1000; + + if (decim <= 4) { + intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk; + remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; + } else { + intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100; + remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; + } + btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk); + + return btr_nom_freq; +} + +/* + * stb0899_dvbs2_calc_dev + * compute the correction to be applied to symbol rate + */ +static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + u32 dec_ratio, correction, master_clk, srate; + + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + + master_clk = internal->master_clk / 1000; /* for integer Calculation*/ + srate = internal->srate / 1000; /* for integer Calculation*/ + correction = (512 * master_clk) / (2 * dec_ratio * srate); + + return correction; +} + +/* + * stb0899_dvbs2_set_srate + * Set DVBS2 symbol rate + */ +static void stb0899_dvbs2_set_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + + u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq; + u32 correction, freq_adj, band_lim, decim_cntrl, reg; + u8 anti_alias; + + /*set decimation to 1*/ + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + dec_rate = Log2Int(dec_ratio); + + win_sel = 0; + if (dec_rate >= 5) + win_sel = dec_rate - 4; + + decim = (1 << dec_rate); + /* (FSamp/Fsymbol *100) for integer Calculation */ + f_sym = internal->master_clk / ((decim * internal->srate) / 1000); + + if (f_sym <= 2250) /* don't band limit signal going into btr block*/ + band_lim = 1; + else + band_lim = 0; /* band limit signal going into btr block*/ + + decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl); + + if (f_sym <= 3450) + anti_alias = 0; + else if (f_sym <= 4250) + anti_alias = 1; + else + anti_alias = 2; + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias); + btr_nom_freq = stb0899_dvbs2_calc_srate(state); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq); + + correction = stb0899_dvbs2_calc_dev(state); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); + STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); + + /* scale UWP+CSM frequency to sample rate*/ + freq_adj = internal->srate / (internal->master_clk / 4096); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj); +} + +/* + * stb0899_dvbs2_set_btr_loopbw + * set bit timing loop bandwidth as a percentage of the symbol rate + */ +static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u32 sym_peak = 23, zeta = 707, loopbw_percent = 60; + s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft; + s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift; + u32 decim, K, wn, k_direct, k_indirect; + u32 reg; + + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + dec_rate = Log2Int(dec_ratio); + decim = (1 << dec_rate); + + sym_peak *= 576000; + K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000); + K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/ + + if (K != 0) { + K = sym_peak / K; + wn = (4 * zeta * zeta) + 1000000; + wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/ + + k_indirect = (wn * wn) / K; /*kindirect = kindirect 10^-6*/ + k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/ + k_direct *= 100; + + k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2; + k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset; + k_btr1 = k_direct / (1 << k_direct_shift); + k_btr1 /= 10000; + + k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/; + k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset; + k_btr0 = k_indirect * (1 << (-k_indirect_shift)); + k_btr0 /= 1000000; + + k_btr2_rshft = 0; + if (k_btr0_rshft > 15) { + k_btr2_rshft = k_btr0_rshft - 15; + k_btr0_rshft = 15; + } + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN); + STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft); + STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0); + STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft); + STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1); + STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg); + } else + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f); +} + +/* + * stb0899_dvbs2_set_carr_freq + * set nominal frequency for carrier search + */ +static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk) +{ + struct stb0899_config *config = state->config; + s32 crl_nom_freq; + u32 reg; + + crl_nom_freq = (1 << config->crl_nco_bits) / master_clk; + crl_nom_freq *= carr_freq; + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); + STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); +} + +/* + * stb0899_dvbs2_init_calc + * Initialize DVBS2 UWP, CSM, carrier and timing loops + */ +static void stb0899_dvbs2_init_calc(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + s32 steps, step_size; + u32 range, reg; + + /* config uwp and csm */ + stb0899_dvbs2_config_uwp(state); + stb0899_dvbs2_config_csm_auto(state); + + /* initialize BTR */ + stb0899_dvbs2_set_srate(state); + stb0899_dvbs2_set_btr_loopbw(state); + + if (internal->srate / 1000000 >= 15) + step_size = (1 << 17) / 5; + else if (internal->srate / 1000000 >= 10) + step_size = (1 << 17) / 7; + else if (internal->srate / 1000000 >= 5) + step_size = (1 << 17) / 10; + else + step_size = (1 << 17) / 4; + + range = internal->srch_range / 1000000; + steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000)); + steps = (steps + 6) / 10; + steps = (steps == 0) ? 1 : steps; + if (steps % 2 == 0) + stb0899_dvbs2_set_carr_freq(state, internal->center_freq - + (internal->step_size * (internal->srate / 20000000)), + (internal->master_clk) / 1000000); + else + stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000); + + /*Set Carrier Search params (zigzag, num steps and freq step size*/ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2); + STB0899_SETFIELD_VAL(ZIGZAG, reg, 1); + STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps); + STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg); +} + +/* + * stb0899_dvbs2_btr_init + * initialize the timing loop + */ +static void stb0899_dvbs2_btr_init(struct stb0899_state *state) +{ + u32 reg; + + /* set enable BTR loopback */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); + STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1); + STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); + + /* fix btr freq accum at 0 */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000); + + /* fix btr freq accum at 0 */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000); +} + +/* + * stb0899_dvbs2_reacquire + * trigger a DVB-S2 acquisition + */ +static void stb0899_dvbs2_reacquire(struct stb0899_state *state) +{ + u32 reg = 0; + + /* demod soft reset */ + STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); + + /*Reset Timing Loop */ + stb0899_dvbs2_btr_init(state); + + /* reset Carrier loop */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30)); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30)); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0); + + /*release demod soft reset */ + reg = 0; + STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); + + /* start acquisition process */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0); + + /* equalizer Init */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1); + + /*Start equilizer */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); + STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0); + STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0); + STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05); + STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); + + /* RESET Packet delineator */ + stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a); +} + +/* + * stb0899_dvbs2_get_dmd_status + * get DVB-S2 Demod LOCK status + */ +static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout) +{ + int time = -10, lock = 0, uwp, csm; + u32 reg; + + do { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS); + dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg); + if (STB0899_GETFIELD(IF_AGC_LOCK, reg)) + dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !"); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); + dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg); + uwp = STB0899_GETFIELD(UWP_LOCK, reg); + csm = STB0899_GETFIELD(CSM_LOCK, reg); + if (uwp && csm) + lock = 1; + + time += 10; + msleep(10); + + } while ((!lock) && (time <= timeout)); + + if (lock) { + dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !"); + return DVBS2_DEMOD_LOCK; + } else { + return DVBS2_DEMOD_NOLOCK; + } +} + +/* + * stb0899_dvbs2_get_data_lock + * get FEC status + */ +static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout) +{ + int time = 0, lock = 0; + u8 reg; + + while ((!lock) && (time < timeout)) { + reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); + dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg); + lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg); + time++; + } + + return lock; +} + +/* + * stb0899_dvbs2_get_fec_status + * get DVB-S2 FEC LOCK status + */ +static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout) +{ + int time = 0, Locked; + + do { + Locked = stb0899_dvbs2_get_data_lock(state, 1); + time++; + msleep(1); + + } while ((!Locked) && (time < timeout)); + + if (Locked) { + dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !"); + return DVBS2_FEC_LOCK; + } else { + return DVBS2_FEC_NOLOCK; + } +} + + +/* + * stb0899_dvbs2_init_csm + * set parameters for manual mode + */ +static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod) +{ + struct stb0899_internal *internal = &state->internal; + + s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80; + s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr; + u32 csm1, csm2, csm3, csm4; + + if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) { + switch (modcod) { + case STB0899_QPSK_12: + gamma_acq = 25; + gamma_rho_acq = 2700; + gamma_trk = 12; + gamma_rho_trk = 180; + lock_count_thr = 8; + break; + case STB0899_QPSK_35: + gamma_acq = 38; + gamma_rho_acq = 7182; + gamma_trk = 14; + gamma_rho_trk = 308; + lock_count_thr = 8; + break; + case STB0899_QPSK_23: + gamma_acq = 42; + gamma_rho_acq = 9408; + gamma_trk = 17; + gamma_rho_trk = 476; + lock_count_thr = 8; + break; + case STB0899_QPSK_34: + gamma_acq = 53; + gamma_rho_acq = 16642; + gamma_trk = 19; + gamma_rho_trk = 646; + lock_count_thr = 8; + break; + case STB0899_QPSK_45: + gamma_acq = 53; + gamma_rho_acq = 17119; + gamma_trk = 22; + gamma_rho_trk = 880; + lock_count_thr = 8; + break; + case STB0899_QPSK_56: + gamma_acq = 55; + gamma_rho_acq = 19250; + gamma_trk = 23; + gamma_rho_trk = 989; + lock_count_thr = 8; + break; + case STB0899_QPSK_89: + gamma_acq = 60; + gamma_rho_acq = 24240; + gamma_trk = 24; + gamma_rho_trk = 1176; + lock_count_thr = 8; + break; + case STB0899_QPSK_910: + gamma_acq = 66; + gamma_rho_acq = 29634; + gamma_trk = 24; + gamma_rho_trk = 1176; + lock_count_thr = 8; + break; + default: + gamma_acq = 66; + gamma_rho_acq = 29634; + gamma_trk = 24; + gamma_rho_trk = 1176; + lock_count_thr = 8; + break; + } + + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2); + csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3); + csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4); + + STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl); + STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass); + STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain); + STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift); + STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift); + STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq); + STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq); + STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk); + STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk); + STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr); + STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr); + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4); + } +} + +/* + * stb0899_dvbs2_get_srate + * get DVB-S2 Symbol Rate + */ +static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg; + int div1, div2, rem1, rem2; + + div1 = config->btr_nco_bits / 2; + div2 = config->btr_nco_bits - div1 - 1; + + bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL); + decimRate = STB0899_GETFIELD(DECIM_RATE, reg); + decimRate = (1 << decimRate); + + intval1 = internal->master_clk / (1 << div1); + intval2 = bTrNomFreq / (1 << div2); + + rem1 = internal->master_clk % (1 << div1); + rem2 = bTrNomFreq % (1 << div2); + /* only for integer calculation */ + srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1)); + srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */ + + return srate; +} + +/* + * stb0899_dvbs2_algo + * Search for signal, timing, carrier and data for a given + * frequency in a given range + */ +enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + enum stb0899_modcod modcod; + + s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum; + int i = 0; + u32 reg, csm1; + + if (internal->srate <= 2000000) { + searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */ + FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */ + } else if (internal->srate <= 5000000) { + searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */ + FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */ + } else if (internal->srate <= 10000000) { + searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs <SYMB <= 10Mbs */ + FecLockTime = 80; /* 80 ms max time to lock FEC, 5Mbs< SYMB <= 10Mbs */ + } else if (internal->srate <= 15000000) { + searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs <SYMB <= 15Mbs */ + FecLockTime = 50; /* 50 ms max time to lock FEC, 10Mbs< SYMB <= 15Mbs */ + } else if (internal->srate <= 20000000) { + searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */ + FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */ + } else if (internal->srate <= 25000000) { + searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */ + FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ + } else { + searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */ + FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ + } + + /* Maintain Stream Merger in reset during acquisition */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + /* Move tuner to frequency */ + if (state->config->tuner_set_frequency) + state->config->tuner_set_frequency(&state->frontend, internal->freq); + if (state->config->tuner_get_frequency) + state->config->tuner_get_frequency(&state->frontend, &internal->freq); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + + /* Set IF AGC to acquisition */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); + STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4); + STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); + STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); + + /* Initialisation */ + stb0899_dvbs2_init_calc(state); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); + switch (internal->inversion) { + case IQ_SWAP_OFF: + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0); + break; + case IQ_SWAP_ON: + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); + break; + } + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); + stb0899_dvbs2_reacquire(state); + + /* Wait for demod lock (UWP and CSM) */ + internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); + + if (internal->status == DVBS2_DEMOD_LOCK) { + dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !"); + i = 0; + /* Demod Locked, check FEC status */ + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + + /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/ + while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { + /* Read the frequency offset*/ + offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); + + /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); + STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); + stb0899_dvbs2_reacquire(state); + internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); + i++; + } + } + + if (internal->status != DVBS2_FEC_LOCK) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); + iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg); + /* IQ Spectrum Inversion */ + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); + /* start acquistion process */ + stb0899_dvbs2_reacquire(state); + + /* Wait for demod lock (UWP and CSM) */ + internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); + if (internal->status == DVBS2_DEMOD_LOCK) { + i = 0; + /* Demod Locked, check FEC */ + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + /*try thrice for false locks, (UWP and CSM Locked but no FEC) */ + while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { + /* Read the frequency offset*/ + offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); + + /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); + STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); + + stb0899_dvbs2_reacquire(state); + internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); + i++; + } + } +/* + if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED) + pParams->IQLocked = !iqSpectrum; +*/ + } + if (internal->status == DVBS2_FEC_LOCK) { + dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !"); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); + modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; + pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; + + if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && + (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) && + (pilots == 1)) { + + stb0899_dvbs2_init_csm(state, pilots, modcod); + /* Wait for UWP,CSM and data LOCK 20ms max */ + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + + i = 0; + while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + i++; + } + } + + if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && + (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) && + (pilots == 1)) { + + /* Equalizer Disable update */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); + STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); + } + + /* slow down the Equalizer once locked */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); + STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); + + /* Store signal parameters */ + offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); + + offsetfreq = sign_extend32(offsetfreq, 29); + + offsetfreq = offsetfreq / ((1 << 30) / 1000); + offsetfreq *= (internal->master_clk / 1000000); + + /* store current inversion for next run */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); + if (STB0899_GETFIELD(SPECTRUM_INVERT, reg)) + internal->inversion = IQ_SWAP_ON; + else + internal->inversion = IQ_SWAP_OFF; + + internal->freq = internal->freq + offsetfreq; + internal->srate = stb0899_dvbs2_get_srate(state); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); + internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; + internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; + internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01; + + /* Set IF AGC to tracking */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); + STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3); + + /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/ + if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23)) + STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16); + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); + STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); + } + + /* Release Stream Merger Reset */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + return internal->status; +} diff --git a/drivers/media/dvb-frontends/stb0899_cfg.h b/drivers/media/dvb-frontends/stb0899_cfg.h new file mode 100644 index 0000000000..cc71902c46 --- /dev/null +++ b/drivers/media/dvb-frontends/stb0899_cfg.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STB0899_CFG_H +#define __STB0899_CFG_H + +static const struct stb0899_s2_reg stb0899_s2_init_2[] = { + + { STB0899_OFF0_DMD_STATUS , STB0899_BASE_DMD_STATUS , 0x00000103 }, /* DMDSTATUS */ + { STB0899_OFF0_CRL_FREQ , STB0899_BASE_CRL_FREQ , 0x3ed1da56 }, /* CRLFREQ */ + { STB0899_OFF0_BTR_FREQ , STB0899_BASE_BTR_FREQ , 0x00004000 }, /* BTRFREQ */ + { STB0899_OFF0_IF_AGC_GAIN , STB0899_BASE_IF_AGC_GAIN , 0x00002ade }, /* IFAGCGAIN */ + { STB0899_OFF0_BB_AGC_GAIN , STB0899_BASE_BB_AGC_GAIN , 0x000001bc }, /* BBAGCGAIN */ + { STB0899_OFF0_DC_OFFSET , STB0899_BASE_DC_OFFSET , 0x00000200 }, /* DCOFFSET */ + { STB0899_OFF0_DMD_CNTRL , STB0899_BASE_DMD_CNTRL , 0x0000000f }, /* DMDCNTRL */ + + { STB0899_OFF0_IF_AGC_CNTRL , STB0899_BASE_IF_AGC_CNTRL , 0x03fb4a20 }, /* IFAGCCNTRL */ + { STB0899_OFF0_BB_AGC_CNTRL , STB0899_BASE_BB_AGC_CNTRL , 0x00200c97 }, /* BBAGCCNTRL */ + + { STB0899_OFF0_CRL_CNTRL , STB0899_BASE_CRL_CNTRL , 0x00000016 }, /* CRLCNTRL */ + { STB0899_OFF0_CRL_PHS_INIT , STB0899_BASE_CRL_PHS_INIT , 0x00000000 }, /* CRLPHSINIT */ + { STB0899_OFF0_CRL_FREQ_INIT , STB0899_BASE_CRL_FREQ_INIT , 0x00000000 }, /* CRLFREQINIT */ + { STB0899_OFF0_CRL_LOOP_GAIN , STB0899_BASE_CRL_LOOP_GAIN , 0x00000000 }, /* CRLLOOPGAIN */ + { STB0899_OFF0_CRL_NOM_FREQ , STB0899_BASE_CRL_NOM_FREQ , 0x3ed097b6 }, /* CRLNOMFREQ */ + { STB0899_OFF0_CRL_SWP_RATE , STB0899_BASE_CRL_SWP_RATE , 0x00000000 }, /* CRLSWPRATE */ + { STB0899_OFF0_CRL_MAX_SWP , STB0899_BASE_CRL_MAX_SWP , 0x00000000 }, /* CRLMAXSWP */ + { STB0899_OFF0_CRL_LK_CNTRL , STB0899_BASE_CRL_LK_CNTRL , 0x0f6cdc01 }, /* CRLLKCNTRL */ + { STB0899_OFF0_DECIM_CNTRL , STB0899_BASE_DECIM_CNTRL , 0x00000000 }, /* DECIMCNTRL */ + { STB0899_OFF0_BTR_CNTRL , STB0899_BASE_BTR_CNTRL , 0x00003993 }, /* BTRCNTRL */ + { STB0899_OFF0_BTR_LOOP_GAIN , STB0899_BASE_BTR_LOOP_GAIN , 0x000d3c6f }, /* BTRLOOPGAIN */ + { STB0899_OFF0_BTR_PHS_INIT , STB0899_BASE_BTR_PHS_INIT , 0x00000000 }, /* BTRPHSINIT */ + { STB0899_OFF0_BTR_FREQ_INIT , STB0899_BASE_BTR_FREQ_INIT , 0x00000000 }, /* BTRFREQINIT */ + { STB0899_OFF0_BTR_NOM_FREQ , STB0899_BASE_BTR_NOM_FREQ , 0x0238e38e }, /* BTRNOMFREQ */ + { STB0899_OFF0_BTR_LK_CNTRL , STB0899_BASE_BTR_LK_CNTRL , 0x00000000 }, /* BTRLKCNTRL */ + { STB0899_OFF0_DECN_CNTRL , STB0899_BASE_DECN_CNTRL , 0x00000000 }, /* DECNCNTRL */ + { STB0899_OFF0_TP_CNTRL , STB0899_BASE_TP_CNTRL , 0x00000000 }, /* TPCNTRL */ + { STB0899_OFF0_TP_BUF_STATUS , STB0899_BASE_TP_BUF_STATUS , 0x00000000 }, /* TPBUFSTATUS */ + { STB0899_OFF0_DC_ESTIM , STB0899_BASE_DC_ESTIM , 0x00000000 }, /* DCESTIM */ + { STB0899_OFF0_FLL_CNTRL , STB0899_BASE_FLL_CNTRL , 0x00000000 }, /* FLLCNTRL */ + { STB0899_OFF0_FLL_FREQ_WD , STB0899_BASE_FLL_FREQ_WD , 0x40070000 }, /* FLLFREQWD */ + { STB0899_OFF0_ANTI_ALIAS_SEL , STB0899_BASE_ANTI_ALIAS_SEL , 0x00000001 }, /* ANTIALIASSEL */ + { STB0899_OFF0_RRC_ALPHA , STB0899_BASE_RRC_ALPHA , 0x00000002 }, /* RRCALPHA */ + { STB0899_OFF0_DC_ADAPT_LSHFT , STB0899_BASE_DC_ADAPT_LSHFT , 0x00000000 }, /* DCADAPTISHFT */ + { STB0899_OFF0_IMB_OFFSET , STB0899_BASE_IMB_OFFSET , 0x0000fe01 }, /* IMBOFFSET */ + { STB0899_OFF0_IMB_ESTIMATE , STB0899_BASE_IMB_ESTIMATE , 0x00000000 }, /* IMBESTIMATE */ + { STB0899_OFF0_IMB_CNTRL , STB0899_BASE_IMB_CNTRL , 0x00000001 }, /* IMBCNTRL */ + { STB0899_OFF0_IF_AGC_CNTRL2 , STB0899_BASE_IF_AGC_CNTRL2 , 0x00005007 }, /* IFAGCCNTRL2 */ + { STB0899_OFF0_DMD_CNTRL2 , STB0899_BASE_DMD_CNTRL2 , 0x00000002 }, /* DMDCNTRL2 */ + { STB0899_OFF0_TP_BUFFER , STB0899_BASE_TP_BUFFER , 0x00000000 }, /* TPBUFFER */ + { STB0899_OFF0_TP_BUFFER1 , STB0899_BASE_TP_BUFFER1 , 0x00000000 }, /* TPBUFFER1 */ + { STB0899_OFF0_TP_BUFFER2 , STB0899_BASE_TP_BUFFER2 , 0x00000000 }, /* TPBUFFER2 */ + { STB0899_OFF0_TP_BUFFER3 , STB0899_BASE_TP_BUFFER3 , 0x00000000 }, /* TPBUFFER3 */ + { STB0899_OFF0_TP_BUFFER4 , STB0899_BASE_TP_BUFFER4 , 0x00000000 }, /* TPBUFFER4 */ + { STB0899_OFF0_TP_BUFFER5 , STB0899_BASE_TP_BUFFER5 , 0x00000000 }, /* TPBUFFER5 */ + { STB0899_OFF0_TP_BUFFER6 , STB0899_BASE_TP_BUFFER6 , 0x00000000 }, /* TPBUFFER6 */ + { STB0899_OFF0_TP_BUFFER7 , STB0899_BASE_TP_BUFFER7 , 0x00000000 }, /* TPBUFFER7 */ + { STB0899_OFF0_TP_BUFFER8 , STB0899_BASE_TP_BUFFER8 , 0x00000000 }, /* TPBUFFER8 */ + { STB0899_OFF0_TP_BUFFER9 , STB0899_BASE_TP_BUFFER9 , 0x00000000 }, /* TPBUFFER9 */ + { STB0899_OFF0_TP_BUFFER10 , STB0899_BASE_TP_BUFFER10 , 0x00000000 }, /* TPBUFFER10 */ + { STB0899_OFF0_TP_BUFFER11 , STB0899_BASE_TP_BUFFER11 , 0x00000000 }, /* TPBUFFER11 */ + { STB0899_OFF0_TP_BUFFER12 , STB0899_BASE_TP_BUFFER12 , 0x00000000 }, /* TPBUFFER12 */ + { STB0899_OFF0_TP_BUFFER13 , STB0899_BASE_TP_BUFFER13 , 0x00000000 }, /* TPBUFFER13 */ + { STB0899_OFF0_TP_BUFFER14 , STB0899_BASE_TP_BUFFER14 , 0x00000000 }, /* TPBUFFER14 */ + { STB0899_OFF0_TP_BUFFER15 , STB0899_BASE_TP_BUFFER15 , 0x00000000 }, /* TPBUFFER15 */ + { STB0899_OFF0_TP_BUFFER16 , STB0899_BASE_TP_BUFFER16 , 0x0000ff00 }, /* TPBUFFER16 */ + { STB0899_OFF0_TP_BUFFER17 , STB0899_BASE_TP_BUFFER17 , 0x00000100 }, /* TPBUFFER17 */ + { STB0899_OFF0_TP_BUFFER18 , STB0899_BASE_TP_BUFFER18 , 0x0000fe01 }, /* TPBUFFER18 */ + { STB0899_OFF0_TP_BUFFER19 , STB0899_BASE_TP_BUFFER19 , 0x000004fe }, /* TPBUFFER19 */ + { STB0899_OFF0_TP_BUFFER20 , STB0899_BASE_TP_BUFFER20 , 0x0000cfe7 }, /* TPBUFFER20 */ + { STB0899_OFF0_TP_BUFFER21 , STB0899_BASE_TP_BUFFER21 , 0x0000bec6 }, /* TPBUFFER21 */ + { STB0899_OFF0_TP_BUFFER22 , STB0899_BASE_TP_BUFFER22 , 0x0000c2bf }, /* TPBUFFER22 */ + { STB0899_OFF0_TP_BUFFER23 , STB0899_BASE_TP_BUFFER23 , 0x0000c1c1 }, /* TPBUFFER23 */ + { STB0899_OFF0_TP_BUFFER24 , STB0899_BASE_TP_BUFFER24 , 0x0000c1c1 }, /* TPBUFFER24 */ + { STB0899_OFF0_TP_BUFFER25 , STB0899_BASE_TP_BUFFER25 , 0x0000c1c1 }, /* TPBUFFER25 */ + { STB0899_OFF0_TP_BUFFER26 , STB0899_BASE_TP_BUFFER26 , 0x0000c1c1 }, /* TPBUFFER26 */ + { STB0899_OFF0_TP_BUFFER27 , STB0899_BASE_TP_BUFFER27 , 0x0000c1c0 }, /* TPBUFFER27 */ + { STB0899_OFF0_TP_BUFFER28 , STB0899_BASE_TP_BUFFER28 , 0x0000c0c0 }, /* TPBUFFER28 */ + { STB0899_OFF0_TP_BUFFER29 , STB0899_BASE_TP_BUFFER29 , 0x0000c1c1 }, /* TPBUFFER29 */ + { STB0899_OFF0_TP_BUFFER30 , STB0899_BASE_TP_BUFFER30 , 0x0000c1c1 }, /* TPBUFFER30 */ + { STB0899_OFF0_TP_BUFFER31 , STB0899_BASE_TP_BUFFER31 , 0x0000c0c1 }, /* TPBUFFER31 */ + { STB0899_OFF0_TP_BUFFER32 , STB0899_BASE_TP_BUFFER32 , 0x0000c0c1 }, /* TPBUFFER32 */ + { STB0899_OFF0_TP_BUFFER33 , STB0899_BASE_TP_BUFFER33 , 0x0000c1c1 }, /* TPBUFFER33 */ + { STB0899_OFF0_TP_BUFFER34 , STB0899_BASE_TP_BUFFER34 , 0x0000c1c1 }, /* TPBUFFER34 */ + { STB0899_OFF0_TP_BUFFER35 , STB0899_BASE_TP_BUFFER35 , 0x0000c0c1 }, /* TPBUFFER35 */ + { STB0899_OFF0_TP_BUFFER36 , STB0899_BASE_TP_BUFFER36 , 0x0000c1c1 }, /* TPBUFFER36 */ + { STB0899_OFF0_TP_BUFFER37 , STB0899_BASE_TP_BUFFER37 , 0x0000c0c1 }, /* TPBUFFER37 */ + { STB0899_OFF0_TP_BUFFER38 , STB0899_BASE_TP_BUFFER38 , 0x0000c1c1 }, /* TPBUFFER38 */ + { STB0899_OFF0_TP_BUFFER39 , STB0899_BASE_TP_BUFFER39 , 0x0000c0c0 }, /* TPBUFFER39 */ + { STB0899_OFF0_TP_BUFFER40 , STB0899_BASE_TP_BUFFER40 , 0x0000c1c0 }, /* TPBUFFER40 */ + { STB0899_OFF0_TP_BUFFER41 , STB0899_BASE_TP_BUFFER41 , 0x0000c1c1 }, /* TPBUFFER41 */ + { STB0899_OFF0_TP_BUFFER42 , STB0899_BASE_TP_BUFFER42 , 0x0000c0c0 }, /* TPBUFFER42 */ + { STB0899_OFF0_TP_BUFFER43 , STB0899_BASE_TP_BUFFER43 , 0x0000c1c0 }, /* TPBUFFER43 */ + { STB0899_OFF0_TP_BUFFER44 , STB0899_BASE_TP_BUFFER44 , 0x0000c0c1 }, /* TPBUFFER44 */ + { STB0899_OFF0_TP_BUFFER45 , STB0899_BASE_TP_BUFFER45 , 0x0000c1be }, /* TPBUFFER45 */ + { STB0899_OFF0_TP_BUFFER46 , STB0899_BASE_TP_BUFFER46 , 0x0000c1c9 }, /* TPBUFFER46 */ + { STB0899_OFF0_TP_BUFFER47 , STB0899_BASE_TP_BUFFER47 , 0x0000c0da }, /* TPBUFFER47 */ + { STB0899_OFF0_TP_BUFFER48 , STB0899_BASE_TP_BUFFER48 , 0x0000c0ba }, /* TPBUFFER48 */ + { STB0899_OFF0_TP_BUFFER49 , STB0899_BASE_TP_BUFFER49 , 0x0000c1c4 }, /* TPBUFFER49 */ + { STB0899_OFF0_TP_BUFFER50 , STB0899_BASE_TP_BUFFER50 , 0x0000c1bf }, /* TPBUFFER50 */ + { STB0899_OFF0_TP_BUFFER51 , STB0899_BASE_TP_BUFFER51 , 0x0000c0c1 }, /* TPBUFFER51 */ + { STB0899_OFF0_TP_BUFFER52 , STB0899_BASE_TP_BUFFER52 , 0x0000c1c0 }, /* TPBUFFER52 */ + { STB0899_OFF0_TP_BUFFER53 , STB0899_BASE_TP_BUFFER53 , 0x0000c0c1 }, /* TPBUFFER53 */ + { STB0899_OFF0_TP_BUFFER54 , STB0899_BASE_TP_BUFFER54 , 0x0000c1c1 }, /* TPBUFFER54 */ + { STB0899_OFF0_TP_BUFFER55 , STB0899_BASE_TP_BUFFER55 , 0x0000c1c1 }, /* TPBUFFER55 */ + { STB0899_OFF0_TP_BUFFER56 , STB0899_BASE_TP_BUFFER56 , 0x0000c1c1 }, /* TPBUFFER56 */ + { STB0899_OFF0_TP_BUFFER57 , STB0899_BASE_TP_BUFFER57 , 0x0000c1c1 }, /* TPBUFFER57 */ + { STB0899_OFF0_TP_BUFFER58 , STB0899_BASE_TP_BUFFER58 , 0x0000c1c1 }, /* TPBUFFER58 */ + { STB0899_OFF0_TP_BUFFER59 , STB0899_BASE_TP_BUFFER59 , 0x0000c1c1 }, /* TPBUFFER59 */ + { STB0899_OFF0_TP_BUFFER60 , STB0899_BASE_TP_BUFFER60 , 0x0000c1c1 }, /* TPBUFFER60 */ + { STB0899_OFF0_TP_BUFFER61 , STB0899_BASE_TP_BUFFER61 , 0x0000c1c1 }, /* TPBUFFER61 */ + { STB0899_OFF0_TP_BUFFER62 , STB0899_BASE_TP_BUFFER62 , 0x0000c1c1 }, /* TPBUFFER62 */ + { STB0899_OFF0_TP_BUFFER63 , STB0899_BASE_TP_BUFFER63 , 0x0000c1c0 }, /* TPBUFFER63 */ + { STB0899_OFF0_RESET_CNTRL , STB0899_BASE_RESET_CNTRL , 0x00000001 }, /* RESETCNTRL */ + { STB0899_OFF0_ACM_ENABLE , STB0899_BASE_ACM_ENABLE , 0x00005654 }, /* ACMENABLE */ + { STB0899_OFF0_DESCR_CNTRL , STB0899_BASE_DESCR_CNTRL , 0x00000000 }, /* DESCRCNTRL */ + { STB0899_OFF0_CSM_CNTRL1 , STB0899_BASE_CSM_CNTRL1 , 0x00020019 }, /* CSMCNTRL1 */ + { STB0899_OFF0_CSM_CNTRL2 , STB0899_BASE_CSM_CNTRL2 , 0x004b3237 }, /* CSMCNTRL2 */ + { STB0899_OFF0_CSM_CNTRL3 , STB0899_BASE_CSM_CNTRL3 , 0x0003dd17 }, /* CSMCNTRL3 */ + { STB0899_OFF0_CSM_CNTRL4 , STB0899_BASE_CSM_CNTRL4 , 0x00008008 }, /* CSMCNTRL4 */ + { STB0899_OFF0_UWP_CNTRL1 , STB0899_BASE_UWP_CNTRL1 , 0x002a3106 }, /* UWPCNTRL1 */ + { STB0899_OFF0_UWP_CNTRL2 , STB0899_BASE_UWP_CNTRL2 , 0x0006140a }, /* UWPCNTRL2 */ + { STB0899_OFF0_UWP_STAT1 , STB0899_BASE_UWP_STAT1 , 0x00008000 }, /* UWPSTAT1 */ + { STB0899_OFF0_UWP_STAT2 , STB0899_BASE_UWP_STAT2 , 0x00000000 }, /* UWPSTAT2 */ + { STB0899_OFF0_DMD_STAT2 , STB0899_BASE_DMD_STAT2 , 0x00000000 }, /* DMDSTAT2 */ + { STB0899_OFF0_FREQ_ADJ_SCALE , STB0899_BASE_FREQ_ADJ_SCALE , 0x00000471 }, /* FREQADJSCALE */ + { STB0899_OFF0_UWP_CNTRL3 , STB0899_BASE_UWP_CNTRL3 , 0x017b0465 }, /* UWPCNTRL3 */ + { STB0899_OFF0_SYM_CLK_SEL , STB0899_BASE_SYM_CLK_SEL , 0x00000002 }, /* SYMCLKSEL */ + { STB0899_OFF0_SOF_SRCH_TO , STB0899_BASE_SOF_SRCH_TO , 0x00196464 }, /* SOFSRCHTO */ + { STB0899_OFF0_ACQ_CNTRL1 , STB0899_BASE_ACQ_CNTRL1 , 0x00000603 }, /* ACQCNTRL1 */ + { STB0899_OFF0_ACQ_CNTRL2 , STB0899_BASE_ACQ_CNTRL2 , 0x02046666 }, /* ACQCNTRL2 */ + { STB0899_OFF0_ACQ_CNTRL3 , STB0899_BASE_ACQ_CNTRL3 , 0x10046583 }, /* ACQCNTRL3 */ + { STB0899_OFF0_FE_SETTLE , STB0899_BASE_FE_SETTLE , 0x00010404 }, /* FESETTLE */ + { STB0899_OFF0_AC_DWELL , STB0899_BASE_AC_DWELL , 0x0002aa8a }, /* ACDWELL */ + { STB0899_OFF0_ACQUIRE_TRIG , STB0899_BASE_ACQUIRE_TRIG , 0x00000000 }, /* ACQUIRETRIG */ + { STB0899_OFF0_LOCK_LOST , STB0899_BASE_LOCK_LOST , 0x00000001 }, /* LOCKLOST */ + { STB0899_OFF0_ACQ_STAT1 , STB0899_BASE_ACQ_STAT1 , 0x00000500 }, /* ACQSTAT1 */ + { STB0899_OFF0_ACQ_TIMEOUT , STB0899_BASE_ACQ_TIMEOUT , 0x0028a0a0 }, /* ACQTIMEOUT */ + { STB0899_OFF0_ACQ_TIME , STB0899_BASE_ACQ_TIME , 0x00000000 }, /* ACQTIME */ + { STB0899_OFF0_FINAL_AGC_CNTRL , STB0899_BASE_FINAL_AGC_CNTRL , 0x00800c17 }, /* FINALAGCCNTRL*/ + { STB0899_OFF0_FINAL_AGC_GAIN , STB0899_BASE_FINAL_AGC_GAIN , 0x00000000 }, /* FINALAGCCGAIN*/ + { STB0899_OFF0_EQUALIZER_INIT , STB0899_BASE_EQUALIZER_INIT , 0x00000000 }, /* EQUILIZERINIT*/ + { STB0899_OFF0_EQ_CNTRL , STB0899_BASE_EQ_CNTRL , 0x00054802 }, /* EQCNTL */ + { STB0899_OFF0_EQ_I_INIT_COEFF_0, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF0 */ + { STB0899_OFF1_EQ_I_INIT_COEFF_1, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF1 */ + { STB0899_OFF2_EQ_I_INIT_COEFF_2, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF2 */ + { STB0899_OFF3_EQ_I_INIT_COEFF_3, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF3 */ + { STB0899_OFF4_EQ_I_INIT_COEFF_4, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF4 */ + { STB0899_OFF5_EQ_I_INIT_COEFF_5, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000400 }, /* EQIINITCOEFF5 */ + { STB0899_OFF6_EQ_I_INIT_COEFF_6, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF6 */ + { STB0899_OFF7_EQ_I_INIT_COEFF_7, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF7 */ + { STB0899_OFF8_EQ_I_INIT_COEFF_8, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF8 */ + { STB0899_OFF9_EQ_I_INIT_COEFF_9, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF9 */ + { STB0899_OFFa_EQ_I_INIT_COEFF_10,STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF10*/ + { STB0899_OFF0_EQ_Q_INIT_COEFF_0, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF0 */ + { STB0899_OFF1_EQ_Q_INIT_COEFF_1, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF1 */ + { STB0899_OFF2_EQ_Q_INIT_COEFF_2, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF2 */ + { STB0899_OFF3_EQ_Q_INIT_COEFF_3, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF3 */ + { STB0899_OFF4_EQ_Q_INIT_COEFF_4, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF4 */ + { STB0899_OFF5_EQ_Q_INIT_COEFF_5, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF5 */ + { STB0899_OFF6_EQ_Q_INIT_COEFF_6, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF6 */ + { STB0899_OFF7_EQ_Q_INIT_COEFF_7, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF7 */ + { STB0899_OFF8_EQ_Q_INIT_COEFF_8, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF8 */ + { STB0899_OFF9_EQ_Q_INIT_COEFF_9, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF9 */ + { STB0899_OFFa_EQ_Q_INIT_COEFF_10,STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF10*/ + { STB0899_OFF0_EQ_I_OUT_COEFF_0 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT0 */ + { STB0899_OFF1_EQ_I_OUT_COEFF_1 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT1 */ + { STB0899_OFF2_EQ_I_OUT_COEFF_2 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT2 */ + { STB0899_OFF3_EQ_I_OUT_COEFF_3 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT3 */ + { STB0899_OFF4_EQ_I_OUT_COEFF_4 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT4 */ + { STB0899_OFF5_EQ_I_OUT_COEFF_5 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT5 */ + { STB0899_OFF6_EQ_I_OUT_COEFF_6 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT6 */ + { STB0899_OFF7_EQ_I_OUT_COEFF_7 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT7 */ + { STB0899_OFF8_EQ_I_OUT_COEFF_8 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT8 */ + { STB0899_OFF9_EQ_I_OUT_COEFF_9 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT9 */ + { STB0899_OFFa_EQ_I_OUT_COEFF_10,STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT10*/ + { STB0899_OFF0_EQ_Q_OUT_COEFF_0 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT0 */ + { STB0899_OFF1_EQ_Q_OUT_COEFF_1 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT1 */ + { STB0899_OFF2_EQ_Q_OUT_COEFF_2 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT2 */ + { STB0899_OFF3_EQ_Q_OUT_COEFF_3 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT3 */ + { STB0899_OFF4_EQ_Q_OUT_COEFF_4 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT4 */ + { STB0899_OFF5_EQ_Q_OUT_COEFF_5 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT5 */ + { STB0899_OFF6_EQ_Q_OUT_COEFF_6 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT6 */ + { STB0899_OFF7_EQ_Q_OUT_COEFF_7 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT7 */ + { STB0899_OFF8_EQ_Q_OUT_COEFF_8 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT8 */ + { STB0899_OFF9_EQ_Q_OUT_COEFF_9 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT9 */ + { STB0899_OFFa_EQ_Q_OUT_COEFF_10, STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT10*/ + { 0xffff , 0xffffffff , 0xffffffff }, +}; +static const struct stb0899_s2_reg stb0899_s2_init_4[] = { + { STB0899_OFF0_BLOCK_LNGTH , STB0899_BASE_BLOCK_LNGTH , 0x00000008 }, /* BLOCKLNGTH */ + { STB0899_OFF0_ROW_STR , STB0899_BASE_ROW_STR , 0x000000b4 }, /* ROWSTR */ + { STB0899_OFF0_BN_END_ADDR , STB0899_BASE_BN_END_ADDR , 0x000004b5 }, /* BNANDADDR */ + { STB0899_OFF0_CN_END_ADDR , STB0899_BASE_CN_END_ADDR , 0x00000b4b }, /* CNANDADDR */ + { STB0899_OFF0_INFO_LENGTH , STB0899_BASE_INFO_LENGTH , 0x00000078 }, /* INFOLENGTH */ + { STB0899_OFF0_BOT_ADDR , STB0899_BASE_BOT_ADDR , 0x000001e0 }, /* BOT_ADDR */ + { STB0899_OFF0_BCH_BLK_LN , STB0899_BASE_BCH_BLK_LN , 0x0000a8c0 }, /* BCHBLKLN */ + { STB0899_OFF0_BCH_T , STB0899_BASE_BCH_T , 0x0000000c }, /* BCHT */ + { STB0899_OFF0_CNFG_MODE , STB0899_BASE_CNFG_MODE , 0x00000001 }, /* CNFGMODE */ + { STB0899_OFF0_LDPC_STAT , STB0899_BASE_LDPC_STAT , 0x0000000d }, /* LDPCSTAT */ + { STB0899_OFF0_ITER_SCALE , STB0899_BASE_ITER_SCALE , 0x00000040 }, /* ITERSCALE */ + { STB0899_OFF0_INPUT_MODE , STB0899_BASE_INPUT_MODE , 0x00000000 }, /* INPUTMODE */ + { STB0899_OFF0_LDPCDECRST , STB0899_BASE_LDPCDECRST , 0x00000000 }, /* LDPCDECRST */ + { STB0899_OFF0_CLK_PER_BYTE_RW , STB0899_BASE_CLK_PER_BYTE_RW , 0x00000008 }, /* CLKPERBYTE */ + { STB0899_OFF0_BCH_ERRORS , STB0899_BASE_BCH_ERRORS , 0x00000000 }, /* BCHERRORS */ + { STB0899_OFF0_LDPC_ERRORS , STB0899_BASE_LDPC_ERRORS , 0x00000000 }, /* LDPCERRORS */ + { STB0899_OFF0_BCH_MODE , STB0899_BASE_BCH_MODE , 0x00000000 }, /* BCHMODE */ + { STB0899_OFF0_ERR_ACC_PER , STB0899_BASE_ERR_ACC_PER , 0x00000008 }, /* ERRACCPER */ + { STB0899_OFF0_BCH_ERR_ACC , STB0899_BASE_BCH_ERR_ACC , 0x00000000 }, /* BCHERRACC */ + { STB0899_OFF0_FEC_TP_SEL , STB0899_BASE_FEC_TP_SEL , 0x00000000 }, /* FECTPSEL */ + { 0xffff , 0xffffffff , 0xffffffff }, +}; + +static const struct stb0899_s1_reg stb0899_s1_init_5[] = { + { STB0899_TSTCK , 0x00 }, + { STB0899_TSTRES , 0x00 }, + { STB0899_TSTOUT , 0x00 }, + { STB0899_TSTIN , 0x00 }, + { STB0899_TSTSYS , 0x00 }, + { STB0899_TSTCHIP , 0x00 }, + { STB0899_TSTFREE , 0x00 }, + { STB0899_TSTI2C , 0x00 }, + { STB0899_BITSPEEDM , 0x00 }, + { STB0899_BITSPEEDL , 0x00 }, + { STB0899_TBUSBIT , 0x00 }, + { STB0899_TSTDIS , 0x00 }, + { STB0899_TSTDISRX , 0x00 }, + { STB0899_TSTJETON , 0x00 }, + { STB0899_TSTDCADJ , 0x00 }, + { STB0899_TSTAGC1 , 0x00 }, + { STB0899_TSTAGC1N , 0x00 }, + { STB0899_TSTPOLYPH , 0x00 }, + { STB0899_TSTR , 0x00 }, + { STB0899_TSTAGC2 , 0x00 }, + { STB0899_TSTCTL1 , 0x00 }, + { STB0899_TSTCTL2 , 0x00 }, + { STB0899_TSTCTL3 , 0x00 }, + { STB0899_TSTDEMAP , 0x00 }, + { STB0899_TSTDEMAP2 , 0x00 }, + { STB0899_TSTDEMMON , 0x00 }, + { STB0899_TSTRATE , 0x00 }, + { STB0899_TSTSELOUT , 0x00 }, + { STB0899_TSYNC , 0x00 }, + { STB0899_TSTERR , 0x00 }, + { STB0899_TSTRAM1 , 0x00 }, + { STB0899_TSTVSELOUT , 0x00 }, + { STB0899_TSTFORCEIN , 0x00 }, + { STB0899_TSTRS1 , 0x00 }, + { STB0899_TSTRS2 , 0x00 }, + { STB0899_TSTRS3 , 0x00 }, + { STB0899_GHOSTREG , 0x81 }, + { 0xffff , 0xff }, +}; + +#define STB0899_DVBS2_ESNO_AVE 3 +#define STB0899_DVBS2_ESNO_QUANT 32 +#define STB0899_DVBS2_AVFRAMES_COARSE 10 +#define STB0899_DVBS2_AVFRAMES_FINE 20 +#define STB0899_DVBS2_MISS_THRESHOLD 6 +#define STB0899_DVBS2_UWP_THRESHOLD_ACQ 1125 +#define STB0899_DVBS2_UWP_THRESHOLD_TRACK 758 +#define STB0899_DVBS2_UWP_THRESHOLD_SOF 1350 +#define STB0899_DVBS2_SOF_SEARCH_TIMEOUT 1664100 + +#define STB0899_DVBS2_BTR_NCO_BITS 28 +#define STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET 15 +#define STB0899_DVBS2_CRL_NCO_BITS 30 +#define STB0899_DVBS2_LDPC_MAX_ITER 70 + +#endif //__STB0899_CFG_H diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c new file mode 100644 index 0000000000..2f4d8fb400 --- /dev/null +++ b/drivers/media/dvb-frontends/stb0899_drv.c @@ -0,0 +1,1645 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +#include "stb0899_drv.h" +#include "stb0899_priv.h" +#include "stb0899_reg.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +static unsigned int verbose = 0;//1; +module_param(verbose, int, 0644); + +/* C/N in dB/10, NIRM/NIRL */ +static const struct stb0899_tab stb0899_cn_tab[] = { + { 200, 2600 }, + { 190, 2700 }, + { 180, 2860 }, + { 170, 3020 }, + { 160, 3210 }, + { 150, 3440 }, + { 140, 3710 }, + { 130, 4010 }, + { 120, 4360 }, + { 110, 4740 }, + { 100, 5190 }, + { 90, 5670 }, + { 80, 6200 }, + { 70, 6770 }, + { 60, 7360 }, + { 50, 7970 }, + { 40, 8250 }, + { 30, 9000 }, + { 20, 9450 }, + { 15, 9600 }, +}; + +/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10. + * As measured, connected to a modulator. + * -8.0 to -50.0 dBm directly connected, + * -52.0 to -74.8 with extra attenuation. + * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm. + * Crude linear extrapolation below -84.8dBm and above -8.0dBm. + */ +static const struct stb0899_tab stb0899_dvbsrf_tab[] = { + { -750, -128 }, + { -748, -94 }, + { -745, -92 }, + { -735, -90 }, + { -720, -87 }, + { -670, -77 }, + { -640, -70 }, + { -610, -62 }, + { -600, -60 }, + { -590, -56 }, + { -560, -41 }, + { -540, -25 }, + { -530, -17 }, + { -520, -11 }, + { -500, 1 }, + { -490, 6 }, + { -480, 10 }, + { -440, 22 }, + { -420, 27 }, + { -400, 31 }, + { -380, 34 }, + { -340, 40 }, + { -320, 43 }, + { -280, 48 }, + { -250, 52 }, + { -230, 55 }, + { -180, 61 }, + { -140, 66 }, + { -90, 73 }, + { -80, 74 }, + { 500, 127 } +}; + +/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10. + * As measured, connected to a modulator. + * -8.0 to -50.1 dBm directly connected, + * -53.0 to -76.6 with extra attenuation. + * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm. + * Crude linear extrapolation below -76.6dBm and above -8.0dBm. + */ +static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { + { 700, 0 }, + { -80, 3217 }, + { -150, 3893 }, + { -190, 4217 }, + { -240, 4621 }, + { -280, 4945 }, + { -320, 5273 }, + { -350, 5545 }, + { -370, 5741 }, + { -410, 6147 }, + { -450, 6671 }, + { -490, 7413 }, + { -501, 7665 }, + { -530, 8767 }, + { -560, 10219 }, + { -580, 10939 }, + { -590, 11518 }, + { -600, 11723 }, + { -650, 12659 }, + { -690, 13219 }, + { -730, 13645 }, + { -750, 13909 }, + { -766, 14153 }, + { -950, 16383 } +}; + +/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ +static struct stb0899_tab stb0899_quant_tab[] = { + { 0, 0 }, + { 0, 100 }, + { 600, 200 }, + { 950, 299 }, + { 1200, 398 }, + { 1400, 501 }, + { 1560, 603 }, + { 1690, 700 }, + { 1810, 804 }, + { 1910, 902 }, + { 2000, 1000 }, + { 2080, 1096 }, + { 2160, 1202 }, + { 2230, 1303 }, + { 2350, 1496 }, + { 2410, 1603 }, + { 2460, 1698 }, + { 2510, 1799 }, + { 2600, 1995 }, + { 2650, 2113 }, + { 2690, 2213 }, + { 2720, 2291 }, + { 2760, 2399 }, + { 2800, 2512 }, + { 2860, 2692 }, + { 2930, 2917 }, + { 2960, 3020 }, + { 3010, 3199 }, + { 3040, 3311 }, + { 3060, 3388 }, + { 3120, 3631 }, + { 3190, 3936 }, + { 3400, 5012 }, + { 3610, 6383 }, + { 3800, 7943 }, + { 4210, 12735 }, + { 4500, 17783 }, + { 4690, 22131 }, + { 4810, 25410 } +}; + +/* DVB-S2 Es/N0 estimate in dB/100 vs read value */ +static struct stb0899_tab stb0899_est_tab[] = { + { 0, 0 }, + { 0, 1 }, + { 301, 2 }, + { 1204, 16 }, + { 1806, 64 }, + { 2408, 256 }, + { 2709, 512 }, + { 3010, 1023 }, + { 3311, 2046 }, + { 3612, 4093 }, + { 3823, 6653 }, + { 3913, 8185 }, + { 4010, 10233 }, + { 4107, 12794 }, + { 4214, 16368 }, + { 4266, 18450 }, + { 4311, 20464 }, + { 4353, 22542 }, + { 4391, 24604 }, + { 4425, 26607 }, + { 4457, 28642 }, + { 4487, 30690 }, + { 4515, 32734 }, + { 4612, 40926 }, + { 4692, 49204 }, + { 4816, 65464 }, + { 4913, 81846 }, + { 4993, 98401 }, + { 5060, 114815 }, + { 5118, 131220 }, + { 5200, 158489 }, + { 5300, 199526 }, + { 5400, 251189 }, + { 5500, 316228 }, + { 5600, 398107 }, + { 5720, 524807 }, + { 5721, 526017 }, +}; + +static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +{ + int ret; + + u8 b0[] = { reg >> 8, reg & 0xff }; + u8 buf; + + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 2 + },{ + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = &buf, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + if (ret != -ERESTARTSYS) + dprintk(state->verbose, FE_ERROR, 1, + "Read error, Reg=[0x%02x], Status=%d", + reg, ret); + + return ret < 0 ? ret : -EREMOTEIO; + } + if (unlikely(*state->verbose >= FE_DEBUGREG)) + dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x", + reg, buf); + + return (unsigned int)buf; +} + +int stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +{ + int result; + + result = _stb0899_read_reg(state, reg); + /* + * Bug ID 9: + * access to 0xf2xx/0xf6xx + * must be followed by read from 0xf2ff/0xf6ff. + */ + if ((reg != 0xf2ff) && (reg != 0xf6ff) && + (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) + _stb0899_read_reg(state, (reg | 0x00ff)); + + return result; +} + +u32 _stb0899_read_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset) +{ + int status; + u32 data; + u8 buf[7] = { 0 }; + u16 tmpaddr; + + u8 buf_0[] = { + GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ + GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ + GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ + }; + u8 buf_1[] = { + 0x00, /* 0xf3 Reg Offset */ + 0x00, /* 0x44 Reg Offset */ + }; + + struct i2c_msg msg_0 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_0, + .len = 6 + }; + + struct i2c_msg msg_1 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_1, + .len = 2 + }; + + struct i2c_msg msg_r = { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = buf, + .len = 4 + }; + + tmpaddr = stb0899_reg_offset & 0xff00; + if (!(stb0899_reg_offset & 0x8)) + tmpaddr = stb0899_reg_offset | 0x20; + + buf_1[0] = GETBYTE(tmpaddr, BYTE1); + buf_1[1] = GETBYTE(tmpaddr, BYTE0); + + status = i2c_transfer(state->i2c, &msg_0, 1); + if (status < 1) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); + + goto err; + } + + /* Dummy */ + status = i2c_transfer(state->i2c, &msg_1, 1); + if (status < 1) + goto err; + + status = i2c_transfer(state->i2c, &msg_r, 1); + if (status < 1) + goto err; + + buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); + buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); + + /* Actual */ + status = i2c_transfer(state->i2c, &msg_1, 1); + if (status < 1) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); + goto err; + } + + status = i2c_transfer(state->i2c, &msg_r, 1); + if (status < 1) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); + return status < 0 ? status : -EREMOTEIO; + } + + data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]); + if (unlikely(*state->verbose >= FE_DEBUGREG)) + printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data); + + return data; + +err: + return status < 0 ? status : -EREMOTEIO; +} + +int stb0899_write_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset, + u32 stb0899_data) +{ + int status; + + /* Base Address Setup */ + u8 buf_0[] = { + GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ + GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ + GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ + }; + u8 buf_1[] = { + 0x00, /* 0xf3 Reg Offset */ + 0x00, /* 0x44 Reg Offset */ + 0x00, /* data */ + 0x00, /* data */ + 0x00, /* data */ + 0x00, /* data */ + }; + + struct i2c_msg msg_0 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_0, + .len = 6 + }; + + struct i2c_msg msg_1 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_1, + .len = 6 + }; + + buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); + buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); + buf_1[2] = GETBYTE(stb0899_data, BYTE0); + buf_1[3] = GETBYTE(stb0899_data, BYTE1); + buf_1[4] = GETBYTE(stb0899_data, BYTE2); + buf_1[5] = GETBYTE(stb0899_data, BYTE3); + + if (unlikely(*state->verbose >= FE_DEBUGREG)) + printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data); + + status = i2c_transfer(state->i2c, &msg_0, 1); + if (unlikely(status < 1)) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); + goto err; + } + status = i2c_transfer(state->i2c, &msg_1, 1); + if (unlikely(status < 1)) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); + + return status < 0 ? status : -EREMOTEIO; + } + + return 0; + +err: + return status < 0 ? status : -EREMOTEIO; +} + +int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count) +{ + int status; + + u8 b0[] = { reg >> 8, reg & 0xff }; + + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 2 + },{ + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = buf, + .len = count + } + }; + + status = i2c_transfer(state->i2c, msg, 2); + if (status != 2) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n", + __func__, reg, count, status); + goto err; + } + /* + * Bug ID 9: + * access to 0xf2xx/0xf6xx + * must be followed by read from 0xf2ff/0xf6ff. + */ + if ((reg != 0xf2ff) && (reg != 0xf6ff) && + (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) + _stb0899_read_reg(state, (reg | 0x00ff)); + + dprintk(state->verbose, FE_DEBUGREG, 1, + "%s [0x%04x]: %*ph", __func__, reg, count, buf); + + return 0; +err: + return status < 0 ? status : -EREMOTEIO; +} + +int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count) +{ + int ret; + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg i2c_msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + count + }; + + if (2 + count > sizeof(buf)) { + printk(KERN_WARNING + "%s: i2c wr reg=%04x: len=%d is too big!\n", + KBUILD_MODNAME, reg, count); + return -EINVAL; + } + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + memcpy(&buf[2], data, count); + + dprintk(state->verbose, FE_DEBUGREG, 1, + "%s [0x%04x]: %*ph", __func__, reg, count, data); + ret = i2c_transfer(state->i2c, &i2c_msg, 1); + + /* + * Bug ID 9: + * access to 0xf2xx/0xf6xx + * must be followed by read from 0xf2ff/0xf6ff. + */ + if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) + stb0899_read_reg(state, (reg | 0x00ff)); + + if (ret != 1) { + if (ret != -ERESTARTSYS) + dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", + reg, data[0], count, ret); + return ret < 0 ? ret : -EREMOTEIO; + } + + return 0; +} + +int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data) +{ + u8 tmp = data; + return stb0899_write_regs(state, reg, &tmp, 1); +} + +/* + * stb0899_get_mclk + * Get STB0899 master clock frequency + * ExtClk: external clock frequency (Hz) + */ +static u32 stb0899_get_mclk(struct stb0899_state *state) +{ + u32 mclk = 0, div = 0; + + div = stb0899_read_reg(state, STB0899_NCOARSE); + mclk = (div + 1) * state->config->xtal_freq / 6; + dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk); + + return mclk; +} + +/* + * stb0899_set_mclk + * Set STB0899 master Clock frequency + * Mclk: demodulator master clock + * ExtClk: external clock frequency (Hz) + */ +static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk) +{ + struct stb0899_internal *internal = &state->internal; + u8 mdiv = 0; + + dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config); + mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1; + dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv); + + stb0899_write_reg(state, STB0899_NCOARSE, mdiv); + internal->master_clk = stb0899_get_mclk(state); + + dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk); +} + +static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable) +{ + struct stb0899_config *config = state->config; + const struct stb0899_postproc *postproc = config->postproc; + + /* post process event */ + if (postproc) { + if (enable) { + if (postproc[ctl].level == STB0899_GPIOPULLUP) + stb0899_write_reg(state, postproc[ctl].gpio, 0x02); + else + stb0899_write_reg(state, postproc[ctl].gpio, 0x82); + } else { + if (postproc[ctl].level == STB0899_GPIOPULLUP) + stb0899_write_reg(state, postproc[ctl].gpio, 0x82); + else + stb0899_write_reg(state, postproc[ctl].gpio, 0x02); + } + } + return 0; +} + +static void stb0899_detach(struct dvb_frontend *fe) +{ + struct stb0899_state *state = fe->demodulator_priv; + + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); +} + +static void stb0899_release(struct dvb_frontend *fe) +{ + struct stb0899_state *state = fe->demodulator_priv; + + dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend"); + kfree(state); +} + +/* + * stb0899_get_alpha + * return: rolloff + */ +static int stb0899_get_alpha(struct stb0899_state *state) +{ + u8 mode_coeff; + + mode_coeff = stb0899_read_reg(state, STB0899_DEMOD); + + if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1) + return 20; + else + return 35; +} + +/* + * stb0899_init_calc + */ +static void stb0899_init_calc(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + int master_clk; + u8 agc[2]; + u32 reg; + + /* Read registers (in burst mode) */ + stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ + + /* Initial calculations */ + master_clk = stb0899_get_mclk(state); + internal->t_agc1 = 0; + internal->t_agc2 = 0; + internal->master_clk = master_clk; + internal->mclk = master_clk / 65536L; + internal->rolloff = stb0899_get_alpha(state); + + /* DVBS2 Initial calculations */ + /* Set AGC value to the middle */ + internal->agc_gain = 8154; + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); + STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA); + internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg); + + internal->center_freq = 0; + internal->av_frame_coarse = 10; + internal->av_frame_fine = 20; + internal->step_size = 2; +/* + if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO)) + pParams->IQLocked = 0; + else + pParams->IQLocked = 1; +*/ +} + +static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout) +{ + u8 reg = 0; + unsigned long start = jiffies; + + while (1) { + reg = stb0899_read_reg(state, STB0899_DISSTATUS); + if (!STB0899_GETFIELD(FIFOFULL, reg)) + break; + if (time_after(jiffies, start + timeout)) { + dprintk(state->verbose, FE_ERROR, 1, "timed out !!"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct stb0899_state *state = fe->demodulator_priv; + u8 reg, i; + + if (cmd->msg_len > sizeof(cmd->msg)) + return -EINVAL; + + /* enable FIFO precharge */ + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + for (i = 0; i < cmd->msg_len; i++) { + /* wait for FIFO empty */ + if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0) + return -ETIMEDOUT; + + stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]); + } + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + msleep(100); + return 0; +} + +static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout) +{ + u8 reg = 0; + unsigned long start = jiffies; + + while (!STB0899_GETFIELD(RXEND, reg)) { + reg = stb0899_read_reg(state, STB0899_DISRX_ST0); + if (time_after(jiffies, start + timeout)) { + dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); + return -ETIMEDOUT; + } + msleep(10); + } + + return 0; +} + +static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) +{ + struct stb0899_state *state = fe->demodulator_priv; + u8 reg, length = 0, i; + int result; + + if (stb0899_wait_diseqc_rxidle(state, 100) < 0) + return -ETIMEDOUT; + + reg = stb0899_read_reg(state, STB0899_DISRX_ST0); + if (STB0899_GETFIELD(RXEND, reg)) { + + reg = stb0899_read_reg(state, STB0899_DISRX_ST1); + length = STB0899_GETFIELD(FIFOBYTENBR, reg); + + if (length > sizeof (reply->msg)) { + result = -EOVERFLOW; + goto exit; + } + reply->msg_len = length; + + /* extract data */ + for (i = 0; i < length; i++) + reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO); + } + + return 0; +exit: + + return result; +} + +static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) +{ + u8 reg = 0; + unsigned long start = jiffies; + + while (!STB0899_GETFIELD(TXIDLE, reg)) { + reg = stb0899_read_reg(state, STB0899_DISSTATUS); + if (time_after(jiffies, start + timeout)) { + dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); + return -ETIMEDOUT; + } + msleep(10); + } + return 0; +} + +static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct stb0899_state *state = fe->demodulator_priv; + u8 reg, old_state; + + /* wait for diseqc idle */ + if (stb0899_wait_diseqc_txidle(state, 100) < 0) + return -ETIMEDOUT; + + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + old_state = reg; + /* set to burst mode */ + STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x03); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + switch (burst) { + case SEC_MINI_A: + /* unmodulated */ + stb0899_write_reg(state, STB0899_DISFIFO, 0x00); + break; + case SEC_MINI_B: + /* modulated */ + stb0899_write_reg(state, STB0899_DISFIFO, 0xff); + break; + } + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + /* wait for diseqc idle */ + if (stb0899_wait_diseqc_txidle(state, 100) < 0) + return -ETIMEDOUT; + + /* restore state */ + stb0899_write_reg(state, STB0899_DISCNTRL1, old_state); + + return 0; +} + +static int stb0899_diseqc_init(struct stb0899_state *state) +{ +/* + struct dvb_diseqc_slave_reply rx_data; +*/ + u8 f22_tx, reg; + + u32 mclk, tx_freq = 22000;/* count = 0, i; */ + reg = stb0899_read_reg(state, STB0899_DISCNTRL2); + STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); + stb0899_write_reg(state, STB0899_DISCNTRL2, reg); + + /* disable Tx spy */ + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + + mclk = stb0899_get_mclk(state); + f22_tx = mclk / (tx_freq * 32); + stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ + state->rx_freq = 20000; + + return 0; +} + +static int stb0899_sleep(struct dvb_frontend *fe) +{ + struct stb0899_state *state = fe->demodulator_priv; +/* + u8 reg; +*/ + dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))"); + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); + + return 0; +} + +static int stb0899_wakeup(struct dvb_frontend *fe) +{ + int rc; + struct stb0899_state *state = fe->demodulator_priv; + + if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI))) + return rc; + /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */ + if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00))) + return rc; + if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00))) + return rc; + + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1); + + return 0; +} + +static int stb0899_init(struct dvb_frontend *fe) +{ + int i; + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_config *config = state->config; + + dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... "); + + /* init device */ + dprintk(state->verbose, FE_DEBUG, 1, "init device"); + for (i = 0; config->init_dev[i].address != 0xffff; i++) + stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod"); + /* init S2 demod */ + for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++) + stb0899_write_s2reg(state, STB0899_S2DEMOD, + config->init_s2_demod[i].base_address, + config->init_s2_demod[i].offset, + config->init_s2_demod[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod"); + /* init S1 demod */ + for (i = 0; config->init_s1_demod[i].address != 0xffff; i++) + stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC"); + /* init S2 fec */ + for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++) + stb0899_write_s2reg(state, STB0899_S2FEC, + config->init_s2_fec[i].base_address, + config->init_s2_fec[i].offset, + config->init_s2_fec[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init TST"); + /* init test */ + for (i = 0; config->init_tst[i].address != 0xffff; i++) + stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data); + + stb0899_init_calc(state); + stb0899_diseqc_init(state); + + return 0; +} + +static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val) +{ + int res = 0; + int min = 0, med; + + if (val < tab[min].read) + res = tab[min].real; + else if (val >= tab[max].read) + res = tab[max].real; + else { + while ((max - min) > 1) { + med = (max + min) / 2; + if (val >= tab[min].read && val < tab[med].read) + max = med; + else + min = med; + } + res = ((val - tab[min].read) * + (tab[max].real - tab[min].real) / + (tab[max].read - tab[min].read)) + + tab[min].real; + } + + return res; +} + +static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + int val; + u32 reg; + *strength = 0; + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + if (internal->lock) { + reg = stb0899_read_reg(state, STB0899_VSTATUS); + if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { + + reg = stb0899_read_reg(state, STB0899_AGCIQIN); + val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg); + + *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val); + *strength += 750; + dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm", + val & 0xff, *strength); + } + } + break; + case SYS_DVBS2: + if (internal->lock) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN); + val = STB0899_GETFIELD(IF_AGC_GAIN, reg); + + *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); + *strength += 950; + dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", + val & 0x3fff, *strength); + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + + return 0; +} + +static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + unsigned int val, quant, quantn = -1, est, estn = -1; + u8 buf[2]; + u32 reg; + + *snr = 0; + reg = stb0899_read_reg(state, STB0899_VSTATUS); + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + if (internal->lock) { + if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { + + stb0899_read_regs(state, STB0899_NIRM, buf, 2); + val = MAKEWORD16(buf[0], buf[1]); + + *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val); + dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n", + buf[0], buf[1], val, *snr); + } + } + break; + case SYS_DVBS2: + if (internal->lock) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); + quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); + est = STB0899_GETFIELD(ESN0_EST, reg); + if (est == 1) + val = 301; /* C/N = 30.1 dB */ + else if (est == 2) + val = 270; /* C/N = 27.0 dB */ + else { + /* quantn = 100 * log(quant^2) */ + quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100); + /* estn = 100 * log(est) */ + estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est); + /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */ + val = (quantn - estn) / 10; + } + *snr = val; + dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm", + quant, quantn, est, estn, val); + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + + return 0; +} + +static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + u8 reg; + *status = 0; + + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS"); + if (internal->lock) { + reg = stb0899_read_reg(state, STB0899_VSTATUS); + if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { + dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK"); + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + + reg = stb0899_read_reg(state, STB0899_PLPARM); + if (STB0899_GETFIELD(VITCURPUN, reg)) { + dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC"); + *status |= FE_HAS_VITERBI | FE_HAS_SYNC; + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); + } + } + } + break; + case SYS_DVBS2: + dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2"); + if (internal->lock) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); + if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) { + *status |= FE_HAS_CARRIER; + dprintk(state->verbose, FE_DEBUG, 1, + "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER"); + + reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); + if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) { + *status |= FE_HAS_LOCK; + dprintk(state->verbose, FE_DEBUG, 1, + "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK"); + + } + if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) { + *status |= FE_HAS_VITERBI; + dprintk(state->verbose, FE_DEBUG, 1, + "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI"); + } + if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) { + *status |= FE_HAS_SYNC; + dprintk(state->verbose, FE_DEBUG, 1, + "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC"); + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); + } + } + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + return 0; +} + +/* + * stb0899_get_error + * viterbi error for DVB-S/DSS + * packet error for DVB-S2 + * Bit Error Rate or Packet Error Rate * 10 ^ 7 + */ +static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + u8 lsb, msb; + + *ber = 0; + + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + if (internal->lock) { + lsb = stb0899_read_reg(state, STB0899_ECNT1L); + msb = stb0899_read_reg(state, STB0899_ECNT1M); + *ber = MAKEWORD16(msb, lsb); + /* Viterbi Check */ + if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) { + /* Error Rate */ + *ber *= 9766; + /* ber = ber * 10 ^ 7 */ + *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); + *ber /= 8; + } + } + break; + case SYS_DVBS2: + if (internal->lock) { + lsb = stb0899_read_reg(state, STB0899_ECNT1L); + msb = stb0899_read_reg(state, STB0899_ECNT1M); + *ber = MAKEWORD16(msb, lsb); + /* ber = ber * 10 ^ 7 */ + *ber *= 10000000; + *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + + return 0; +} + +static int stb0899_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct stb0899_state *state = fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); + stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); + stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00); + break; + case SEC_VOLTAGE_18: + stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02); + stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); + stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); + break; + case SEC_VOLTAGE_OFF: + stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); + stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82); + stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stb0899_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + u8 div, reg; + + /* wait for diseqc idle */ + if (stb0899_wait_diseqc_txidle(state, 100) < 0) + return -ETIMEDOUT; + + switch (tone) { + case SEC_TONE_ON: + div = (internal->master_clk / 100) / 5632; + div = (div + 5) / 10; + stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66); + reg = stb0899_read_reg(state, STB0899_ACRPRESC); + STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03); + stb0899_write_reg(state, STB0899_ACRPRESC, reg); + stb0899_write_reg(state, STB0899_ACRDIV1, div); + break; + case SEC_TONE_OFF: + stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20); + break; + default: + return -EINVAL; + } + return 0; +} + +int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + int i2c_stat; + struct stb0899_state *state = fe->demodulator_priv; + + i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT); + if (i2c_stat < 0) + goto err; + + if (enable) { + dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ..."); + i2c_stat |= STB0899_I2CTON; + if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) + goto err; + } else { + dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ..."); + i2c_stat &= ~STB0899_I2CTON; + if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) + goto err; + } + return 0; +err: + dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed"); + return -EREMOTEIO; +} + + +static inline void CONVERT32(u32 x, char *str) +{ + *str++ = (x >> 24) & 0xff; + *str++ = (x >> 16) & 0xff; + *str++ = (x >> 8) & 0xff; + *str++ = (x >> 0) & 0xff; + *str = '\0'; +} + +static int stb0899_get_dev_id(struct stb0899_state *state) +{ + u8 chip_id, release; + u16 id; + u32 demod_ver = 0, fec_ver = 0; + char demod_str[5] = { 0 }; + char fec_str[5] = { 0 }; + + id = stb0899_read_reg(state, STB0899_DEV_ID); + dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id); + chip_id = STB0899_GETFIELD(CHIP_ID, id); + release = STB0899_GETFIELD(CHIP_REL, id); + + dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]", + chip_id, release); + + CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str); + + demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID); + dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); + CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); + fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); + if (! (chip_id > 0)) { + dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); + + return -ENODEV; + } + dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver); + + return 0; +} + +static void stb0899_set_delivery(struct stb0899_state *state) +{ + u8 reg; + u8 stop_clk[2]; + + stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1); + stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2); + + switch (state->delsys) { + case SYS_DVBS: + dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S"); + /* FECM/Viterbi ON */ + reg = stb0899_read_reg(state, STB0899_FECM); + STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); + STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); + stb0899_write_reg(state, STB0899_FECM, reg); + + stb0899_write_reg(state, STB0899_RSULC, 0xb1); + stb0899_write_reg(state, STB0899_TSULC, 0x40); + stb0899_write_reg(state, STB0899_RSLLC, 0x42); + stb0899_write_reg(state, STB0899_TSLPL, 0x12); + + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); + + STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); + STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); + + STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); + break; + case SYS_DVBS2: + /* FECM/Viterbi OFF */ + reg = stb0899_read_reg(state, STB0899_FECM); + STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); + STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0); + stb0899_write_reg(state, STB0899_FECM, reg); + + stb0899_write_reg(state, STB0899_RSULC, 0xb1); + stb0899_write_reg(state, STB0899_TSULC, 0x42); + stb0899_write_reg(state, STB0899_RSLLC, 0x40); + stb0899_write_reg(state, STB0899_TSLPL, 0x02); + + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESLDPC, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0); + STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0); + STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0); + + STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0); + STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0); + break; + case SYS_DSS: + /* FECM/Viterbi ON */ + reg = stb0899_read_reg(state, STB0899_FECM); + STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1); + STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); + stb0899_write_reg(state, STB0899_FECM, reg); + + stb0899_write_reg(state, STB0899_RSULC, 0xa1); + stb0899_write_reg(state, STB0899_TSULC, 0x61); + stb0899_write_reg(state, STB0899_RSLLC, 0x42); + + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); + + STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); + STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); + + STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); + break; + default: + dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); + break; + } + STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0); + stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2); +} + +/* + * stb0899_set_iterations + * set the LDPC iteration scale function + */ +static void stb0899_set_iterations(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + s32 iter_scale; + u32 reg; + + iter_scale = 17 * (internal->master_clk / 1000); + iter_scale += 410000; + iter_scale /= (internal->srate / 1000000); + iter_scale /= 1000; + + if (iter_scale > config->ldpc_max_iter) + iter_scale = config->ldpc_max_iter; + + reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER); + STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale); + stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); +} + +static enum dvbfe_search stb0899_search(struct dvb_frontend *fe) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_params *i_params = &state->params; + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + struct dtv_frontend_properties *props = &fe->dtv_property_cache; + + u32 SearchRange, gain; + + i_params->freq = props->frequency; + i_params->srate = props->symbol_rate; + state->delsys = props->delivery_system; + dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys); + + SearchRange = 10000000; + dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate); + /* checking Search Range is meaningless for a fixed 3 Mhz */ + if (INRANGE(i_params->srate, 1000000, 45000000)) { + dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE"); + stb0899_set_delivery(state); + + if (state->config->tuner_set_rfsiggain) { + if (internal->srate > 15000000) + gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */ + else if (internal->srate > 5000000) + gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */ + else + gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */ + state->config->tuner_set_rfsiggain(fe, gain); + } + + if (i_params->srate <= 5000000) + stb0899_set_mclk(state, config->lo_clk); + else + stb0899_set_mclk(state, config->hi_clk); + + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system"); + internal->freq = i_params->freq; + internal->srate = i_params->srate; + /* + * search = user search range + + * 500Khz + + * 2 * Tuner_step_size + + * 10% of the symbol rate + */ + internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5); + internal->derot_percent = 30; + + /* What to do for tuners having no bandwidth setup ? */ + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + if (state->config->tuner_set_bandwidth) + state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10); + if (state->config->tuner_get_bandwidth) + state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + + /* Set DVB-S1 AGC */ + stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11); + + /* Run the search algorithm */ + dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo .."); + if (stb0899_dvbs_algo(state) == RANGEOK) { + internal->lock = 1; + dprintk(state->verbose, FE_DEBUG, 1, + "-------------------------------------> DVB-S LOCK !"); + +// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */ +// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); +// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); +// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status); +// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl); + + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + internal->lock = 0; + + return DVBFE_ALGO_SEARCH_FAILED; + } + break; + case SYS_DVBS2: + internal->freq = i_params->freq; + internal->srate = i_params->srate; + internal->srch_range = SearchRange; + + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + if (state->config->tuner_set_bandwidth) + state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange)); + if (state->config->tuner_get_bandwidth) + state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + +// pParams->SpectralInv = pSearch->IQ_Inversion; + + /* Set DVB-S2 AGC */ + stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c); + + /* Set IterScale =f(MCLK,SYMB) */ + stb0899_set_iterations(state); + + /* Run the search algorithm */ + dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo .."); + if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) { + internal->lock = 1; + dprintk(state->verbose, FE_DEBUG, 1, + "-------------------------------------> DVB-S2 LOCK !"); + +// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */ +// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); +// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); + + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + internal->lock = 0; + + return DVBFE_ALGO_SEARCH_FAILED; + } + break; + default: + dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); + return DVBFE_ALGO_SEARCH_INVALID; + } + } + + return DVBFE_ALGO_SEARCH_ERROR; +} + +static int stb0899_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + dprintk(state->verbose, FE_DEBUG, 1, "Get params"); + p->symbol_rate = internal->srate; + p->frequency = internal->freq; + + return 0; +} + +static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_CUSTOM; +} + +static const struct dvb_frontend_ops stb0899_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, + .info = { + .name = "STB0899 Multistandard", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .symbol_rate_min = 5000000, + .symbol_rate_max = 45000000, + + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK + }, + + .detach = stb0899_detach, + .release = stb0899_release, + .init = stb0899_init, + .sleep = stb0899_sleep, +// .wakeup = stb0899_wakeup, + + .i2c_gate_ctrl = stb0899_i2c_gate_ctrl, + + .get_frontend_algo = stb0899_frontend_algo, + .search = stb0899_search, + .get_frontend = stb0899_get_frontend, + + + .read_status = stb0899_read_status, + .read_snr = stb0899_read_snr, + .read_signal_strength = stb0899_read_signal_strength, + .read_ber = stb0899_read_ber, + + .set_voltage = stb0899_set_voltage, + .set_tone = stb0899_set_tone, + + .diseqc_send_master_cmd = stb0899_send_diseqc_msg, + .diseqc_recv_slave_reply = stb0899_recv_slave_reply, + .diseqc_send_burst = stb0899_send_diseqc_burst, +}; + +struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c) +{ + struct stb0899_state *state = NULL; + + state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->verbose = &verbose; + state->config = config; + state->i2c = i2c; + state->frontend.ops = stb0899_ops; + state->frontend.demodulator_priv = state; + /* use configured inversion as default -- we'll later autodetect inversion */ + state->internal.inversion = config->inversion; + + stb0899_wakeup(&state->frontend); + if (stb0899_get_dev_id(state) == -ENODEV) { + printk("%s: Exiting .. !\n", __func__); + goto error; + } + + printk("%s: Attaching STB0899 \n", __func__); + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stb0899_attach); +MODULE_PARM_DESC(verbose, "Set Verbosity level"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("STB0899 Multi-Std frontend"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h new file mode 100644 index 0000000000..5a99f0b42d --- /dev/null +++ b/drivers/media/dvb-frontends/stb0899_drv.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STB0899_DRV_H +#define __STB0899_DRV_H + +#include <linux/kernel.h> +#include <linux/module.h> + +#include <media/dvb_frontend.h> + +#define STB0899_TSMODE_SERIAL 1 +#define STB0899_CLKPOL_FALLING 2 +#define STB0899_CLKNULL_PARITY 3 +#define STB0899_SYNC_FORCED 4 +#define STB0899_FECMODE_DSS 5 + +struct stb0899_s1_reg { + u16 address; + u8 data; +}; + +struct stb0899_s2_reg { + u16 offset; + u32 base_address; + u32 data; +}; + +enum stb0899_inversion { + IQ_SWAP_OFF = +1, /* inversion affects the sign of e. g. */ + IQ_SWAP_ON = -1, /* the derotator frequency register */ +}; + +#define STB0899_GPIO00 0xf140 +#define STB0899_GPIO01 0xf141 +#define STB0899_GPIO02 0xf142 +#define STB0899_GPIO03 0xf143 +#define STB0899_GPIO04 0xf144 +#define STB0899_GPIO05 0xf145 +#define STB0899_GPIO06 0xf146 +#define STB0899_GPIO07 0xf147 +#define STB0899_GPIO08 0xf148 +#define STB0899_GPIO09 0xf149 +#define STB0899_GPIO10 0xf14a +#define STB0899_GPIO11 0xf14b +#define STB0899_GPIO12 0xf14c +#define STB0899_GPIO13 0xf14d +#define STB0899_GPIO14 0xf14e +#define STB0899_GPIO15 0xf14f +#define STB0899_GPIO16 0xf150 +#define STB0899_GPIO17 0xf151 +#define STB0899_GPIO18 0xf152 +#define STB0899_GPIO19 0xf153 +#define STB0899_GPIO20 0xf154 + +#define STB0899_GPIOPULLUP 0x01 /* Output device is connected to Vdd */ +#define STB0899_GPIOPULLDN 0x00 /* Output device is connected to Vss */ + +#define STB0899_POSTPROC_GPIO_POWER 0x00 +#define STB0899_POSTPROC_GPIO_LOCK 0x01 + +/* + * Post process output configuration control + * 1. POWER ON/OFF (index 0) + * 2. FE_HAS_LOCK/LOCK_LOSS (index 1) + * + * @gpio = one of the above listed GPIO's + * @level = output state: pulled up or low + */ +struct stb0899_postproc { + u16 gpio; + u8 level; +}; + +struct stb0899_config { + const struct stb0899_s1_reg *init_dev; + const struct stb0899_s2_reg *init_s2_demod; + const struct stb0899_s1_reg *init_s1_demod; + const struct stb0899_s2_reg *init_s2_fec; + const struct stb0899_s1_reg *init_tst; + + const struct stb0899_postproc *postproc; + + enum stb0899_inversion inversion; + + u32 xtal_freq; + + u8 demod_address; + u8 ts_output_mode; + u8 block_sync_mode; + u8 ts_pfbit_toggle; + + u8 clock_polarity; + u8 data_clk_parity; + u8 fec_mode; + u8 data_output_ctl; + u8 data_fifo_mode; + u8 out_rate_comp; + u8 i2c_repeater; +// int inversion; + int lo_clk; + int hi_clk; + + u32 esno_ave; + u32 esno_quant; + u32 avframes_coarse; + u32 avframes_fine; + u32 miss_threshold; + u32 uwp_threshold_acq; + u32 uwp_threshold_track; + u32 uwp_threshold_sof; + u32 sof_search_timeout; + + u32 btr_nco_bits; + u32 btr_gain_shift_offset; + u32 crl_nco_bits; + u32 ldpc_max_iter; + + int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency); + int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency); + int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); + int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); + int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain); +}; + +#if IS_REACHABLE(CONFIG_DVB_STB0899) + +extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *stb0899_attach(struct stb0899_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif //CONFIG_DVB_STB0899 + + +#endif diff --git a/drivers/media/dvb-frontends/stb0899_priv.h b/drivers/media/dvb-frontends/stb0899_priv.h new file mode 100644 index 0000000000..c354ffd8ad --- /dev/null +++ b/drivers/media/dvb-frontends/stb0899_priv.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STB0899_PRIV_H +#define __STB0899_PRIV_H + +#include <media/dvb_frontend.h> +#include "stb0899_drv.h" + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 +#define FE_DEBUGREG 4 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((*x > FE_ERROR) && (*x > y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((*x > FE_NOTICE) && (*x > y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((*x > FE_INFO) && (*x > y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((*x > FE_DEBUG) && (*x > y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (*x > y) \ + printk(format, ##arg); \ + } \ +} while(0) + +#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ + ((y <= val) && (val <= x)) ? 1 : 0) + +#define BYTE0 0 +#define BYTE1 8 +#define BYTE2 16 +#define BYTE3 24 + +#define GETBYTE(x, y) (((x) >> (y)) & 0xff) +#define MAKEWORD32(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define MAKEWORD16(a, b) (((a) << 8) | (b)) + +#define LSB(x) ((x & 0xff)) +#define MSB(y) ((y >> 8) & 0xff) + + +#define STB0899_GETFIELD(bitf, val) ((val >> STB0899_OFFST_##bitf) & ((1 << STB0899_WIDTH_##bitf) - 1)) + + +#define STB0899_SETFIELD(mask, val, width, offset) (mask & (~(((1 << width) - 1) << \ + offset))) | ((val & \ + ((1 << width) - 1)) << offset) + +#define STB0899_SETFIELD_VAL(bitf, mask, val) (mask = (mask & (~(((1 << STB0899_WIDTH_##bitf) - 1) <<\ + STB0899_OFFST_##bitf))) | \ + (val << STB0899_OFFST_##bitf)) + + +enum stb0899_status { + NOAGC1 = 0, + AGC1OK, + NOTIMING, + ANALOGCARRIER, + TIMINGOK, + NOAGC2, + AGC2OK, + NOCARRIER, + CARRIEROK, + NODATA, + FALSELOCK, + DATAOK, + OUTOFRANGE, + RANGEOK, + DVBS2_DEMOD_LOCK, + DVBS2_DEMOD_NOLOCK, + DVBS2_FEC_LOCK, + DVBS2_FEC_NOLOCK +}; + +enum stb0899_modcod { + STB0899_DUMMY_PLF, + STB0899_QPSK_14, + STB0899_QPSK_13, + STB0899_QPSK_25, + STB0899_QPSK_12, + STB0899_QPSK_35, + STB0899_QPSK_23, + STB0899_QPSK_34, + STB0899_QPSK_45, + STB0899_QPSK_56, + STB0899_QPSK_89, + STB0899_QPSK_910, + STB0899_8PSK_35, + STB0899_8PSK_23, + STB0899_8PSK_34, + STB0899_8PSK_56, + STB0899_8PSK_89, + STB0899_8PSK_910, + STB0899_16APSK_23, + STB0899_16APSK_34, + STB0899_16APSK_45, + STB0899_16APSK_56, + STB0899_16APSK_89, + STB0899_16APSK_910, + STB0899_32APSK_34, + STB0899_32APSK_45, + STB0899_32APSK_56, + STB0899_32APSK_89, + STB0899_32APSK_910 +}; + +enum stb0899_frame { + STB0899_LONG_FRAME, + STB0899_SHORT_FRAME +}; + +enum stb0899_alpha { + RRC_20, + RRC_25, + RRC_35 +}; + +struct stb0899_tab { + s32 real; + s32 read; +}; + +enum stb0899_fec { + STB0899_FEC_1_2 = 13, + STB0899_FEC_2_3 = 18, + STB0899_FEC_3_4 = 21, + STB0899_FEC_5_6 = 24, + STB0899_FEC_6_7 = 25, + STB0899_FEC_7_8 = 26 +}; + +struct stb0899_params { + u32 freq; /* Frequency */ + u32 srate; /* Symbol rate */ + enum fe_code_rate fecrate; +}; + +struct stb0899_internal { + u32 master_clk; + u32 freq; /* Demod internal Frequency */ + u32 srate; /* Demod internal Symbol rate */ + enum stb0899_fec fecrate; /* Demod internal FEC rate */ + s32 srch_range; /* Demod internal Search Range */ + s32 sub_range; /* Demod current sub range (Hz) */ + s32 tuner_step; /* Tuner step (Hz) */ + s32 tuner_offst; /* Relative offset to carrier (Hz) */ + u32 tuner_bw; /* Current bandwidth of the tuner (Hz) */ + + s32 mclk; /* Masterclock Divider factor (binary) */ + s32 rolloff; /* Current RollOff of the filter (x100) */ + + s16 derot_freq; /* Current derotator frequency (Hz) */ + s16 derot_percent; + + s16 direction; /* Current derotator search direction */ + s16 derot_step; /* Derotator step (binary value) */ + s16 t_derot; /* Derotator time constant (ms) */ + s16 t_data; /* Data recovery time constant (ms) */ + s16 sub_dir; /* Direction of the next sub range */ + + s16 t_agc1; /* Agc1 time constant (ms) */ + s16 t_agc2; /* Agc2 time constant (ms) */ + + u32 lock; /* Demod internal lock state */ + enum stb0899_status status; /* Demod internal status */ + + /* DVB-S2 */ + s32 agc_gain; /* RF AGC Gain */ + s32 center_freq; /* Nominal carrier frequency */ + s32 av_frame_coarse; /* Coarse carrier freq search frames */ + s32 av_frame_fine; /* Fine carrier freq search frames */ + + s16 step_size; /* Carrier frequency search step size */ + + enum stb0899_alpha rrc_alpha; + enum stb0899_inversion inversion; + enum stb0899_modcod modcod; + u8 pilots; /* Pilots found */ + + enum stb0899_frame frame_length; + u8 v_status; /* VSTATUS */ + u8 err_ctrl; /* ERRCTRLn */ +}; + +struct stb0899_state { + struct i2c_adapter *i2c; + struct stb0899_config *config; + struct dvb_frontend frontend; + + u32 *verbose; /* Cached module verbosity level */ + + struct stb0899_internal internal; /* Device internal parameters */ + + /* cached params from API */ + enum fe_delivery_system delsys; + struct stb0899_params params; + + u32 rx_freq; /* DiSEqC 2.0 receiver freq */ + struct mutex search_lock; +}; +/* stb0899.c */ +extern int stb0899_read_reg(struct stb0899_state *state, + unsigned int reg); + +extern u32 _stb0899_read_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset); + +extern int stb0899_read_regs(struct stb0899_state *state, + unsigned int reg, u8 *buf, + u32 count); + +extern int stb0899_write_regs(struct stb0899_state *state, + unsigned int reg, u8 *data, + u32 count); + +extern int stb0899_write_reg(struct stb0899_state *state, + unsigned int reg, + u8 data); + +extern int stb0899_write_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset, + u32 stb0899_data); + +extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); + + +#define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG)) +//#define STB0899_WRITE_S2REG(DEVICE, REG, DATA) (_stb0899_write_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG, DATA)) + +/* stb0899_algo.c */ +extern enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state); +extern enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state); +extern long stb0899_carr_width(struct stb0899_state *state); + +#endif //__STB0899_PRIV_H diff --git a/drivers/media/dvb-frontends/stb0899_reg.h b/drivers/media/dvb-frontends/stb0899_reg.h new file mode 100644 index 0000000000..6cf9679ec5 --- /dev/null +++ b/drivers/media/dvb-frontends/stb0899_reg.h @@ -0,0 +1,2015 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STB0899_REG_H +#define __STB0899_REG_H + +/* S1 */ +#define STB0899_DEV_ID 0xf000 +#define STB0899_CHIP_ID (0x0f << 4) +#define STB0899_OFFST_CHIP_ID 4 +#define STB0899_WIDTH_CHIP_ID 4 +#define STB0899_CHIP_REL (0x0f << 0) +#define STB0899_OFFST_CHIP_REL 0 +#define STB0899_WIDTH_CHIP_REL 4 + +#define STB0899_DEMOD 0xf40e +#define STB0899_MODECOEFF (0x01 << 0) +#define STB0899_OFFST_MODECOEFF 0 +#define STB0899_WIDTH_MODECOEFF 1 + +#define STB0899_RCOMPC 0xf410 +#define STB0899_AGC1CN 0xf412 +#define STB0899_AGC1REF 0xf413 +#define STB0899_RTC 0xf417 +#define STB0899_TMGCFG 0xf418 +#define STB0899_AGC2REF 0xf419 +#define STB0899_TLSR 0xf41a + +#define STB0899_CFD 0xf41b +#define STB0899_CFD_ON (0x01 << 7) +#define STB0899_OFFST_CFD_ON 7 +#define STB0899_WIDTH_CFD_ON 1 + +#define STB0899_ACLC 0xf41c + +#define STB0899_BCLC 0xf41d +#define STB0899_OFFST_ALGO 6 +#define STB0899_WIDTH_ALGO_QPSK2 2 +#define STB0899_ALGO_QPSK2 (2 << 6) +#define STB0899_ALGO_QPSK1 (1 << 6) +#define STB0899_ALGO_BPSK (0 << 6) +#define STB0899_OFFST_BETA 0 +#define STB0899_WIDTH_BETA 6 + +#define STB0899_EQON 0xf41e +#define STB0899_LDT 0xf41f +#define STB0899_LDT2 0xf420 +#define STB0899_EQUALREF 0xf425 +#define STB0899_TMGRAMP 0xf426 +#define STB0899_TMGTHD 0xf427 +#define STB0899_IDCCOMP 0xf428 +#define STB0899_QDCCOMP 0xf429 +#define STB0899_POWERI 0xf42a +#define STB0899_POWERQ 0xf42b +#define STB0899_RCOMP 0xf42c + +#define STB0899_AGCIQIN 0xf42e +#define STB0899_AGCIQVALUE (0xff << 0) +#define STB0899_OFFST_AGCIQVALUE 0 +#define STB0899_WIDTH_AGCIQVALUE 8 + +#define STB0899_AGC2I1 0xf436 +#define STB0899_AGC2I2 0xf437 + +#define STB0899_TLIR 0xf438 +#define STB0899_TLIR_TMG_LOCK_IND (0xff << 0) +#define STB0899_OFFST_TLIR_TMG_LOCK_IND 0 +#define STB0899_WIDTH_TLIR_TMG_LOCK_IND 8 + +#define STB0899_RTF 0xf439 +#define STB0899_RTF_TIMING_LOOP_FREQ (0xff << 0) +#define STB0899_OFFST_RTF_TIMING_LOOP_FREQ 0 +#define STB0899_WIDTH_RTF_TIMING_LOOP_FREQ 8 + +#define STB0899_DSTATUS 0xf43a +#define STB0899_CARRIER_FOUND (0x01 << 7) +#define STB0899_OFFST_CARRIER_FOUND 7 +#define STB0899_WIDTH_CARRIER_FOUND 1 +#define STB0899_TMG_LOCK (0x01 << 6) +#define STB0899_OFFST_TMG_LOCK 6 +#define STB0899_WIDTH_TMG_LOCK 1 +#define STB0899_DEMOD_LOCK (0x01 << 5) +#define STB0899_OFFST_DEMOD_LOCK 5 +#define STB0899_WIDTH_DEMOD_LOCK 1 +#define STB0899_TMG_AUTO (0x01 << 4) +#define STB0899_OFFST_TMG_AUTO 4 +#define STB0899_WIDTH_TMG_AUTO 1 +#define STB0899_END_MAIN (0x01 << 3) +#define STB0899_OFFST_END_MAIN 3 +#define STB0899_WIDTH_END_MAIN 1 + +#define STB0899_LDI 0xf43b +#define STB0899_OFFST_LDI 0 +#define STB0899_WIDTH_LDI 8 + +#define STB0899_CFRM 0xf43e +#define STB0899_OFFST_CFRM 0 +#define STB0899_WIDTH_CFRM 8 + +#define STB0899_CFRL 0xf43f +#define STB0899_OFFST_CFRL 0 +#define STB0899_WIDTH_CFRL 8 + +#define STB0899_NIRM 0xf440 +#define STB0899_OFFST_NIRM 0 +#define STB0899_WIDTH_NIRM 8 + +#define STB0899_NIRL 0xf441 +#define STB0899_OFFST_NIRL 0 +#define STB0899_WIDTH_NIRL 8 + +#define STB0899_ISYMB 0xf444 +#define STB0899_QSYMB 0xf445 + +#define STB0899_SFRH 0xf446 +#define STB0899_OFFST_SFRH 0 +#define STB0899_WIDTH_SFRH 8 + +#define STB0899_SFRM 0xf447 +#define STB0899_OFFST_SFRM 0 +#define STB0899_WIDTH_SFRM 8 + +#define STB0899_SFRL 0xf448 +#define STB0899_OFFST_SFRL 4 +#define STB0899_WIDTH_SFRL 4 + +#define STB0899_SFRUPH 0xf44c +#define STB0899_SFRUPM 0xf44d +#define STB0899_SFRUPL 0xf44e + +#define STB0899_EQUAI1 0xf4e0 +#define STB0899_EQUAQ1 0xf4e1 +#define STB0899_EQUAI2 0xf4e2 +#define STB0899_EQUAQ2 0xf4e3 +#define STB0899_EQUAI3 0xf4e4 +#define STB0899_EQUAQ3 0xf4e5 +#define STB0899_EQUAI4 0xf4e6 +#define STB0899_EQUAQ4 0xf4e7 +#define STB0899_EQUAI5 0xf4e8 +#define STB0899_EQUAQ5 0xf4e9 + +#define STB0899_DSTATUS2 0xf50c +#define STB0899_DS2_TMG_AUTOSRCH (0x01 << 7) +#define STB8999_OFFST_DS2_TMG_AUTOSRCH 7 +#define STB0899_WIDTH_DS2_TMG_AUTOSRCH 1 +#define STB0899_DS2_END_MAINLOOP (0x01 << 6) +#define STB0899_OFFST_DS2_END_MAINLOOP 6 +#define STB0899_WIDTH_DS2_END_MAINLOOP 1 +#define STB0899_DS2_CFSYNC (0x01 << 5) +#define STB0899_OFFST_DS2_CFSYNC 5 +#define STB0899_WIDTH_DS2_CFSYNC 1 +#define STB0899_DS2_TMGLOCK (0x01 << 4) +#define STB0899_OFFST_DS2_TMGLOCK 4 +#define STB0899_WIDTH_DS2_TMGLOCK 1 +#define STB0899_DS2_DEMODWAIT (0x01 << 3) +#define STB0899_OFFST_DS2_DEMODWAIT 3 +#define STB0899_WIDTH_DS2_DEMODWAIT 1 +#define STB0899_DS2_FECON (0x01 << 1) +#define STB0899_OFFST_DS2_FECON 1 +#define STB0899_WIDTH_DS2_FECON 1 + +/* S1 FEC */ +#define STB0899_VSTATUS 0xf50d +#define STB0899_VSTATUS_VITERBI_ON (0x01 << 7) +#define STB0899_OFFST_VSTATUS_VITERBI_ON 7 +#define STB0899_WIDTH_VSTATUS_VITERBI_ON 1 +#define STB0899_VSTATUS_END_LOOPVIT (0x01 << 6) +#define STB0899_OFFST_VSTATUS_END_LOOPVIT 6 +#define STB0899_WIDTH_VSTATUS_END_LOOPVIT 1 +#define STB0899_VSTATUS_PRFVIT (0x01 << 4) +#define STB0899_OFFST_VSTATUS_PRFVIT 4 +#define STB0899_WIDTH_VSTATUS_PRFVIT 1 +#define STB0899_VSTATUS_LOCKEDVIT (0x01 << 3) +#define STB0899_OFFST_VSTATUS_LOCKEDVIT 3 +#define STB0899_WIDTH_VSTATUS_LOCKEDVIT 1 + +#define STB0899_VERROR 0xf50f + +#define STB0899_IQSWAP 0xf523 +#define STB0899_SYM (0x01 << 3) +#define STB0899_OFFST_SYM 3 +#define STB0899_WIDTH_SYM 1 + +#define STB0899_FECAUTO1 0xf530 +#define STB0899_DSSSRCH (0x01 << 3) +#define STB0899_OFFST_DSSSRCH 3 +#define STB0899_WIDTH_DSSSRCH 1 +#define STB0899_SYMSRCH (0x01 << 2) +#define STB0899_OFFST_SYMSRCH 2 +#define STB0899_WIDTH_SYMSRCH 1 +#define STB0899_QPSKSRCH (0x01 << 1) +#define STB0899_OFFST_QPSKSRCH 1 +#define STB0899_WIDTH_QPSKSRCH 1 +#define STB0899_BPSKSRCH (0x01 << 0) +#define STB0899_OFFST_BPSKSRCH 0 +#define STB0899_WIDTH_BPSKSRCH 1 + +#define STB0899_FECM 0xf533 +#define STB0899_FECM_NOT_DVB (0x01 << 7) +#define STB0899_OFFST_FECM_NOT_DVB 7 +#define STB0899_WIDTH_FECM_NOT_DVB 1 +#define STB0899_FECM_RSVD1 (0x07 << 4) +#define STB0899_OFFST_FECM_RSVD1 4 +#define STB0899_WIDTH_FECM_RSVD1 3 +#define STB0899_FECM_VITERBI_ON (0x01 << 3) +#define STB0899_OFFST_FECM_VITERBI_ON 3 +#define STB0899_WIDTH_FECM_VITERBI_ON 1 +#define STB0899_FECM_RSVD0 (0x01 << 2) +#define STB0899_OFFST_FECM_RSVD0 2 +#define STB0899_WIDTH_FECM_RSVD0 1 +#define STB0899_FECM_SYNCDIS (0x01 << 1) +#define STB0899_OFFST_FECM_SYNCDIS 1 +#define STB0899_WIDTH_FECM_SYNCDIS 1 +#define STB0899_FECM_SYMI (0x01 << 0) +#define STB0899_OFFST_FECM_SYMI 0 +#define STB0899_WIDTH_FECM_SYMI 1 + +#define STB0899_VTH12 0xf534 +#define STB0899_VTH23 0xf535 +#define STB0899_VTH34 0xf536 +#define STB0899_VTH56 0xf537 +#define STB0899_VTH67 0xf538 +#define STB0899_VTH78 0xf539 + +#define STB0899_PRVIT 0xf53c +#define STB0899_PR_7_8 (0x01 << 5) +#define STB0899_OFFST_PR_7_8 5 +#define STB0899_WIDTH_PR_7_8 1 +#define STB0899_PR_6_7 (0x01 << 4) +#define STB0899_OFFST_PR_6_7 4 +#define STB0899_WIDTH_PR_6_7 1 +#define STB0899_PR_5_6 (0x01 << 3) +#define STB0899_OFFST_PR_5_6 3 +#define STB0899_WIDTH_PR_5_6 1 +#define STB0899_PR_3_4 (0x01 << 2) +#define STB0899_OFFST_PR_3_4 2 +#define STB0899_WIDTH_PR_3_4 1 +#define STB0899_PR_2_3 (0x01 << 1) +#define STB0899_OFFST_PR_2_3 1 +#define STB0899_WIDTH_PR_2_3 1 +#define STB0899_PR_1_2 (0x01 << 0) +#define STB0899_OFFST_PR_1_2 0 +#define STB0899_WIDTH_PR_1_2 1 + +#define STB0899_VITSYNC 0xf53d +#define STB0899_AM (0x01 << 7) +#define STB0899_OFFST_AM 7 +#define STB0899_WIDTH_AM 1 +#define STB0899_FREEZE (0x01 << 6) +#define STB0899_OFFST_FREEZE 6 +#define STB0899_WIDTH_FREEZE 1 +#define STB0899_SN_65536 (0x03 << 4) +#define STB0899_OFFST_SN_65536 4 +#define STB0899_WIDTH_SN_65536 2 +#define STB0899_SN_16384 (0x01 << 5) +#define STB0899_OFFST_SN_16384 5 +#define STB0899_WIDTH_SN_16384 1 +#define STB0899_SN_4096 (0x01 << 4) +#define STB0899_OFFST_SN_4096 4 +#define STB0899_WIDTH_SN_4096 1 +#define STB0899_SN_1024 (0x00 << 4) +#define STB0899_OFFST_SN_1024 4 +#define STB0899_WIDTH_SN_1024 0 +#define STB0899_TO_128 (0x03 << 2) +#define STB0899_OFFST_TO_128 2 +#define STB0899_WIDTH_TO_128 2 +#define STB0899_TO_64 (0x01 << 3) +#define STB0899_OFFST_TO_64 3 +#define STB0899_WIDTH_TO_64 1 +#define STB0899_TO_32 (0x01 << 2) +#define STB0899_OFFST_TO_32 2 +#define STB0899_WIDTH_TO_32 1 +#define STB0899_TO_16 (0x00 << 2) +#define STB0899_OFFST_TO_16 2 +#define STB0899_WIDTH_TO_16 0 +#define STB0899_HYST_128 (0x03 << 1) +#define STB0899_OFFST_HYST_128 1 +#define STB0899_WIDTH_HYST_128 2 +#define STB0899_HYST_64 (0x01 << 1) +#define STB0899_OFFST_HYST_64 1 +#define STB0899_WIDTH_HYST_64 1 +#define STB0899_HYST_32 (0x01 << 0) +#define STB0899_OFFST_HYST_32 0 +#define STB0899_WIDTH_HYST_32 1 +#define STB0899_HYST_16 (0x00 << 0) +#define STB0899_OFFST_HYST_16 0 +#define STB0899_WIDTH_HYST_16 0 + +#define STB0899_RSULC 0xf548 +#define STB0899_ULDIL_ON (0x01 << 7) +#define STB0899_OFFST_ULDIL_ON 7 +#define STB0899_WIDTH_ULDIL_ON 1 +#define STB0899_ULAUTO_ON (0x01 << 6) +#define STB0899_OFFST_ULAUTO_ON 6 +#define STB0899_WIDTH_ULAUTO_ON 1 +#define STB0899_ULRS_ON (0x01 << 5) +#define STB0899_OFFST_ULRS_ON 5 +#define STB0899_WIDTH_ULRS_ON 1 +#define STB0899_ULDESCRAM_ON (0x01 << 4) +#define STB0899_OFFST_ULDESCRAM_ON 4 +#define STB0899_WIDTH_ULDESCRAM_ON 1 +#define STB0899_UL_DISABLE (0x01 << 2) +#define STB0899_OFFST_UL_DISABLE 2 +#define STB0899_WIDTH_UL_DISABLE 1 +#define STB0899_NOFTHRESHOLD (0x01 << 0) +#define STB0899_OFFST_NOFTHRESHOLD 0 +#define STB0899_WIDTH_NOFTHRESHOLD 1 + +#define STB0899_RSLLC 0xf54a +#define STB0899_DEMAPVIT 0xf583 +#define STB0899_DEMAPVIT_RSVD (0x01 << 7) +#define STB0899_OFFST_DEMAPVIT_RSVD 7 +#define STB0899_WIDTH_DEMAPVIT_RSVD 1 +#define STB0899_DEMAPVIT_KDIVIDER (0x7f << 0) +#define STB0899_OFFST_DEMAPVIT_KDIVIDER 0 +#define STB0899_WIDTH_DEMAPVIT_KDIVIDER 7 + +#define STB0899_PLPARM 0xf58c +#define STB0899_VITMAPPING (0x07 << 5) +#define STB0899_OFFST_VITMAPPING 5 +#define STB0899_WIDTH_VITMAPPING 3 +#define STB0899_VITMAPPING_BPSK (0x01 << 5) +#define STB0899_OFFST_VITMAPPING_BPSK 5 +#define STB0899_WIDTH_VITMAPPING_BPSK 1 +#define STB0899_VITMAPPING_QPSK (0x00 << 5) +#define STB0899_OFFST_VITMAPPING_QPSK 5 +#define STB0899_WIDTH_VITMAPPING_QPSK 0 +#define STB0899_VITCURPUN (0x1f << 0) +#define STB0899_OFFST_VITCURPUN 0 +#define STB0899_WIDTH_VITCURPUN 5 +#define STB0899_VITCURPUN_1_2 (0x0d << 0) +#define STB0899_VITCURPUN_2_3 (0x12 << 0) +#define STB0899_VITCURPUN_3_4 (0x15 << 0) +#define STB0899_VITCURPUN_5_6 (0x18 << 0) +#define STB0899_VITCURPUN_6_7 (0x19 << 0) +#define STB0899_VITCURPUN_7_8 (0x1a << 0) + +/* S2 DEMOD */ +#define STB0899_OFF0_DMD_STATUS 0xf300 +#define STB0899_BASE_DMD_STATUS 0x00000000 +#define STB0899_IF_AGC_LOCK (0x01 << 8) +#define STB0899_OFFST_IF_AGC_LOCK 0 +#define STB0899_WIDTH_IF_AGC_LOCK 1 + +#define STB0899_OFF0_CRL_FREQ 0xf304 +#define STB0899_BASE_CRL_FREQ 0x00000000 +#define STB0899_CARR_FREQ (0x3fffffff << 0) +#define STB0899_OFFST_CARR_FREQ 0 +#define STB0899_WIDTH_CARR_FREQ 30 + +#define STB0899_OFF0_BTR_FREQ 0xf308 +#define STB0899_BASE_BTR_FREQ 0x00000000 +#define STB0899_BTR_FREQ (0xfffffff << 0) +#define STB0899_OFFST_BTR_FREQ 0 +#define STB0899_WIDTH_BTR_FREQ 28 + +#define STB0899_OFF0_IF_AGC_GAIN 0xf30c +#define STB0899_BASE_IF_AGC_GAIN 0x00000000 +#define STB0899_IF_AGC_GAIN (0x3fff << 0) +#define STB0899_OFFST_IF_AGC_GAIN 0 +#define STB0899_WIDTH_IF_AGC_GAIN 14 + +#define STB0899_OFF0_BB_AGC_GAIN 0xf310 +#define STB0899_BASE_BB_AGC_GAIN 0x00000000 +#define STB0899_BB_AGC_GAIN (0x3fff << 0) +#define STB0899_OFFST_BB_AGC_GAIN 0 +#define STB0899_WIDTH_BB_AGC_GAIN 14 + +#define STB0899_OFF0_DC_OFFSET 0xf314 +#define STB0899_BASE_DC_OFFSET 0x00000000 +#define STB0899_I (0xff << 8) +#define STB0899_OFFST_I 8 +#define STB0899_WIDTH_I 8 +#define STB0899_Q (0xff << 0) +#define STB0899_OFFST_Q 8 +#define STB0899_WIDTH_Q 8 + +#define STB0899_OFF0_DMD_CNTRL 0xf31c +#define STB0899_BASE_DMD_CNTRL 0x00000000 +#define STB0899_ADC0_PINS1IN (0x01 << 6) +#define STB0899_OFFST_ADC0_PINS1IN 6 +#define STB0899_WIDTH_ADC0_PINS1IN 1 +#define STB0899_IN2COMP1_OFFBIN0 (0x01 << 3) +#define STB0899_OFFST_IN2COMP1_OFFBIN0 3 +#define STB0899_WIDTH_IN2COMP1_OFFBIN0 1 +#define STB0899_DC_COMP (0x01 << 2) +#define STB0899_OFFST_DC_COMP 2 +#define STB0899_WIDTH_DC_COMP 1 +#define STB0899_MODMODE (0x03 << 0) +#define STB0899_OFFST_MODMODE 0 +#define STB0899_WIDTH_MODMODE 2 + +#define STB0899_OFF0_IF_AGC_CNTRL 0xf320 +#define STB0899_BASE_IF_AGC_CNTRL 0x00000000 +#define STB0899_IF_GAIN_INIT (0x3fff << 13) +#define STB0899_OFFST_IF_GAIN_INIT 13 +#define STB0899_WIDTH_IF_GAIN_INIT 14 +#define STB0899_IF_GAIN_SENSE (0x01 << 12) +#define STB0899_OFFST_IF_GAIN_SENSE 12 +#define STB0899_WIDTH_IF_GAIN_SENSE 1 +#define STB0899_IF_LOOP_GAIN (0x0f << 8) +#define STB0899_OFFST_IF_LOOP_GAIN 8 +#define STB0899_WIDTH_IF_LOOP_GAIN 4 +#define STB0899_IF_LD_GAIN_INIT (0x01 << 7) +#define STB0899_OFFST_IF_LD_GAIN_INIT 7 +#define STB0899_WIDTH_IF_LD_GAIN_INIT 1 +#define STB0899_IF_AGC_REF (0x7f << 0) +#define STB0899_OFFST_IF_AGC_REF 0 +#define STB0899_WIDTH_IF_AGC_REF 7 + +#define STB0899_OFF0_BB_AGC_CNTRL 0xf324 +#define STB0899_BASE_BB_AGC_CNTRL 0x00000000 +#define STB0899_BB_GAIN_INIT (0x3fff << 12) +#define STB0899_OFFST_BB_GAIN_INIT 12 +#define STB0899_WIDTH_BB_GAIN_INIT 14 +#define STB0899_BB_LOOP_GAIN (0x0f << 8) +#define STB0899_OFFST_BB_LOOP_GAIN 8 +#define STB0899_WIDTH_BB_LOOP_GAIN 4 +#define STB0899_BB_LD_GAIN_INIT (0x01 << 7) +#define STB0899_OFFST_BB_LD_GAIN_INIT 7 +#define STB0899_WIDTH_BB_LD_GAIN_INIT 1 +#define STB0899_BB_AGC_REF (0x7f << 0) +#define STB0899_OFFST_BB_AGC_REF 0 +#define STB0899_WIDTH_BB_AGC_REF 7 + +#define STB0899_OFF0_CRL_CNTRL 0xf328 +#define STB0899_BASE_CRL_CNTRL 0x00000000 +#define STB0899_CRL_LOCK_CLEAR (0x01 << 5) +#define STB0899_OFFST_CRL_LOCK_CLEAR 5 +#define STB0899_WIDTH_CRL_LOCK_CLEAR 1 +#define STB0899_CRL_SWPR_CLEAR (0x01 << 4) +#define STB0899_OFFST_CRL_SWPR_CLEAR 4 +#define STB0899_WIDTH_CRL_SWPR_CLEAR 1 +#define STB0899_CRL_SWP_ENA (0x01 << 3) +#define STB0899_OFFST_CRL_SWP_ENA 3 +#define STB0899_WIDTH_CRL_SWP_ENA 1 +#define STB0899_CRL_DET_SEL (0x01 << 2) +#define STB0899_OFFST_CRL_DET_SEL 2 +#define STB0899_WIDTH_CRL_DET_SEL 1 +#define STB0899_CRL_SENSE (0x01 << 1) +#define STB0899_OFFST_CRL_SENSE 1 +#define STB0899_WIDTH_CRL_SENSE 1 +#define STB0899_CRL_PHSERR_CLEAR (0x01 << 0) +#define STB0899_OFFST_CRL_PHSERR_CLEAR 0 +#define STB0899_WIDTH_CRL_PHSERR_CLEAR 1 + +#define STB0899_OFF0_CRL_PHS_INIT 0xf32c +#define STB0899_BASE_CRL_PHS_INIT 0x00000000 +#define STB0899_CRL_PHS_INIT_31 (0x1 << 30) +#define STB0899_OFFST_CRL_PHS_INIT_31 30 +#define STB0899_WIDTH_CRL_PHS_INIT_31 1 +#define STB0899_CRL_LD_INIT_PHASE (0x1 << 24) +#define STB0899_OFFST_CRL_LD_INIT_PHASE 24 +#define STB0899_WIDTH_CRL_LD_INIT_PHASE 1 +#define STB0899_CRL_INIT_PHASE (0xffffff << 0) +#define STB0899_OFFST_CRL_INIT_PHASE 0 +#define STB0899_WIDTH_CRL_INIT_PHASE 24 + +#define STB0899_OFF0_CRL_FREQ_INIT 0xf330 +#define STB0899_BASE_CRL_FREQ_INIT 0x00000000 +#define STB0899_CRL_FREQ_INIT_31 (0x1 << 30) +#define STB0899_OFFST_CRL_FREQ_INIT_31 30 +#define STB0899_WIDTH_CRL_FREQ_INIT_31 1 +#define STB0899_CRL_LD_FREQ_INIT (0x1 << 24) +#define STB0899_OFFST_CRL_LD_FREQ_INIT 24 +#define STB0899_WIDTH_CRL_LD_FREQ_INIT 1 +#define STB0899_CRL_FREQ_INIT (0xffffff << 0) +#define STB0899_OFFST_CRL_FREQ_INIT 0 +#define STB0899_WIDTH_CRL_FREQ_INIT 24 + +#define STB0899_OFF0_CRL_LOOP_GAIN 0xf334 +#define STB0899_BASE_CRL_LOOP_GAIN 0x00000000 +#define STB0899_KCRL2_RSHFT (0xf << 16) +#define STB0899_OFFST_KCRL2_RSHFT 16 +#define STB0899_WIDTH_KCRL2_RSHFT 4 +#define STB0899_KCRL1 (0xf << 12) +#define STB0899_OFFST_KCRL1 12 +#define STB0899_WIDTH_KCRL1 4 +#define STB0899_KCRL1_RSHFT (0xf << 8) +#define STB0899_OFFST_KCRL1_RSHFT 8 +#define STB0899_WIDTH_KCRL1_RSHFT 4 +#define STB0899_KCRL0 (0xf << 4) +#define STB0899_OFFST_KCRL0 4 +#define STB0899_WIDTH_KCRL0 4 +#define STB0899_KCRL0_RSHFT (0xf << 0) +#define STB0899_OFFST_KCRL0_RSHFT 0 +#define STB0899_WIDTH_KCRL0_RSHFT 4 + +#define STB0899_OFF0_CRL_NOM_FREQ 0xf338 +#define STB0899_BASE_CRL_NOM_FREQ 0x00000000 +#define STB0899_CRL_NOM_FREQ (0x3fffffff << 0) +#define STB0899_OFFST_CRL_NOM_FREQ 0 +#define STB0899_WIDTH_CRL_NOM_FREQ 30 + +#define STB0899_OFF0_CRL_SWP_RATE 0xf33c +#define STB0899_BASE_CRL_SWP_RATE 0x00000000 +#define STB0899_CRL_SWP_RATE (0x3fffffff << 0) +#define STB0899_OFFST_CRL_SWP_RATE 0 +#define STB0899_WIDTH_CRL_SWP_RATE 30 + +#define STB0899_OFF0_CRL_MAX_SWP 0xf340 +#define STB0899_BASE_CRL_MAX_SWP 0x00000000 +#define STB0899_CRL_MAX_SWP (0x3fffffff << 0) +#define STB0899_OFFST_CRL_MAX_SWP 0 +#define STB0899_WIDTH_CRL_MAX_SWP 30 + +#define STB0899_OFF0_CRL_LK_CNTRL 0xf344 +#define STB0899_BASE_CRL_LK_CNTRL 0x00000000 + +#define STB0899_OFF0_DECIM_CNTRL 0xf348 +#define STB0899_BASE_DECIM_CNTRL 0x00000000 +#define STB0899_BAND_LIMIT_B (0x01 << 5) +#define STB0899_OFFST_BAND_LIMIT_B 5 +#define STB0899_WIDTH_BAND_LIMIT_B 1 +#define STB0899_WIN_SEL (0x03 << 3) +#define STB0899_OFFST_WIN_SEL 3 +#define STB0899_WIDTH_WIN_SEL 2 +#define STB0899_DECIM_RATE (0x07 << 0) +#define STB0899_OFFST_DECIM_RATE 0 +#define STB0899_WIDTH_DECIM_RATE 3 + +#define STB0899_OFF0_BTR_CNTRL 0xf34c +#define STB0899_BASE_BTR_CNTRL 0x00000000 +#define STB0899_BTR_FREQ_CORR (0x7ff << 4) +#define STB0899_OFFST_BTR_FREQ_CORR 4 +#define STB0899_WIDTH_BTR_FREQ_CORR 11 +#define STB0899_BTR_CLR_LOCK (0x01 << 3) +#define STB0899_OFFST_BTR_CLR_LOCK 3 +#define STB0899_WIDTH_BTR_CLR_LOCK 1 +#define STB0899_BTR_SENSE (0x01 << 2) +#define STB0899_OFFST_BTR_SENSE 2 +#define STB0899_WIDTH_BTR_SENSE 1 +#define STB0899_BTR_ERR_ENA (0x01 << 1) +#define STB0899_OFFST_BTR_ERR_ENA 1 +#define STB0899_WIDTH_BTR_ERR_ENA 1 +#define STB0899_INTRP_PHS_SENSE (0x01 << 0) +#define STB0899_OFFST_INTRP_PHS_SENSE 0 +#define STB0899_WIDTH_INTRP_PHS_SENSE 1 + +#define STB0899_OFF0_BTR_LOOP_GAIN 0xf350 +#define STB0899_BASE_BTR_LOOP_GAIN 0x00000000 +#define STB0899_KBTR2_RSHFT (0x0f << 16) +#define STB0899_OFFST_KBTR2_RSHFT 16 +#define STB0899_WIDTH_KBTR2_RSHFT 4 +#define STB0899_KBTR1 (0x0f << 12) +#define STB0899_OFFST_KBTR1 12 +#define STB0899_WIDTH_KBTR1 4 +#define STB0899_KBTR1_RSHFT (0x0f << 8) +#define STB0899_OFFST_KBTR1_RSHFT 8 +#define STB0899_WIDTH_KBTR1_RSHFT 4 +#define STB0899_KBTR0 (0x0f << 4) +#define STB0899_OFFST_KBTR0 4 +#define STB0899_WIDTH_KBTR0 4 +#define STB0899_KBTR0_RSHFT (0x0f << 0) +#define STB0899_OFFST_KBTR0_RSHFT 0 +#define STB0899_WIDTH_KBTR0_RSHFT 4 + +#define STB0899_OFF0_BTR_PHS_INIT 0xf354 +#define STB0899_BASE_BTR_PHS_INIT 0x00000000 +#define STB0899_BTR_LD_PHASE_INIT (0x01 << 28) +#define STB0899_OFFST_BTR_LD_PHASE_INIT 28 +#define STB0899_WIDTH_BTR_LD_PHASE_INIT 1 +#define STB0899_BTR_INIT_PHASE (0xfffffff << 0) +#define STB0899_OFFST_BTR_INIT_PHASE 0 +#define STB0899_WIDTH_BTR_INIT_PHASE 28 + +#define STB0899_OFF0_BTR_FREQ_INIT 0xf358 +#define STB0899_BASE_BTR_FREQ_INIT 0x00000000 +#define STB0899_BTR_LD_FREQ_INIT (1 << 28) +#define STB0899_OFFST_BTR_LD_FREQ_INIT 28 +#define STB0899_WIDTH_BTR_LD_FREQ_INIT 1 +#define STB0899_BTR_FREQ_INIT (0xfffffff << 0) +#define STB0899_OFFST_BTR_FREQ_INIT 0 +#define STB0899_WIDTH_BTR_FREQ_INIT 28 + +#define STB0899_OFF0_BTR_NOM_FREQ 0xf35c +#define STB0899_BASE_BTR_NOM_FREQ 0x00000000 +#define STB0899_BTR_NOM_FREQ (0xfffffff << 0) +#define STB0899_OFFST_BTR_NOM_FREQ 0 +#define STB0899_WIDTH_BTR_NOM_FREQ 28 + +#define STB0899_OFF0_BTR_LK_CNTRL 0xf360 +#define STB0899_BASE_BTR_LK_CNTRL 0x00000000 +#define STB0899_BTR_MIN_ENERGY (0x0f << 24) +#define STB0899_OFFST_BTR_MIN_ENERGY 24 +#define STB0899_WIDTH_BTR_MIN_ENERGY 4 +#define STB0899_BTR_LOCK_TH_LO (0xff << 16) +#define STB0899_OFFST_BTR_LOCK_TH_LO 16 +#define STB0899_WIDTH_BTR_LOCK_TH_LO 8 +#define STB0899_BTR_LOCK_TH_HI (0xff << 8) +#define STB0899_OFFST_BTR_LOCK_TH_HI 8 +#define STB0899_WIDTH_BTR_LOCK_TH_HI 8 +#define STB0899_BTR_LOCK_GAIN (0x03 << 6) +#define STB0899_OFFST_BTR_LOCK_GAIN 6 +#define STB0899_WIDTH_BTR_LOCK_GAIN 2 +#define STB0899_BTR_LOCK_LEAK (0x3f << 0) +#define STB0899_OFFST_BTR_LOCK_LEAK 0 +#define STB0899_WIDTH_BTR_LOCK_LEAK 6 + +#define STB0899_OFF0_DECN_CNTRL 0xf364 +#define STB0899_BASE_DECN_CNTRL 0x00000000 + +#define STB0899_OFF0_TP_CNTRL 0xf368 +#define STB0899_BASE_TP_CNTRL 0x00000000 + +#define STB0899_OFF0_TP_BUF_STATUS 0xf36c +#define STB0899_BASE_TP_BUF_STATUS 0x00000000 +#define STB0899_TP_BUFFER_FULL (1 << 0) + +#define STB0899_OFF0_DC_ESTIM 0xf37c +#define STB0899_BASE_DC_ESTIM 0x0000 +#define STB0899_I_DC_ESTIMATE (0xff << 8) +#define STB0899_OFFST_I_DC_ESTIMATE 8 +#define STB0899_WIDTH_I_DC_ESTIMATE 8 +#define STB0899_Q_DC_ESTIMATE (0xff << 0) +#define STB0899_OFFST_Q_DC_ESTIMATE 0 +#define STB0899_WIDTH_Q_DC_ESTIMATE 8 + +#define STB0899_OFF0_FLL_CNTRL 0xf310 +#define STB0899_BASE_FLL_CNTRL 0x00000020 +#define STB0899_CRL_FLL_ACC (0x01 << 4) +#define STB0899_OFFST_CRL_FLL_ACC 4 +#define STB0899_WIDTH_CRL_FLL_ACC 1 +#define STB0899_FLL_AVG_PERIOD (0x0f << 0) +#define STB0899_OFFST_FLL_AVG_PERIOD 0 +#define STB0899_WIDTH_FLL_AVG_PERIOD 4 + +#define STB0899_OFF0_FLL_FREQ_WD 0xf314 +#define STB0899_BASE_FLL_FREQ_WD 0x00000020 +#define STB0899_FLL_FREQ_WD (0xffffffff << 0) +#define STB0899_OFFST_FLL_FREQ_WD 0 +#define STB0899_WIDTH_FLL_FREQ_WD 32 + +#define STB0899_OFF0_ANTI_ALIAS_SEL 0xf358 +#define STB0899_BASE_ANTI_ALIAS_SEL 0x00000020 +#define STB0899_ANTI_ALIAS_SELB (0x03 << 0) +#define STB0899_OFFST_ANTI_ALIAS_SELB 0 +#define STB0899_WIDTH_ANTI_ALIAS_SELB 2 + +#define STB0899_OFF0_RRC_ALPHA 0xf35c +#define STB0899_BASE_RRC_ALPHA 0x00000020 +#define STB0899_RRC_ALPHA (0x03 << 0) +#define STB0899_OFFST_RRC_ALPHA 0 +#define STB0899_WIDTH_RRC_ALPHA 2 + +#define STB0899_OFF0_DC_ADAPT_LSHFT 0xf360 +#define STB0899_BASE_DC_ADAPT_LSHFT 0x00000020 +#define STB0899_DC_ADAPT_LSHFT (0x077 << 0) +#define STB0899_OFFST_DC_ADAPT_LSHFT 0 +#define STB0899_WIDTH_DC_ADAPT_LSHFT 3 + +#define STB0899_OFF0_IMB_OFFSET 0xf364 +#define STB0899_BASE_IMB_OFFSET 0x00000020 +#define STB0899_PHS_IMB_COMP (0xff << 8) +#define STB0899_OFFST_PHS_IMB_COMP 8 +#define STB0899_WIDTH_PHS_IMB_COMP 8 +#define STB0899_AMPL_IMB_COMP (0xff << 0) +#define STB0899_OFFST_AMPL_IMB_COMP 0 +#define STB0899_WIDTH_AMPL_IMB_COMP 8 + +#define STB0899_OFF0_IMB_ESTIMATE 0xf368 +#define STB0899_BASE_IMB_ESTIMATE 0x00000020 +#define STB0899_PHS_IMB_ESTIMATE (0xff << 8) +#define STB0899_OFFST_PHS_IMB_ESTIMATE 8 +#define STB0899_WIDTH_PHS_IMB_ESTIMATE 8 +#define STB0899_AMPL_IMB_ESTIMATE (0xff << 0) +#define STB0899_OFFST_AMPL_IMB_ESTIMATE 0 +#define STB0899_WIDTH_AMPL_IMB_ESTIMATE 8 + +#define STB0899_OFF0_IMB_CNTRL 0xf36c +#define STB0899_BASE_IMB_CNTRL 0x00000020 +#define STB0899_PHS_ADAPT_LSHFT (0x07 << 4) +#define STB0899_OFFST_PHS_ADAPT_LSHFT 4 +#define STB0899_WIDTH_PHS_ADAPT_LSHFT 3 +#define STB0899_AMPL_ADAPT_LSHFT (0x07 << 1) +#define STB0899_OFFST_AMPL_ADAPT_LSHFT 1 +#define STB0899_WIDTH_AMPL_ADAPT_LSHFT 3 +#define STB0899_IMB_COMP (0x01 << 0) +#define STB0899_OFFST_IMB_COMP 0 +#define STB0899_WIDTH_IMB_COMP 1 + +#define STB0899_OFF0_IF_AGC_CNTRL2 0xf374 +#define STB0899_BASE_IF_AGC_CNTRL2 0x00000020 +#define STB0899_IF_AGC_LOCK_TH (0xff << 11) +#define STB0899_OFFST_IF_AGC_LOCK_TH 11 +#define STB0899_WIDTH_IF_AGC_LOCK_TH 8 +#define STB0899_IF_AGC_SD_DIV (0xff << 3) +#define STB0899_OFFST_IF_AGC_SD_DIV 3 +#define STB0899_WIDTH_IF_AGC_SD_DIV 8 +#define STB0899_IF_AGC_DUMP_PER (0x07 << 0) +#define STB0899_OFFST_IF_AGC_DUMP_PER 0 +#define STB0899_WIDTH_IF_AGC_DUMP_PER 3 + +#define STB0899_OFF0_DMD_CNTRL2 0xf378 +#define STB0899_BASE_DMD_CNTRL2 0x00000020 +#define STB0899_SPECTRUM_INVERT (0x01 << 2) +#define STB0899_OFFST_SPECTRUM_INVERT 2 +#define STB0899_WIDTH_SPECTRUM_INVERT 1 +#define STB0899_AGC_MODE (0x01 << 1) +#define STB0899_OFFST_AGC_MODE 1 +#define STB0899_WIDTH_AGC_MODE 1 +#define STB0899_CRL_FREQ_ADJ (0x01 << 0) +#define STB0899_OFFST_CRL_FREQ_ADJ 0 +#define STB0899_WIDTH_CRL_FREQ_ADJ 1 + +#define STB0899_OFF0_TP_BUFFER 0xf300 +#define STB0899_BASE_TP_BUFFER 0x00000040 +#define STB0899_TP_BUFFER_IN (0xffff << 0) +#define STB0899_OFFST_TP_BUFFER_IN 0 +#define STB0899_WIDTH_TP_BUFFER_IN 16 + +#define STB0899_OFF0_TP_BUFFER1 0xf304 +#define STB0899_BASE_TP_BUFFER1 0x00000040 +#define STB0899_OFF0_TP_BUFFER2 0xf308 +#define STB0899_BASE_TP_BUFFER2 0x00000040 +#define STB0899_OFF0_TP_BUFFER3 0xf30c +#define STB0899_BASE_TP_BUFFER3 0x00000040 +#define STB0899_OFF0_TP_BUFFER4 0xf310 +#define STB0899_BASE_TP_BUFFER4 0x00000040 +#define STB0899_OFF0_TP_BUFFER5 0xf314 +#define STB0899_BASE_TP_BUFFER5 0x00000040 +#define STB0899_OFF0_TP_BUFFER6 0xf318 +#define STB0899_BASE_TP_BUFFER6 0x00000040 +#define STB0899_OFF0_TP_BUFFER7 0xf31c +#define STB0899_BASE_TP_BUFFER7 0x00000040 +#define STB0899_OFF0_TP_BUFFER8 0xf320 +#define STB0899_BASE_TP_BUFFER8 0x00000040 +#define STB0899_OFF0_TP_BUFFER9 0xf324 +#define STB0899_BASE_TP_BUFFER9 0x00000040 +#define STB0899_OFF0_TP_BUFFER10 0xf328 +#define STB0899_BASE_TP_BUFFER10 0x00000040 +#define STB0899_OFF0_TP_BUFFER11 0xf32c +#define STB0899_BASE_TP_BUFFER11 0x00000040 +#define STB0899_OFF0_TP_BUFFER12 0xf330 +#define STB0899_BASE_TP_BUFFER12 0x00000040 +#define STB0899_OFF0_TP_BUFFER13 0xf334 +#define STB0899_BASE_TP_BUFFER13 0x00000040 +#define STB0899_OFF0_TP_BUFFER14 0xf338 +#define STB0899_BASE_TP_BUFFER14 0x00000040 +#define STB0899_OFF0_TP_BUFFER15 0xf33c +#define STB0899_BASE_TP_BUFFER15 0x00000040 +#define STB0899_OFF0_TP_BUFFER16 0xf340 +#define STB0899_BASE_TP_BUFFER16 0x00000040 +#define STB0899_OFF0_TP_BUFFER17 0xf344 +#define STB0899_BASE_TP_BUFFER17 0x00000040 +#define STB0899_OFF0_TP_BUFFER18 0xf348 +#define STB0899_BASE_TP_BUFFER18 0x00000040 +#define STB0899_OFF0_TP_BUFFER19 0xf34c +#define STB0899_BASE_TP_BUFFER19 0x00000040 +#define STB0899_OFF0_TP_BUFFER20 0xf350 +#define STB0899_BASE_TP_BUFFER20 0x00000040 +#define STB0899_OFF0_TP_BUFFER21 0xf354 +#define STB0899_BASE_TP_BUFFER21 0x00000040 +#define STB0899_OFF0_TP_BUFFER22 0xf358 +#define STB0899_BASE_TP_BUFFER22 0x00000040 +#define STB0899_OFF0_TP_BUFFER23 0xf35c +#define STB0899_BASE_TP_BUFFER23 0x00000040 +#define STB0899_OFF0_TP_BUFFER24 0xf360 +#define STB0899_BASE_TP_BUFFER24 0x00000040 +#define STB0899_OFF0_TP_BUFFER25 0xf364 +#define STB0899_BASE_TP_BUFFER25 0x00000040 +#define STB0899_OFF0_TP_BUFFER26 0xf368 +#define STB0899_BASE_TP_BUFFER26 0x00000040 +#define STB0899_OFF0_TP_BUFFER27 0xf36c +#define STB0899_BASE_TP_BUFFER27 0x00000040 +#define STB0899_OFF0_TP_BUFFER28 0xf370 +#define STB0899_BASE_TP_BUFFER28 0x00000040 +#define STB0899_OFF0_TP_BUFFER29 0xf374 +#define STB0899_BASE_TP_BUFFER29 0x00000040 +#define STB0899_OFF0_TP_BUFFER30 0xf378 +#define STB0899_BASE_TP_BUFFER30 0x00000040 +#define STB0899_OFF0_TP_BUFFER31 0xf37c +#define STB0899_BASE_TP_BUFFER31 0x00000040 +#define STB0899_OFF0_TP_BUFFER32 0xf300 +#define STB0899_BASE_TP_BUFFER32 0x00000060 +#define STB0899_OFF0_TP_BUFFER33 0xf304 +#define STB0899_BASE_TP_BUFFER33 0x00000060 +#define STB0899_OFF0_TP_BUFFER34 0xf308 +#define STB0899_BASE_TP_BUFFER34 0x00000060 +#define STB0899_OFF0_TP_BUFFER35 0xf30c +#define STB0899_BASE_TP_BUFFER35 0x00000060 +#define STB0899_OFF0_TP_BUFFER36 0xf310 +#define STB0899_BASE_TP_BUFFER36 0x00000060 +#define STB0899_OFF0_TP_BUFFER37 0xf314 +#define STB0899_BASE_TP_BUFFER37 0x00000060 +#define STB0899_OFF0_TP_BUFFER38 0xf318 +#define STB0899_BASE_TP_BUFFER38 0x00000060 +#define STB0899_OFF0_TP_BUFFER39 0xf31c +#define STB0899_BASE_TP_BUFFER39 0x00000060 +#define STB0899_OFF0_TP_BUFFER40 0xf320 +#define STB0899_BASE_TP_BUFFER40 0x00000060 +#define STB0899_OFF0_TP_BUFFER41 0xf324 +#define STB0899_BASE_TP_BUFFER41 0x00000060 +#define STB0899_OFF0_TP_BUFFER42 0xf328 +#define STB0899_BASE_TP_BUFFER42 0x00000060 +#define STB0899_OFF0_TP_BUFFER43 0xf32c +#define STB0899_BASE_TP_BUFFER43 0x00000060 +#define STB0899_OFF0_TP_BUFFER44 0xf330 +#define STB0899_BASE_TP_BUFFER44 0x00000060 +#define STB0899_OFF0_TP_BUFFER45 0xf334 +#define STB0899_BASE_TP_BUFFER45 0x00000060 +#define STB0899_OFF0_TP_BUFFER46 0xf338 +#define STB0899_BASE_TP_BUFFER46 0x00000060 +#define STB0899_OFF0_TP_BUFFER47 0xf33c +#define STB0899_BASE_TP_BUFFER47 0x00000060 +#define STB0899_OFF0_TP_BUFFER48 0xf340 +#define STB0899_BASE_TP_BUFFER48 0x00000060 +#define STB0899_OFF0_TP_BUFFER49 0xf344 +#define STB0899_BASE_TP_BUFFER49 0x00000060 +#define STB0899_OFF0_TP_BUFFER50 0xf348 +#define STB0899_BASE_TP_BUFFER50 0x00000060 +#define STB0899_OFF0_TP_BUFFER51 0xf34c +#define STB0899_BASE_TP_BUFFER51 0x00000060 +#define STB0899_OFF0_TP_BUFFER52 0xf350 +#define STB0899_BASE_TP_BUFFER52 0x00000060 +#define STB0899_OFF0_TP_BUFFER53 0xf354 +#define STB0899_BASE_TP_BUFFER53 0x00000060 +#define STB0899_OFF0_TP_BUFFER54 0xf358 +#define STB0899_BASE_TP_BUFFER54 0x00000060 +#define STB0899_OFF0_TP_BUFFER55 0xf35c +#define STB0899_BASE_TP_BUFFER55 0x00000060 +#define STB0899_OFF0_TP_BUFFER56 0xf360 +#define STB0899_BASE_TP_BUFFER56 0x00000060 +#define STB0899_OFF0_TP_BUFFER57 0xf364 +#define STB0899_BASE_TP_BUFFER57 0x00000060 +#define STB0899_OFF0_TP_BUFFER58 0xf368 +#define STB0899_BASE_TP_BUFFER58 0x00000060 +#define STB0899_OFF0_TP_BUFFER59 0xf36c +#define STB0899_BASE_TP_BUFFER59 0x00000060 +#define STB0899_OFF0_TP_BUFFER60 0xf370 +#define STB0899_BASE_TP_BUFFER60 0x00000060 +#define STB0899_OFF0_TP_BUFFER61 0xf374 +#define STB0899_BASE_TP_BUFFER61 0x00000060 +#define STB0899_OFF0_TP_BUFFER62 0xf378 +#define STB0899_BASE_TP_BUFFER62 0x00000060 +#define STB0899_OFF0_TP_BUFFER63 0xf37c +#define STB0899_BASE_TP_BUFFER63 0x00000060 + +#define STB0899_OFF0_RESET_CNTRL 0xf300 +#define STB0899_BASE_RESET_CNTRL 0x00000400 +#define STB0899_DVBS2_RESET (0x01 << 0) +#define STB0899_OFFST_DVBS2_RESET 0 +#define STB0899_WIDTH_DVBS2_RESET 1 + +#define STB0899_OFF0_ACM_ENABLE 0xf304 +#define STB0899_BASE_ACM_ENABLE 0x00000400 +#define STB0899_ACM_ENABLE 1 + +#define STB0899_OFF0_DESCR_CNTRL 0xf30c +#define STB0899_BASE_DESCR_CNTRL 0x00000400 +#define STB0899_OFFST_DESCR_CNTRL 0 +#define STB0899_WIDTH_DESCR_CNTRL 16 + +#define STB0899_OFF0_UWP_CNTRL1 0xf320 +#define STB0899_BASE_UWP_CNTRL1 0x00000400 +#define STB0899_UWP_TH_SOF (0x7fff << 11) +#define STB0899_OFFST_UWP_TH_SOF 11 +#define STB0899_WIDTH_UWP_TH_SOF 15 +#define STB0899_UWP_ESN0_QUANT (0xff << 3) +#define STB0899_OFFST_UWP_ESN0_QUANT 3 +#define STB0899_WIDTH_UWP_ESN0_QUANT 8 +#define STB0899_UWP_ESN0_AVE (0x03 << 1) +#define STB0899_OFFST_UWP_ESN0_AVE 1 +#define STB0899_WIDTH_UWP_ESN0_AVE 2 +#define STB0899_UWP_START (0x01 << 0) +#define STB0899_OFFST_UWP_START 0 +#define STB0899_WIDTH_UWP_START 1 + +#define STB0899_OFF0_UWP_CNTRL2 0xf324 +#define STB0899_BASE_UWP_CNTRL2 0x00000400 +#define STB0899_UWP_MISS_TH (0xff << 16) +#define STB0899_OFFST_UWP_MISS_TH 16 +#define STB0899_WIDTH_UWP_MISS_TH 8 +#define STB0899_FE_FINE_TRK (0xff << 8) +#define STB0899_OFFST_FE_FINE_TRK 8 +#define STB0899_WIDTH_FE_FINE_TRK 8 +#define STB0899_FE_COARSE_TRK (0xff << 0) +#define STB0899_OFFST_FE_COARSE_TRK 0 +#define STB0899_WIDTH_FE_COARSE_TRK 8 + +#define STB0899_OFF0_UWP_STAT1 0xf328 +#define STB0899_BASE_UWP_STAT1 0x00000400 +#define STB0899_UWP_STATE (0x03ff << 15) +#define STB0899_OFFST_UWP_STATE 15 +#define STB0899_WIDTH_UWP_STATE 10 +#define STB0899_UW_MAX_PEAK (0x7fff << 0) +#define STB0899_OFFST_UW_MAX_PEAK 0 +#define STB0899_WIDTH_UW_MAX_PEAK 15 + +#define STB0899_OFF0_UWP_STAT2 0xf32c +#define STB0899_BASE_UWP_STAT2 0x00000400 +#define STB0899_ESNO_EST (0x07ffff << 7) +#define STB0899_OFFST_ESN0_EST 7 +#define STB0899_WIDTH_ESN0_EST 19 +#define STB0899_UWP_DECODE_MOD (0x7f << 0) +#define STB0899_OFFST_UWP_DECODE_MOD 0 +#define STB0899_WIDTH_UWP_DECODE_MOD 7 + +#define STB0899_OFF0_DMD_CORE_ID 0xf334 +#define STB0899_BASE_DMD_CORE_ID 0x00000400 +#define STB0899_CORE_ID (0xffffffff << 0) +#define STB0899_OFFST_CORE_ID 0 +#define STB0899_WIDTH_CORE_ID 32 + +#define STB0899_OFF0_DMD_VERSION_ID 0xf33c +#define STB0899_BASE_DMD_VERSION_ID 0x00000400 +#define STB0899_VERSION_ID (0xff << 0) +#define STB0899_OFFST_VERSION_ID 0 +#define STB0899_WIDTH_VERSION_ID 8 + +#define STB0899_OFF0_DMD_STAT2 0xf340 +#define STB0899_BASE_DMD_STAT2 0x00000400 +#define STB0899_CSM_LOCK (0x01 << 1) +#define STB0899_OFFST_CSM_LOCK 1 +#define STB0899_WIDTH_CSM_LOCK 1 +#define STB0899_UWP_LOCK (0x01 << 0) +#define STB0899_OFFST_UWP_LOCK 0 +#define STB0899_WIDTH_UWP_LOCK 1 + +#define STB0899_OFF0_FREQ_ADJ_SCALE 0xf344 +#define STB0899_BASE_FREQ_ADJ_SCALE 0x00000400 +#define STB0899_FREQ_ADJ_SCALE (0x0fff << 0) +#define STB0899_OFFST_FREQ_ADJ_SCALE 0 +#define STB0899_WIDTH_FREQ_ADJ_SCALE 12 + +#define STB0899_OFF0_UWP_CNTRL3 0xf34c +#define STB0899_BASE_UWP_CNTRL3 0x00000400 +#define STB0899_UWP_TH_TRACK (0x7fff << 15) +#define STB0899_OFFST_UWP_TH_TRACK 15 +#define STB0899_WIDTH_UWP_TH_TRACK 15 +#define STB0899_UWP_TH_ACQ (0x7fff << 0) +#define STB0899_OFFST_UWP_TH_ACQ 0 +#define STB0899_WIDTH_UWP_TH_ACQ 15 + +#define STB0899_OFF0_SYM_CLK_SEL 0xf350 +#define STB0899_BASE_SYM_CLK_SEL 0x00000400 +#define STB0899_SYM_CLK_SEL (0x03 << 0) +#define STB0899_OFFST_SYM_CLK_SEL 0 +#define STB0899_WIDTH_SYM_CLK_SEL 2 + +#define STB0899_OFF0_SOF_SRCH_TO 0xf354 +#define STB0899_BASE_SOF_SRCH_TO 0x00000400 +#define STB0899_SOF_SEARCH_TIMEOUT (0x3fffff << 0) +#define STB0899_OFFST_SOF_SEARCH_TIMEOUT 0 +#define STB0899_WIDTH_SOF_SEARCH_TIMEOUT 22 + +#define STB0899_OFF0_ACQ_CNTRL1 0xf358 +#define STB0899_BASE_ACQ_CNTRL1 0x00000400 +#define STB0899_FE_FINE_ACQ (0xff << 8) +#define STB0899_OFFST_FE_FINE_ACQ 8 +#define STB0899_WIDTH_FE_FINE_ACQ 8 +#define STB0899_FE_COARSE_ACQ (0xff << 0) +#define STB0899_OFFST_FE_COARSE_ACQ 0 +#define STB0899_WIDTH_FE_COARSE_ACQ 8 + +#define STB0899_OFF0_ACQ_CNTRL2 0xf35c +#define STB0899_BASE_ACQ_CNTRL2 0x00000400 +#define STB0899_ZIGZAG (0x01 << 25) +#define STB0899_OFFST_ZIGZAG 25 +#define STB0899_WIDTH_ZIGZAG 1 +#define STB0899_NUM_STEPS (0xff << 17) +#define STB0899_OFFST_NUM_STEPS 17 +#define STB0899_WIDTH_NUM_STEPS 8 +#define STB0899_FREQ_STEPSIZE (0x1ffff << 0) +#define STB0899_OFFST_FREQ_STEPSIZE 0 +#define STB0899_WIDTH_FREQ_STEPSIZE 17 + +#define STB0899_OFF0_ACQ_CNTRL3 0xf360 +#define STB0899_BASE_ACQ_CNTRL3 0x00000400 +#define STB0899_THRESHOLD_SCL (0x3f << 23) +#define STB0899_OFFST_THRESHOLD_SCL 23 +#define STB0899_WIDTH_THRESHOLD_SCL 6 +#define STB0899_UWP_TH_SRCH (0x7fff << 8) +#define STB0899_OFFST_UWP_TH_SRCH 8 +#define STB0899_WIDTH_UWP_TH_SRCH 15 +#define STB0899_AUTO_REACQUIRE (0x01 << 7) +#define STB0899_OFFST_AUTO_REACQUIRE 7 +#define STB0899_WIDTH_AUTO_REACQUIRE 1 +#define STB0899_TRACK_LOCK_SEL (0x01 << 6) +#define STB0899_OFFST_TRACK_LOCK_SEL 6 +#define STB0899_WIDTH_TRACK_LOCK_SEL 1 +#define STB0899_ACQ_SEARCH_MODE (0x03 << 4) +#define STB0899_OFFST_ACQ_SEARCH_MODE 4 +#define STB0899_WIDTH_ACQ_SEARCH_MODE 2 +#define STB0899_CONFIRM_FRAMES (0x0f << 0) +#define STB0899_OFFST_CONFIRM_FRAMES 0 +#define STB0899_WIDTH_CONFIRM_FRAMES 4 + +#define STB0899_OFF0_FE_SETTLE 0xf364 +#define STB0899_BASE_FE_SETTLE 0x00000400 +#define STB0899_SETTLING_TIME (0x3fffff << 0) +#define STB0899_OFFST_SETTLING_TIME 0 +#define STB0899_WIDTH_SETTLING_TIME 22 + +#define STB0899_OFF0_AC_DWELL 0xf368 +#define STB0899_BASE_AC_DWELL 0x00000400 +#define STB0899_DWELL_TIME (0x3fffff << 0) +#define STB0899_OFFST_DWELL_TIME 0 +#define STB0899_WIDTH_DWELL_TIME 22 + +#define STB0899_OFF0_ACQUIRE_TRIG 0xf36c +#define STB0899_BASE_ACQUIRE_TRIG 0x00000400 +#define STB0899_ACQUIRE (0x01 << 0) +#define STB0899_OFFST_ACQUIRE 0 +#define STB0899_WIDTH_ACQUIRE 1 + +#define STB0899_OFF0_LOCK_LOST 0xf370 +#define STB0899_BASE_LOCK_LOST 0x00000400 +#define STB0899_LOCK_LOST (0x01 << 0) +#define STB0899_OFFST_LOCK_LOST 0 +#define STB0899_WIDTH_LOCK_LOST 1 + +#define STB0899_OFF0_ACQ_STAT1 0xf374 +#define STB0899_BASE_ACQ_STAT1 0x00000400 +#define STB0899_STEP_FREQ (0x1fffff << 11) +#define STB0899_OFFST_STEP_FREQ 11 +#define STB0899_WIDTH_STEP_FREQ 21 +#define STB0899_ACQ_STATE (0x07 << 8) +#define STB0899_OFFST_ACQ_STATE 8 +#define STB0899_WIDTH_ACQ_STATE 3 +#define STB0899_UW_DETECT_COUNT (0xff << 0) +#define STB0899_OFFST_UW_DETECT_COUNT 0 +#define STB0899_WIDTH_UW_DETECT_COUNT 8 + +#define STB0899_OFF0_ACQ_TIMEOUT 0xf378 +#define STB0899_BASE_ACQ_TIMEOUT 0x00000400 +#define STB0899_ACQ_TIMEOUT (0x3fffff << 0) +#define STB0899_OFFST_ACQ_TIMEOUT 0 +#define STB0899_WIDTH_ACQ_TIMEOUT 22 + +#define STB0899_OFF0_ACQ_TIME 0xf37c +#define STB0899_BASE_ACQ_TIME 0x00000400 +#define STB0899_ACQ_TIME_SYM (0xffffff << 0) +#define STB0899_OFFST_ACQ_TIME_SYM 0 +#define STB0899_WIDTH_ACQ_TIME_SYM 24 + +#define STB0899_OFF0_FINAL_AGC_CNTRL 0xf308 +#define STB0899_BASE_FINAL_AGC_CNTRL 0x00000440 +#define STB0899_FINAL_GAIN_INIT (0x3fff << 12) +#define STB0899_OFFST_FINAL_GAIN_INIT 12 +#define STB0899_WIDTH_FINAL_GAIN_INIT 14 +#define STB0899_FINAL_LOOP_GAIN (0x0f << 8) +#define STB0899_OFFST_FINAL_LOOP_GAIN 8 +#define STB0899_WIDTH_FINAL_LOOP_GAIN 4 +#define STB0899_FINAL_LD_GAIN_INIT (0x01 << 7) +#define STB0899_OFFST_FINAL_LD_GAIN_INIT 7 +#define STB0899_WIDTH_FINAL_LD_GAIN_INIT 1 +#define STB0899_FINAL_AGC_REF (0x7f << 0) +#define STB0899_OFFST_FINAL_AGC_REF 0 +#define STB0899_WIDTH_FINAL_AGC_REF 7 + +#define STB0899_OFF0_FINAL_AGC_GAIN 0xf30c +#define STB0899_BASE_FINAL_AGC_GAIN 0x00000440 +#define STB0899_FINAL_AGC_GAIN (0x3fff << 0) +#define STB0899_OFFST_FINAL_AGC_GAIN 0 +#define STB0899_WIDTH_FINAL_AGC_GAIN 14 + +#define STB0899_OFF0_EQUALIZER_INIT 0xf310 +#define STB0899_BASE_EQUALIZER_INIT 0x00000440 +#define STB0899_EQ_SRST (0x01 << 1) +#define STB0899_OFFST_EQ_SRST 1 +#define STB0899_WIDTH_EQ_SRST 1 +#define STB0899_EQ_INIT (0x01 << 0) +#define STB0899_OFFST_EQ_INIT 0 +#define STB0899_WIDTH_EQ_INIT 1 + +#define STB0899_OFF0_EQ_CNTRL 0xf314 +#define STB0899_BASE_EQ_CNTRL 0x00000440 +#define STB0899_EQ_ADAPT_MODE (0x01 << 18) +#define STB0899_OFFST_EQ_ADAPT_MODE 18 +#define STB0899_WIDTH_EQ_ADAPT_MODE 1 +#define STB0899_EQ_DELAY (0x0f << 14) +#define STB0899_OFFST_EQ_DELAY 14 +#define STB0899_WIDTH_EQ_DELAY 4 +#define STB0899_EQ_QUANT_LEVEL (0xff << 6) +#define STB0899_OFFST_EQ_QUANT_LEVEL 6 +#define STB0899_WIDTH_EQ_QUANT_LEVEL 8 +#define STB0899_EQ_DISABLE_UPDATE (0x01 << 5) +#define STB0899_OFFST_EQ_DISABLE_UPDATE 5 +#define STB0899_WIDTH_EQ_DISABLE_UPDATE 1 +#define STB0899_EQ_BYPASS (0x01 << 4) +#define STB0899_OFFST_EQ_BYPASS 4 +#define STB0899_WIDTH_EQ_BYPASS 1 +#define STB0899_EQ_SHIFT (0x0f << 0) +#define STB0899_OFFST_EQ_SHIFT 0 +#define STB0899_WIDTH_EQ_SHIFT 4 + +#define STB0899_OFF0_EQ_I_INIT_COEFF_0 0xf320 +#define STB0899_OFF1_EQ_I_INIT_COEFF_1 0xf324 +#define STB0899_OFF2_EQ_I_INIT_COEFF_2 0xf328 +#define STB0899_OFF3_EQ_I_INIT_COEFF_3 0xf32c +#define STB0899_OFF4_EQ_I_INIT_COEFF_4 0xf330 +#define STB0899_OFF5_EQ_I_INIT_COEFF_5 0xf334 +#define STB0899_OFF6_EQ_I_INIT_COEFF_6 0xf338 +#define STB0899_OFF7_EQ_I_INIT_COEFF_7 0xf33c +#define STB0899_OFF8_EQ_I_INIT_COEFF_8 0xf340 +#define STB0899_OFF9_EQ_I_INIT_COEFF_9 0xf344 +#define STB0899_OFFa_EQ_I_INIT_COEFF_10 0xf348 +#define STB0899_BASE_EQ_I_INIT_COEFF_N 0x00000440 +#define STB0899_EQ_I_INIT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_I_INIT_COEFF_N 0 +#define STB0899_WIDTH_EQ_I_INIT_COEFF_N 12 + +#define STB0899_OFF0_EQ_Q_INIT_COEFF_0 0xf350 +#define STB0899_OFF1_EQ_Q_INIT_COEFF_1 0xf354 +#define STB0899_OFF2_EQ_Q_INIT_COEFF_2 0xf358 +#define STB0899_OFF3_EQ_Q_INIT_COEFF_3 0xf35c +#define STB0899_OFF4_EQ_Q_INIT_COEFF_4 0xf360 +#define STB0899_OFF5_EQ_Q_INIT_COEFF_5 0xf364 +#define STB0899_OFF6_EQ_Q_INIT_COEFF_6 0xf368 +#define STB0899_OFF7_EQ_Q_INIT_COEFF_7 0xf36c +#define STB0899_OFF8_EQ_Q_INIT_COEFF_8 0xf370 +#define STB0899_OFF9_EQ_Q_INIT_COEFF_9 0xf374 +#define STB0899_OFFa_EQ_Q_INIT_COEFF_10 0xf378 +#define STB0899_BASE_EQ_Q_INIT_COEFF_N 0x00000440 +#define STB0899_EQ_Q_INIT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_Q_INIT_COEFF_N 0 +#define STB0899_WIDTH_EQ_Q_INIT_COEFF_N 12 + +#define STB0899_OFF0_EQ_I_OUT_COEFF_0 0xf300 +#define STB0899_OFF1_EQ_I_OUT_COEFF_1 0xf304 +#define STB0899_OFF2_EQ_I_OUT_COEFF_2 0xf308 +#define STB0899_OFF3_EQ_I_OUT_COEFF_3 0xf30c +#define STB0899_OFF4_EQ_I_OUT_COEFF_4 0xf310 +#define STB0899_OFF5_EQ_I_OUT_COEFF_5 0xf314 +#define STB0899_OFF6_EQ_I_OUT_COEFF_6 0xf318 +#define STB0899_OFF7_EQ_I_OUT_COEFF_7 0xf31c +#define STB0899_OFF8_EQ_I_OUT_COEFF_8 0xf320 +#define STB0899_OFF9_EQ_I_OUT_COEFF_9 0xf324 +#define STB0899_OFFa_EQ_I_OUT_COEFF_10 0xf328 +#define STB0899_BASE_EQ_I_OUT_COEFF_N 0x00000460 +#define STB0899_EQ_I_OUT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_I_OUT_COEFF_N 0 +#define STB0899_WIDTH_EQ_I_OUT_COEFF_N 12 + +#define STB0899_OFF0_EQ_Q_OUT_COEFF_0 0xf330 +#define STB0899_OFF1_EQ_Q_OUT_COEFF_1 0xf334 +#define STB0899_OFF2_EQ_Q_OUT_COEFF_2 0xf338 +#define STB0899_OFF3_EQ_Q_OUT_COEFF_3 0xf33c +#define STB0899_OFF4_EQ_Q_OUT_COEFF_4 0xf340 +#define STB0899_OFF5_EQ_Q_OUT_COEFF_5 0xf344 +#define STB0899_OFF6_EQ_Q_OUT_COEFF_6 0xf348 +#define STB0899_OFF7_EQ_Q_OUT_COEFF_7 0xf34c +#define STB0899_OFF8_EQ_Q_OUT_COEFF_8 0xf350 +#define STB0899_OFF9_EQ_Q_OUT_COEFF_9 0xf354 +#define STB0899_OFFa_EQ_Q_OUT_COEFF_10 0xf358 +#define STB0899_BASE_EQ_Q_OUT_COEFF_N 0x00000460 +#define STB0899_EQ_Q_OUT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_Q_OUT_COEFF_N 0 +#define STB0899_WIDTH_EQ_Q_OUT_COEFF_N 12 + +/* S2 FEC */ +#define STB0899_OFF0_BLOCK_LNGTH 0xfa04 +#define STB0899_BASE_BLOCK_LNGTH 0x00000000 +#define STB0899_BLOCK_LENGTH (0xff << 0) +#define STB0899_OFFST_BLOCK_LENGTH 0 +#define STB0899_WIDTH_BLOCK_LENGTH 8 + +#define STB0899_OFF0_ROW_STR 0xfa08 +#define STB0899_BASE_ROW_STR 0x00000000 +#define STB0899_ROW_STRIDE (0xff << 0) +#define STB0899_OFFST_ROW_STRIDE 0 +#define STB0899_WIDTH_ROW_STRIDE 8 + +#define STB0899_OFF0_MAX_ITER 0xfa0c +#define STB0899_BASE_MAX_ITER 0x00000000 +#define STB0899_MAX_ITERATIONS (0xff << 0) +#define STB0899_OFFST_MAX_ITERATIONS 0 +#define STB0899_WIDTH_MAX_ITERATIONS 8 + +#define STB0899_OFF0_BN_END_ADDR 0xfa10 +#define STB0899_BASE_BN_END_ADDR 0x00000000 +#define STB0899_BN_END_ADDR (0x0fff << 0) +#define STB0899_OFFST_BN_END_ADDR 0 +#define STB0899_WIDTH_BN_END_ADDR 12 + +#define STB0899_OFF0_CN_END_ADDR 0xfa14 +#define STB0899_BASE_CN_END_ADDR 0x00000000 +#define STB0899_CN_END_ADDR (0x0fff << 0) +#define STB0899_OFFST_CN_END_ADDR 0 +#define STB0899_WIDTH_CN_END_ADDR 12 + +#define STB0899_OFF0_INFO_LENGTH 0xfa1c +#define STB0899_BASE_INFO_LENGTH 0x00000000 +#define STB0899_INFO_LENGTH (0xff << 0) +#define STB0899_OFFST_INFO_LENGTH 0 +#define STB0899_WIDTH_INFO_LENGTH 8 + +#define STB0899_OFF0_BOT_ADDR 0xfa20 +#define STB0899_BASE_BOT_ADDR 0x00000000 +#define STB0899_BOTTOM_BASE_ADDR (0x03ff << 0) +#define STB0899_OFFST_BOTTOM_BASE_ADDR 0 +#define STB0899_WIDTH_BOTTOM_BASE_ADDR 10 + +#define STB0899_OFF0_BCH_BLK_LN 0xfa24 +#define STB0899_BASE_BCH_BLK_LN 0x00000000 +#define STB0899_BCH_BLOCK_LENGTH (0xffff << 0) +#define STB0899_OFFST_BCH_BLOCK_LENGTH 0 +#define STB0899_WIDTH_BCH_BLOCK_LENGTH 16 + +#define STB0899_OFF0_BCH_T 0xfa28 +#define STB0899_BASE_BCH_T 0x00000000 +#define STB0899_BCH_T (0x0f << 0) +#define STB0899_OFFST_BCH_T 0 +#define STB0899_WIDTH_BCH_T 4 + +#define STB0899_OFF0_CNFG_MODE 0xfa00 +#define STB0899_BASE_CNFG_MODE 0x00000800 +#define STB0899_MODCOD (0x1f << 2) +#define STB0899_OFFST_MODCOD 2 +#define STB0899_WIDTH_MODCOD 5 +#define STB0899_MODCOD_SEL (0x01 << 1) +#define STB0899_OFFST_MODCOD_SEL 1 +#define STB0899_WIDTH_MODCOD_SEL 1 +#define STB0899_CONFIG_MODE (0x01 << 0) +#define STB0899_OFFST_CONFIG_MODE 0 +#define STB0899_WIDTH_CONFIG_MODE 1 + +#define STB0899_OFF0_LDPC_STAT 0xfa04 +#define STB0899_BASE_LDPC_STAT 0x00000800 +#define STB0899_ITERATION (0xff << 3) +#define STB0899_OFFST_ITERATION 3 +#define STB0899_WIDTH_ITERATION 8 +#define STB0899_LDPC_DEC_STATE (0x07 << 0) +#define STB0899_OFFST_LDPC_DEC_STATE 0 +#define STB0899_WIDTH_LDPC_DEC_STATE 3 + +#define STB0899_OFF0_ITER_SCALE 0xfa08 +#define STB0899_BASE_ITER_SCALE 0x00000800 +#define STB0899_ITERATION_SCALE (0xff << 0) +#define STB0899_OFFST_ITERATION_SCALE 0 +#define STB0899_WIDTH_ITERATION_SCALE 8 + +#define STB0899_OFF0_INPUT_MODE 0xfa0c +#define STB0899_BASE_INPUT_MODE 0x00000800 +#define STB0899_SD_BLOCK1_STREAM0 (0x01 << 0) +#define STB0899_OFFST_SD_BLOCK1_STREAM0 0 +#define STB0899_WIDTH_SD_BLOCK1_STREAM0 1 + +#define STB0899_OFF0_LDPCDECRST 0xfa10 +#define STB0899_BASE_LDPCDECRST 0x00000800 +#define STB0899_LDPC_DEC_RST (0x01 << 0) +#define STB0899_OFFST_LDPC_DEC_RST 0 +#define STB0899_WIDTH_LDPC_DEC_RST 1 + +#define STB0899_OFF0_CLK_PER_BYTE_RW 0xfa14 +#define STB0899_BASE_CLK_PER_BYTE_RW 0x00000800 +#define STB0899_CLKS_PER_BYTE (0x0f << 0) +#define STB0899_OFFST_CLKS_PER_BYTE 0 +#define STB0899_WIDTH_CLKS_PER_BYTE 5 + +#define STB0899_OFF0_BCH_ERRORS 0xfa18 +#define STB0899_BASE_BCH_ERRORS 0x00000800 +#define STB0899_BCH_ERRORS (0x0f << 0) +#define STB0899_OFFST_BCH_ERRORS 0 +#define STB0899_WIDTH_BCH_ERRORS 4 + +#define STB0899_OFF0_LDPC_ERRORS 0xfa1c +#define STB0899_BASE_LDPC_ERRORS 0x00000800 +#define STB0899_LDPC_ERRORS (0xffff << 0) +#define STB0899_OFFST_LDPC_ERRORS 0 +#define STB0899_WIDTH_LDPC_ERRORS 16 + +#define STB0899_OFF0_BCH_MODE 0xfa20 +#define STB0899_BASE_BCH_MODE 0x00000800 +#define STB0899_BCH_CORRECT_N (0x01 << 1) +#define STB0899_OFFST_BCH_CORRECT_N 1 +#define STB0899_WIDTH_BCH_CORRECT_N 1 +#define STB0899_FULL_BYPASS (0x01 << 0) +#define STB0899_OFFST_FULL_BYPASS 0 +#define STB0899_WIDTH_FULL_BYPASS 1 + +#define STB0899_OFF0_ERR_ACC_PER 0xfa24 +#define STB0899_BASE_ERR_ACC_PER 0x00000800 +#define STB0899_BCH_ERR_ACC_PERIOD (0x0f << 0) +#define STB0899_OFFST_BCH_ERR_ACC_PERIOD 0 +#define STB0899_WIDTH_BCH_ERR_ACC_PERIOD 4 + +#define STB0899_OFF0_BCH_ERR_ACC 0xfa28 +#define STB0899_BASE_BCH_ERR_ACC 0x00000800 +#define STB0899_BCH_ERR_ACCUM (0xff << 0) +#define STB0899_OFFST_BCH_ERR_ACCUM 0 +#define STB0899_WIDTH_BCH_ERR_ACCUM 8 + +#define STB0899_OFF0_FEC_CORE_ID_REG 0xfa2c +#define STB0899_BASE_FEC_CORE_ID_REG 0x00000800 +#define STB0899_FEC_CORE_ID (0xffffffff << 0) +#define STB0899_OFFST_FEC_CORE_ID 0 +#define STB0899_WIDTH_FEC_CORE_ID 32 + +#define STB0899_OFF0_FEC_VER_ID_REG 0xfa34 +#define STB0899_BASE_FEC_VER_ID_REG 0x00000800 +#define STB0899_FEC_VER_ID (0xff << 0) +#define STB0899_OFFST_FEC_VER_ID 0 +#define STB0899_WIDTH_FEC_VER_ID 8 + +#define STB0899_OFF0_FEC_TP_SEL 0xfa38 +#define STB0899_BASE_FEC_TP_SEL 0x00000800 + +#define STB0899_OFF0_CSM_CNTRL1 0xf310 +#define STB0899_BASE_CSM_CNTRL1 0x00000400 +#define STB0899_CSM_FORCE_FREQLOCK (0x01 << 19) +#define STB0899_OFFST_CSM_FORCE_FREQLOCK 19 +#define STB0899_WIDTH_CSM_FORCE_FREQLOCK 1 +#define STB0899_CSM_FREQ_LOCKSTATE (0x01 << 18) +#define STB0899_OFFST_CSM_FREQ_LOCKSTATE 18 +#define STB0899_WIDTH_CSM_FREQ_LOCKSTATE 1 +#define STB0899_CSM_AUTO_PARAM (0x01 << 17) +#define STB0899_OFFST_CSM_AUTO_PARAM 17 +#define STB0899_WIDTH_CSM_AUTO_PARAM 1 +#define STB0899_FE_LOOP_SHIFT (0x07 << 14) +#define STB0899_OFFST_FE_LOOP_SHIFT 14 +#define STB0899_WIDTH_FE_LOOP_SHIFT 3 +#define STB0899_CSM_AGC_SHIFT (0x07 << 11) +#define STB0899_OFFST_CSM_AGC_SHIFT 11 +#define STB0899_WIDTH_CSM_AGC_SHIFT 3 +#define STB0899_CSM_AGC_GAIN (0x1ff << 2) +#define STB0899_OFFST_CSM_AGC_GAIN 2 +#define STB0899_WIDTH_CSM_AGC_GAIN 9 +#define STB0899_CSM_TWO_PASS (0x01 << 1) +#define STB0899_OFFST_CSM_TWO_PASS 1 +#define STB0899_WIDTH_CSM_TWO_PASS 1 +#define STB0899_CSM_DVT_TABLE (0x01 << 0) +#define STB0899_OFFST_CSM_DVT_TABLE 0 +#define STB0899_WIDTH_CSM_DVT_TABLE 1 + +#define STB0899_OFF0_CSM_CNTRL2 0xf314 +#define STB0899_BASE_CSM_CNTRL2 0x00000400 +#define STB0899_CSM_GAMMA_RHO_ACQ (0x1ff << 9) +#define STB0899_OFFST_CSM_GAMMA_RHOACQ 9 +#define STB0899_WIDTH_CSM_GAMMA_RHOACQ 9 +#define STB0899_CSM_GAMMA_ACQ (0x1ff << 0) +#define STB0899_OFFST_CSM_GAMMA_ACQ 0 +#define STB0899_WIDTH_CSM_GAMMA_ACQ 9 + +#define STB0899_OFF0_CSM_CNTRL3 0xf318 +#define STB0899_BASE_CSM_CNTRL3 0x00000400 +#define STB0899_CSM_GAMMA_RHO_TRACK (0x1ff << 9) +#define STB0899_OFFST_CSM_GAMMA_RHOTRACK 9 +#define STB0899_WIDTH_CSM_GAMMA_RHOTRACK 9 +#define STB0899_CSM_GAMMA_TRACK (0x1ff << 0) +#define STB0899_OFFST_CSM_GAMMA_TRACK 0 +#define STB0899_WIDTH_CSM_GAMMA_TRACK 9 + +#define STB0899_OFF0_CSM_CNTRL4 0xf31c +#define STB0899_BASE_CSM_CNTRL4 0x00000400 +#define STB0899_CSM_PHASEDIFF_THRESH (0x0f << 8) +#define STB0899_OFFST_CSM_PHASEDIFF_THRESH 8 +#define STB0899_WIDTH_CSM_PHASEDIFF_THRESH 4 +#define STB0899_CSM_LOCKCOUNT_THRESH (0xff << 0) +#define STB0899_OFFST_CSM_LOCKCOUNT_THRESH 0 +#define STB0899_WIDTH_CSM_LOCKCOUNT_THRESH 8 + +/* Check on chapter 8 page 42 */ +#define STB0899_ERRCTRL1 0xf574 +#define STB0899_ERRCTRL2 0xf575 +#define STB0899_ERRCTRL3 0xf576 +#define STB0899_ERR_SRC_S1 (0x1f << 3) +#define STB0899_OFFST_ERR_SRC_S1 3 +#define STB0899_WIDTH_ERR_SRC_S1 5 +#define STB0899_ERR_SRC_S2 (0x0f << 0) +#define STB0899_OFFST_ERR_SRC_S2 0 +#define STB0899_WIDTH_ERR_SRC_S2 4 +#define STB0899_NOE (0x07 << 0) +#define STB0899_OFFST_NOE 0 +#define STB0899_WIDTH_NOE 3 + +#define STB0899_ECNT1M 0xf524 +#define STB0899_ECNT1L 0xf525 +#define STB0899_ECNT2M 0xf526 +#define STB0899_ECNT2L 0xf527 +#define STB0899_ECNT3M 0xf528 +#define STB0899_ECNT3L 0xf529 + +#define STB0899_DMONMSK1 0xf57b +#define STB0899_DMONMSK1_WAIT_1STEP (1 << 7) +#define STB0899_DMONMSK1_FREE_14 (1 << 6) +#define STB0899_DMONMSK1_AVRGVIT_CALC (1 << 5) +#define STB0899_DMONMSK1_FREE_12 (1 << 4) +#define STB0899_DMONMSK1_FREE_11 (1 << 3) +#define STB0899_DMONMSK1_B0DIV_CALC (1 << 2) +#define STB0899_DMONMSK1_KDIVB1_CALC (1 << 1) +#define STB0899_DMONMSK1_KDIVB2_CALC (1 << 0) + +#define STB0899_DMONMSK0 0xf57c +#define STB0899_DMONMSK0_SMOTTH_CALC (1 << 7) +#define STB0899_DMONMSK0_FREE_6 (1 << 6) +#define STB0899_DMONMSK0_SIGPOWER_CALC (1 << 5) +#define STB0899_DMONMSK0_QSEUIL_CALC (1 << 4) +#define STB0899_DMONMSK0_FREE_3 (1 << 3) +#define STB0899_DMONMSK0_FREE_2 (1 << 2) +#define STB0899_DMONMSK0_KVDIVB1_CALC (1 << 1) +#define STB0899_DMONMSK0_KVDIVB2_CALC (1 << 0) + +#define STB0899_TSULC 0xf549 +#define STB0899_ULNOSYNCBYTES (0x01 << 7) +#define STB0899_OFFST_ULNOSYNCBYTES 7 +#define STB0899_WIDTH_ULNOSYNCBYTES 1 +#define STB0899_ULPARITY_ON (0x01 << 6) +#define STB0899_OFFST_ULPARITY_ON 6 +#define STB0899_WIDTH_ULPARITY_ON 1 +#define STB0899_ULSYNCOUTRS (0x01 << 5) +#define STB0899_OFFST_ULSYNCOUTRS 5 +#define STB0899_WIDTH_ULSYNCOUTRS 1 +#define STB0899_ULDSS_PACKETS (0x01 << 0) +#define STB0899_OFFST_ULDSS_PACKETS 0 +#define STB0899_WIDTH_ULDSS_PACKETS 1 + +#define STB0899_TSLPL 0xf54b +#define STB0899_LLDVBS2_MODE (0x01 << 4) +#define STB0899_OFFST_LLDVBS2_MODE 4 +#define STB0899_WIDTH_LLDVBS2_MODE 1 +#define STB0899_LLISSYI_ON (0x01 << 3) +#define STB0899_OFFST_LLISSYI_ON 3 +#define STB0899_WIDTH_LLISSYI_ON 1 +#define STB0899_LLNPD_ON (0x01 << 2) +#define STB0899_OFFST_LLNPD_ON 2 +#define STB0899_WIDTH_LLNPD_ON 1 +#define STB0899_LLCRC8_ON (0x01 << 1) +#define STB0899_OFFST_LLCRC8_ON 1 +#define STB0899_WIDTH_LLCRC8_ON 1 + +#define STB0899_TSCFGH 0xf54c +#define STB0899_OUTRS_PS (0x01 << 6) +#define STB0899_OFFST_OUTRS_PS 6 +#define STB0899_WIDTH_OUTRS_PS 1 +#define STB0899_SYNCBYTE (0x01 << 5) +#define STB0899_OFFST_SYNCBYTE 5 +#define STB0899_WIDTH_SYNCBYTE 1 +#define STB0899_PFBIT (0x01 << 4) +#define STB0899_OFFST_PFBIT 4 +#define STB0899_WIDTH_PFBIT 1 +#define STB0899_ERR_BIT (0x01 << 3) +#define STB0899_OFFST_ERR_BIT 3 +#define STB0899_WIDTH_ERR_BIT 1 +#define STB0899_MPEG (0x01 << 2) +#define STB0899_OFFST_MPEG 2 +#define STB0899_WIDTH_MPEG 1 +#define STB0899_CLK_POL (0x01 << 1) +#define STB0899_OFFST_CLK_POL 1 +#define STB0899_WIDTH_CLK_POL 1 +#define STB0899_FORCE0 (0x01 << 0) +#define STB0899_OFFST_FORCE0 0 +#define STB0899_WIDTH_FORCE0 1 + +#define STB0899_TSCFGM 0xf54d +#define STB0899_LLPRIORITY (0x01 << 3) +#define STB0899_OFFST_LLPRIORIY 3 +#define STB0899_WIDTH_LLPRIORITY 1 +#define STB0899_EN188 (0x01 << 2) +#define STB0899_OFFST_EN188 2 +#define STB0899_WIDTH_EN188 1 + +#define STB0899_TSCFGL 0xf54e +#define STB0899_DEL_ERRPCK (0x01 << 7) +#define STB0899_OFFST_DEL_ERRPCK 7 +#define STB0899_WIDTH_DEL_ERRPCK 1 +#define STB0899_ERRFLAGSTD (0x01 << 5) +#define STB0899_OFFST_ERRFLAGSTD 5 +#define STB0899_WIDTH_ERRFLAGSTD 1 +#define STB0899_MPEGERR (0x01 << 4) +#define STB0899_OFFST_MPEGERR 4 +#define STB0899_WIDTH_MPEGERR 1 +#define STB0899_BCH_CHK (0x01 << 3) +#define STB0899_OFFST_BCH_CHK 5 +#define STB0899_WIDTH_BCH_CHK 1 +#define STB0899_CRC8CHK (0x01 << 2) +#define STB0899_OFFST_CRC8CHK 2 +#define STB0899_WIDTH_CRC8CHK 1 +#define STB0899_SPEC_INFO (0x01 << 1) +#define STB0899_OFFST_SPEC_INFO 1 +#define STB0899_WIDTH_SPEC_INFO 1 +#define STB0899_LOW_PRIO_CLK (0x01 << 0) +#define STB0899_OFFST_LOW_PRIO_CLK 0 +#define STB0899_WIDTH_LOW_PRIO_CLK 1 +#define STB0899_ERROR_NORM (0x00 << 0) +#define STB0899_OFFST_ERROR_NORM 0 +#define STB0899_WIDTH_ERROR_NORM 0 + +#define STB0899_TSOUT 0xf54f +#define STB0899_RSSYNCDEL 0xf550 +#define STB0899_TSINHDELH 0xf551 +#define STB0899_TSINHDELM 0xf552 +#define STB0899_TSINHDELL 0xf553 +#define STB0899_TSLLSTKM 0xf55a +#define STB0899_TSLLSTKL 0xf55b +#define STB0899_TSULSTKM 0xf55c +#define STB0899_TSULSTKL 0xf55d +#define STB0899_TSSTATUS 0xf561 + +#define STB0899_PDELCTRL 0xf600 +#define STB0899_INVERT_RES (0x01 << 7) +#define STB0899_OFFST_INVERT_RES 7 +#define STB0899_WIDTH_INVERT_RES 1 +#define STB0899_FORCE_ACCEPTED (0x01 << 6) +#define STB0899_OFFST_FORCE_ACCEPTED 6 +#define STB0899_WIDTH_FORCE_ACCEPTED 1 +#define STB0899_FILTER_EN (0x01 << 5) +#define STB0899_OFFST_FILTER_EN 5 +#define STB0899_WIDTH_FILTER_EN 1 +#define STB0899_LOCKFALL_THRESH (0x01 << 4) +#define STB0899_OFFST_LOCKFALL_THRESH 4 +#define STB0899_WIDTH_LOCKFALL_THRESH 1 +#define STB0899_HYST_EN (0x01 << 3) +#define STB0899_OFFST_HYST_EN 3 +#define STB0899_WIDTH_HYST_EN 1 +#define STB0899_HYST_SWRST (0x01 << 2) +#define STB0899_OFFST_HYST_SWRST 2 +#define STB0899_WIDTH_HYST_SWRST 1 +#define STB0899_ALGO_EN (0x01 << 1) +#define STB0899_OFFST_ALGO_EN 1 +#define STB0899_WIDTH_ALGO_EN 1 +#define STB0899_ALGO_SWRST (0x01 << 0) +#define STB0899_OFFST_ALGO_SWRST 0 +#define STB0899_WIDTH_ALGO_SWRST 1 + +#define STB0899_PDELCTRL2 0xf601 +#define STB0899_BBHCTRL1 0xf602 +#define STB0899_BBHCTRL2 0xf603 +#define STB0899_HYSTTHRESH 0xf604 + +#define STB0899_MATCSTM 0xf605 +#define STB0899_MATCSTL 0xf606 +#define STB0899_UPLCSTM 0xf607 +#define STB0899_UPLCSTL 0xf608 +#define STB0899_DFLCSTM 0xf609 +#define STB0899_DFLCSTL 0xf60a +#define STB0899_SYNCCST 0xf60b +#define STB0899_SYNCDCSTM 0xf60c +#define STB0899_SYNCDCSTL 0xf60d +#define STB0899_ISI_ENTRY 0xf60e +#define STB0899_ISI_BIT_EN 0xf60f +#define STB0899_MATSTRM 0xf610 +#define STB0899_MATSTRL 0xf611 +#define STB0899_UPLSTRM 0xf612 +#define STB0899_UPLSTRL 0xf613 +#define STB0899_DFLSTRM 0xf614 +#define STB0899_DFLSTRL 0xf615 +#define STB0899_SYNCSTR 0xf616 +#define STB0899_SYNCDSTRM 0xf617 +#define STB0899_SYNCDSTRL 0xf618 + +#define STB0899_CFGPDELSTATUS1 0xf619 +#define STB0899_BADDFL (0x01 << 6) +#define STB0899_OFFST_BADDFL 6 +#define STB0899_WIDTH_BADDFL 1 +#define STB0899_CONTINUOUS_STREAM (0x01 << 5) +#define STB0899_OFFST_CONTINUOUS_STREAM 5 +#define STB0899_WIDTH_CONTINUOUS_STREAM 1 +#define STB0899_ACCEPTED_STREAM (0x01 << 4) +#define STB0899_OFFST_ACCEPTED_STREAM 4 +#define STB0899_WIDTH_ACCEPTED_STREAM 1 +#define STB0899_BCH_ERRFLAG (0x01 << 3) +#define STB0899_OFFST_BCH_ERRFLAG 3 +#define STB0899_WIDTH_BCH_ERRFLAG 1 +#define STB0899_CRCRES (0x01 << 2) +#define STB0899_OFFST_CRCRES 2 +#define STB0899_WIDTH_CRCRES 1 +#define STB0899_CFGPDELSTATUS_LOCK (0x01 << 1) +#define STB0899_OFFST_CFGPDELSTATUS_LOCK 1 +#define STB0899_WIDTH_CFGPDELSTATUS_LOCK 1 +#define STB0899_1STLOCK (0x01 << 0) +#define STB0899_OFFST_1STLOCK 0 +#define STB0899_WIDTH_1STLOCK 1 + +#define STB0899_CFGPDELSTATUS2 0xf61a +#define STB0899_BBFERRORM 0xf61b +#define STB0899_BBFERRORL 0xf61c +#define STB0899_UPKTERRORM 0xf61d +#define STB0899_UPKTERRORL 0xf61e + +#define STB0899_TSTCK 0xff10 + +#define STB0899_TSTRES 0xff11 +#define STB0899_FRESLDPC (0x01 << 7) +#define STB0899_OFFST_FRESLDPC 7 +#define STB0899_WIDTH_FRESLDPC 1 +#define STB0899_FRESRS (0x01 << 6) +#define STB0899_OFFST_FRESRS 6 +#define STB0899_WIDTH_FRESRS 1 +#define STB0899_FRESVIT (0x01 << 5) +#define STB0899_OFFST_FRESVIT 5 +#define STB0899_WIDTH_FRESVIT 1 +#define STB0899_FRESMAS1_2 (0x01 << 4) +#define STB0899_OFFST_FRESMAS1_2 4 +#define STB0899_WIDTH_FRESMAS1_2 1 +#define STB0899_FRESACS (0x01 << 3) +#define STB0899_OFFST_FRESACS 3 +#define STB0899_WIDTH_FRESACS 1 +#define STB0899_FRESSYM (0x01 << 2) +#define STB0899_OFFST_FRESSYM 2 +#define STB0899_WIDTH_FRESSYM 1 +#define STB0899_FRESMAS (0x01 << 1) +#define STB0899_OFFST_FRESMAS 1 +#define STB0899_WIDTH_FRESMAS 1 +#define STB0899_FRESINT (0x01 << 0) +#define STB0899_OFFST_FRESINIT 0 +#define STB0899_WIDTH_FRESINIT 1 + +#define STB0899_TSTOUT 0xff12 +#define STB0899_EN_SIGNATURE (0x01 << 7) +#define STB0899_OFFST_EN_SIGNATURE 7 +#define STB0899_WIDTH_EN_SIGNATURE 1 +#define STB0899_BCLK_CLK (0x01 << 6) +#define STB0899_OFFST_BCLK_CLK 6 +#define STB0899_WIDTH_BCLK_CLK 1 +#define STB0899_SGNL_OUT (0x01 << 5) +#define STB0899_OFFST_SGNL_OUT 5 +#define STB0899_WIDTH_SGNL_OUT 1 +#define STB0899_TS (0x01 << 4) +#define STB0899_OFFST_TS 4 +#define STB0899_WIDTH_TS 1 +#define STB0899_CTEST (0x01 << 0) +#define STB0899_OFFST_CTEST 0 +#define STB0899_WIDTH_CTEST 1 + +#define STB0899_TSTIN 0xff13 +#define STB0899_TEST_IN (0x01 << 7) +#define STB0899_OFFST_TEST_IN 7 +#define STB0899_WIDTH_TEST_IN 1 +#define STB0899_EN_ADC (0x01 << 6) +#define STB0899_OFFST_EN_ADC 6 +#define STB0899_WIDTH_ENADC 1 +#define STB0899_SGN_ADC (0x01 << 5) +#define STB0899_OFFST_SGN_ADC 5 +#define STB0899_WIDTH_SGN_ADC 1 +#define STB0899_BCLK_IN (0x01 << 4) +#define STB0899_OFFST_BCLK_IN 4 +#define STB0899_WIDTH_BCLK_IN 1 +#define STB0899_JETONIN_MODE (0x01 << 3) +#define STB0899_OFFST_JETONIN_MODE 3 +#define STB0899_WIDTH_JETONIN_MODE 1 +#define STB0899_BCLK_VALUE (0x01 << 2) +#define STB0899_OFFST_BCLK_VALUE 2 +#define STB0899_WIDTH_BCLK_VALUE 1 +#define STB0899_SGNRST_T12 (0x01 << 1) +#define STB0899_OFFST_SGNRST_T12 1 +#define STB0899_WIDTH_SGNRST_T12 1 +#define STB0899_LOWSP_ENAX (0x01 << 0) +#define STB0899_OFFST_LOWSP_ENAX 0 +#define STB0899_WIDTH_LOWSP_ENAX 1 + +#define STB0899_TSTSYS 0xff14 +#define STB0899_TSTCHIP 0xff15 +#define STB0899_TSTFREE 0xff16 +#define STB0899_TSTI2C 0xff17 +#define STB0899_BITSPEEDM 0xff1c +#define STB0899_BITSPEEDL 0xff1d +#define STB0899_TBUSBIT 0xff1e +#define STB0899_TSTDIS 0xff24 +#define STB0899_TSTDISRX 0xff25 +#define STB0899_TSTJETON 0xff28 +#define STB0899_TSTDCADJ 0xff40 +#define STB0899_TSTAGC1 0xff41 +#define STB0899_TSTAGC1N 0xff42 +#define STB0899_TSTPOLYPH 0xff48 +#define STB0899_TSTR 0xff49 +#define STB0899_TSTAGC2 0xff4a +#define STB0899_TSTCTL1 0xff4b +#define STB0899_TSTCTL2 0xff4c +#define STB0899_TSTCTL3 0xff4d +#define STB0899_TSTDEMAP 0xff50 +#define STB0899_TSTDEMAP2 0xff51 +#define STB0899_TSTDEMMON 0xff52 +#define STB0899_TSTRATE 0xff53 +#define STB0899_TSTSELOUT 0xff54 +#define STB0899_TSYNC 0xff55 +#define STB0899_TSTERR 0xff56 +#define STB0899_TSTRAM1 0xff58 +#define STB0899_TSTVSELOUT 0xff59 +#define STB0899_TSTFORCEIN 0xff5a +#define STB0899_TSTRS1 0xff5c +#define STB0899_TSTRS2 0xff5d +#define STB0899_TSTRS3 0xff53 + +#define STB0899_INTBUFSTATUS 0xf200 +#define STB0899_INTBUFCTRL 0xf201 +#define STB0899_PCKLENUL 0xf55e +#define STB0899_PCKLENLL 0xf55f +#define STB0899_RSPCKLEN 0xf560 + +/* 2 registers */ +#define STB0899_SYNCDCST 0xf60c + +/* DiSEqC */ +#define STB0899_DISCNTRL1 0xf0a0 +#define STB0899_TIMOFF (0x01 << 7) +#define STB0899_OFFST_TIMOFF 7 +#define STB0899_WIDTH_TIMOFF 1 +#define STB0899_DISEQCRESET (0x01 << 6) +#define STB0899_OFFST_DISEQCRESET 6 +#define STB0899_WIDTH_DISEQCRESET 1 +#define STB0899_TIMCMD (0x03 << 4) +#define STB0899_OFFST_TIMCMD 4 +#define STB0899_WIDTH_TIMCMD 2 +#define STB0899_DISPRECHARGE (0x01 << 2) +#define STB0899_OFFST_DISPRECHARGE 2 +#define STB0899_WIDTH_DISPRECHARGE 1 +#define STB0899_DISEQCMODE (0x03 << 0) +#define STB0899_OFFST_DISEQCMODE 0 +#define STB0899_WIDTH_DISEQCMODE 2 + +#define STB0899_DISCNTRL2 0xf0a1 +#define STB0899_RECEIVER_ON (0x01 << 7) +#define STB0899_OFFST_RECEIVER_ON 7 +#define STB0899_WIDTH_RECEIVER_ON 1 +#define STB0899_IGNO_SHORT_22K (0x01 << 6) +#define STB0899_OFFST_IGNO_SHORT_22K 6 +#define STB0899_WIDTH_IGNO_SHORT_22K 1 +#define STB0899_ONECHIP_TRX (0x01 << 5) +#define STB0899_OFFST_ONECHIP_TRX 5 +#define STB0899_WIDTH_ONECHIP_TRX 1 +#define STB0899_EXT_ENVELOP (0x01 << 4) +#define STB0899_OFFST_EXT_ENVELOP 4 +#define STB0899_WIDTH_EXT_ENVELOP 1 +#define STB0899_PIN_SELECT (0x03 << 2) +#define STB0899_OFFST_PIN_SELCT 2 +#define STB0899_WIDTH_PIN_SELCT 2 +#define STB0899_IRQ_RXEND (0x01 << 1) +#define STB0899_OFFST_IRQ_RXEND 1 +#define STB0899_WIDTH_IRQ_RXEND 1 +#define STB0899_IRQ_4NBYTES (0x01 << 0) +#define STB0899_OFFST_IRQ_4NBYTES 0 +#define STB0899_WIDTH_IRQ_4NBYTES 1 + +#define STB0899_DISRX_ST0 0xf0a4 +#define STB0899_RXEND (0x01 << 7) +#define STB0899_OFFST_RXEND 7 +#define STB0899_WIDTH_RXEND 1 +#define STB0899_RXACTIVE (0x01 << 6) +#define STB0899_OFFST_RXACTIVE 6 +#define STB0899_WIDTH_RXACTIVE 1 +#define STB0899_SHORT22K (0x01 << 5) +#define STB0899_OFFST_SHORT22K 5 +#define STB0899_WIDTH_SHORT22K 1 +#define STB0899_CONTTONE (0x01 << 4) +#define STB0899_OFFST_CONTTONE 4 +#define STB0899_WIDTH_CONTONE 1 +#define STB0899_4BFIFOREDY (0x01 << 3) +#define STB0899_OFFST_4BFIFOREDY 3 +#define STB0899_WIDTH_4BFIFOREDY 1 +#define STB0899_FIFOEMPTY (0x01 << 2) +#define STB0899_OFFST_FIFOEMPTY 2 +#define STB0899_WIDTH_FIFOEMPTY 1 +#define STB0899_ABORTTRX (0x01 << 0) +#define STB0899_OFFST_ABORTTRX 0 +#define STB0899_WIDTH_ABORTTRX 1 + +#define STB0899_DISRX_ST1 0xf0a5 +#define STB0899_RXFAIL (0x01 << 7) +#define STB0899_OFFST_RXFAIL 7 +#define STB0899_WIDTH_RXFAIL 1 +#define STB0899_FIFOPFAIL (0x01 << 6) +#define STB0899_OFFST_FIFOPFAIL 6 +#define STB0899_WIDTH_FIFOPFAIL 1 +#define STB0899_RXNONBYTES (0x01 << 5) +#define STB0899_OFFST_RXNONBYTES 5 +#define STB0899_WIDTH_RXNONBYTES 1 +#define STB0899_FIFOOVF (0x01 << 4) +#define STB0899_OFFST_FIFOOVF 4 +#define STB0899_WIDTH_FIFOOVF 1 +#define STB0899_FIFOBYTENBR (0x0f << 0) +#define STB0899_OFFST_FIFOBYTENBR 0 +#define STB0899_WIDTH_FIFOBYTENBR 4 + +#define STB0899_DISPARITY 0xf0a6 + +#define STB0899_DISFIFO 0xf0a7 + +#define STB0899_DISSTATUS 0xf0a8 +#define STB0899_FIFOFULL (0x01 << 6) +#define STB0899_OFFST_FIFOFULL 6 +#define STB0899_WIDTH_FIFOFULL 1 +#define STB0899_TXIDLE (0x01 << 5) +#define STB0899_OFFST_TXIDLE 5 +#define STB0899_WIDTH_TXIDLE 1 +#define STB0899_GAPBURST (0x01 << 4) +#define STB0899_OFFST_GAPBURST 4 +#define STB0899_WIDTH_GAPBURST 1 +#define STB0899_TXFIFOBYTES (0x0f << 0) +#define STB0899_OFFST_TXFIFOBYTES 0 +#define STB0899_WIDTH_TXFIFOBYTES 4 +#define STB0899_DISF22 0xf0a9 + +#define STB0899_DISF22RX 0xf0aa + +/* General Purpose */ +#define STB0899_SYSREG 0xf101 +#define STB0899_ACRPRESC 0xf110 +#define STB0899_OFFST_RSVD2 7 +#define STB0899_WIDTH_RSVD2 1 +#define STB0899_OFFST_ACRPRESC 4 +#define STB0899_WIDTH_ACRPRESC 3 +#define STB0899_OFFST_RSVD1 3 +#define STB0899_WIDTH_RSVD1 1 +#define STB0899_OFFST_ACRPRESC2 0 +#define STB0899_WIDTH_ACRPRESC2 3 + +#define STB0899_ACRDIV1 0xf111 +#define STB0899_ACRDIV2 0xf112 +#define STB0899_DACR1 0xf113 +#define STB0899_DACR2 0xf114 +#define STB0899_OUTCFG 0xf11c +#define STB0899_MODECFG 0xf11d +#define STB0899_NCOARSE 0xf1b3 + +#define STB0899_SYNTCTRL 0xf1b6 +#define STB0899_STANDBY (0x01 << 7) +#define STB0899_OFFST_STANDBY 7 +#define STB0899_WIDTH_STANDBY 1 +#define STB0899_BYPASSPLL (0x01 << 6) +#define STB0899_OFFST_BYPASSPLL 6 +#define STB0899_WIDTH_BYPASSPLL 1 +#define STB0899_SEL1XRATIO (0x01 << 5) +#define STB0899_OFFST_SEL1XRATIO 5 +#define STB0899_WIDTH_SEL1XRATIO 1 +#define STB0899_SELOSCI (0x01 << 1) +#define STB0899_OFFST_SELOSCI 1 +#define STB0899_WIDTH_SELOSCI 1 + +#define STB0899_FILTCTRL 0xf1b7 +#define STB0899_SYSCTRL 0xf1b8 + +#define STB0899_STOPCLK1 0xf1c2 +#define STB0899_STOP_CKINTBUF108 (0x01 << 7) +#define STB0899_OFFST_STOP_CKINTBUF108 7 +#define STB0899_WIDTH_STOP_CKINTBUF108 1 +#define STB0899_STOP_CKINTBUF216 (0x01 << 6) +#define STB0899_OFFST_STOP_CKINTBUF216 6 +#define STB0899_WIDTH_STOP_CKINTBUF216 1 +#define STB0899_STOP_CHK8PSK (0x01 << 5) +#define STB0899_OFFST_STOP_CHK8PSK 5 +#define STB0899_WIDTH_STOP_CHK8PSK 1 +#define STB0899_STOP_CKFEC108 (0x01 << 4) +#define STB0899_OFFST_STOP_CKFEC108 4 +#define STB0899_WIDTH_STOP_CKFEC108 1 +#define STB0899_STOP_CKFEC216 (0x01 << 3) +#define STB0899_OFFST_STOP_CKFEC216 3 +#define STB0899_WIDTH_STOP_CKFEC216 1 +#define STB0899_STOP_CKCORE216 (0x01 << 2) +#define STB0899_OFFST_STOP_CKCORE216 2 +#define STB0899_WIDTH_STOP_CKCORE216 1 +#define STB0899_STOP_CKADCI108 (0x01 << 1) +#define STB0899_OFFST_STOP_CKADCI108 1 +#define STB0899_WIDTH_STOP_CKADCI108 1 +#define STB0899_STOP_INVCKADCI108 (0x01 << 0) +#define STB0899_OFFST_STOP_INVCKADCI108 0 +#define STB0899_WIDTH_STOP_INVCKADCI108 1 + +#define STB0899_STOPCLK2 0xf1c3 +#define STB0899_STOP_CKS2DMD108 (0x01 << 2) +#define STB0899_OFFST_STOP_CKS2DMD108 2 +#define STB0899_WIDTH_STOP_CKS2DMD108 1 +#define STB0899_STOP_CKPKDLIN108 (0x01 << 1) +#define STB0899_OFFST_STOP_CKPKDLIN108 1 +#define STB0899_WIDTH_STOP_CKPKDLIN108 1 +#define STB0899_STOP_CKPKDLIN216 (0x01 << 0) +#define STB0899_OFFST_STOP_CKPKDLIN216 0 +#define STB0899_WIDTH_STOP_CKPKDLIN216 1 + +#define STB0899_TSTTNR1 0xf1e0 +#define STB0899_BYPASS_ADC (0x01 << 7) +#define STB0899_OFFST_BYPASS_ADC 7 +#define STB0899_WIDTH_BYPASS_ADC 1 +#define STB0899_INVADCICKOUT (0x01 << 6) +#define STB0899_OFFST_INVADCICKOUT 6 +#define STB0899_WIDTH_INVADCICKOUT 1 +#define STB0899_ADCTEST_VOLTAGE (0x03 << 4) +#define STB0899_OFFST_ADCTEST_VOLTAGE 4 +#define STB0899_WIDTH_ADCTEST_VOLTAGE 1 +#define STB0899_ADC_RESET (0x01 << 3) +#define STB0899_OFFST_ADC_RESET 3 +#define STB0899_WIDTH_ADC_RESET 1 +#define STB0899_TSTTNR1_2 (0x01 << 2) +#define STB0899_OFFST_TSTTNR1_2 2 +#define STB0899_WIDTH_TSTTNR1_2 1 +#define STB0899_ADCPON (0x01 << 1) +#define STB0899_OFFST_ADCPON 1 +#define STB0899_WIDTH_ADCPON 1 +#define STB0899_ADCIN_MODE (0x01 << 0) +#define STB0899_OFFST_ADCIN_MODE 0 +#define STB0899_WIDTH_ADCIN_MODE 1 + +#define STB0899_TSTTNR2 0xf1e1 +#define STB0899_TSTTNR2_7 (0x01 << 7) +#define STB0899_OFFST_TSTTNR2_7 7 +#define STB0899_WIDTH_TSTTNR2_7 1 +#define STB0899_NOT_DISRX_WIRED (0x01 << 6) +#define STB0899_OFFST_NOT_DISRX_WIRED 6 +#define STB0899_WIDTH_NOT_DISRX_WIRED 1 +#define STB0899_DISEQC_DCURRENT (0x01 << 5) +#define STB0899_OFFST_DISEQC_DCURRENT 5 +#define STB0899_WIDTH_DISEQC_DCURRENT 1 +#define STB0899_DISEQC_ZCURRENT (0x01 << 4) +#define STB0899_OFFST_DISEQC_ZCURRENT 4 +#define STB0899_WIDTH_DISEQC_ZCURRENT 1 +#define STB0899_DISEQC_SINC_SOURCE (0x03 << 2) +#define STB0899_OFFST_DISEQC_SINC_SOURCE 2 +#define STB0899_WIDTH_DISEQC_SINC_SOURCE 2 +#define STB0899_SELIQSRC (0x03 << 0) +#define STB0899_OFFST_SELIQSRC 0 +#define STB0899_WIDTH_SELIQSRC 2 + +#define STB0899_TSTTNR3 0xf1e2 + +#define STB0899_I2CCFG 0xf129 +#define STB0899_I2CCFGRSVD (0x0f << 4) +#define STB0899_OFFST_I2CCFGRSVD 4 +#define STB0899_WIDTH_I2CCFGRSVD 4 +#define STB0899_I2CFASTMODE (0x01 << 3) +#define STB0899_OFFST_I2CFASTMODE 3 +#define STB0899_WIDTH_I2CFASTMODE 1 +#define STB0899_STATUSWR (0x01 << 2) +#define STB0899_OFFST_STATUSWR 2 +#define STB0899_WIDTH_STATUSWR 1 +#define STB0899_I2CADDRINC (0x03 << 0) +#define STB0899_OFFST_I2CADDRINC 0 +#define STB0899_WIDTH_I2CADDRINC 2 + +#define STB0899_I2CRPT 0xf12a +#define STB0899_I2CTON (0x01 << 7) +#define STB0899_OFFST_I2CTON 7 +#define STB0899_WIDTH_I2CTON 1 +#define STB0899_ENARPTLEVEL (0x01 << 6) +#define STB0899_OFFST_ENARPTLEVEL 6 +#define STB0899_WIDTH_ENARPTLEVEL 2 +#define STB0899_SCLTDELAY (0x01 << 3) +#define STB0899_OFFST_SCLTDELAY 3 +#define STB0899_WIDTH_SCLTDELAY 1 +#define STB0899_STOPENA (0x01 << 2) +#define STB0899_OFFST_STOPENA 2 +#define STB0899_WIDTH_STOPENA 1 +#define STB0899_STOPSDAT2SDA (0x01 << 1) +#define STB0899_OFFST_STOPSDAT2SDA 1 +#define STB0899_WIDTH_STOPSDAT2SDA 1 + +#define STB0899_IOPVALUE8 0xf136 +#define STB0899_IOPVALUE7 0xf137 +#define STB0899_IOPVALUE6 0xf138 +#define STB0899_IOPVALUE5 0xf139 +#define STB0899_IOPVALUE4 0xf13a +#define STB0899_IOPVALUE3 0xf13b +#define STB0899_IOPVALUE2 0xf13c +#define STB0899_IOPVALUE1 0xf13d +#define STB0899_IOPVALUE0 0xf13e + +#define STB0899_GPIO00CFG 0xf140 + +#define STB0899_GPIO01CFG 0xf141 +#define STB0899_GPIO02CFG 0xf142 +#define STB0899_GPIO03CFG 0xf143 +#define STB0899_GPIO04CFG 0xf144 +#define STB0899_GPIO05CFG 0xf145 +#define STB0899_GPIO06CFG 0xf146 +#define STB0899_GPIO07CFG 0xf147 +#define STB0899_GPIO08CFG 0xf148 +#define STB0899_GPIO09CFG 0xf149 +#define STB0899_GPIO10CFG 0xf14a +#define STB0899_GPIO11CFG 0xf14b +#define STB0899_GPIO12CFG 0xf14c +#define STB0899_GPIO13CFG 0xf14d +#define STB0899_GPIO14CFG 0xf14e +#define STB0899_GPIO15CFG 0xf14f +#define STB0899_GPIO16CFG 0xf150 +#define STB0899_GPIO17CFG 0xf151 +#define STB0899_GPIO18CFG 0xf152 +#define STB0899_GPIO19CFG 0xf153 +#define STB0899_GPIO20CFG 0xf154 + +#define STB0899_SDATCFG 0xf155 +#define STB0899_SCLTCFG 0xf156 +#define STB0899_AGCRFCFG 0xf157 +#define STB0899_GPIO22 0xf158 /* AGCBB2CFG */ +#define STB0899_GPIO21 0xf159 /* AGCBB1CFG */ +#define STB0899_DIRCLKCFG 0xf15a +#define STB0899_CLKOUT27CFG 0xf15b +#define STB0899_STDBYCFG 0xf15c +#define STB0899_CS0CFG 0xf15d +#define STB0899_CS1CFG 0xf15e +#define STB0899_DISEQCOCFG 0xf15f + +#define STB0899_GPIO32CFG 0xf160 +#define STB0899_GPIO33CFG 0xf161 +#define STB0899_GPIO34CFG 0xf162 +#define STB0899_GPIO35CFG 0xf163 +#define STB0899_GPIO36CFG 0xf164 +#define STB0899_GPIO37CFG 0xf165 +#define STB0899_GPIO38CFG 0xf166 +#define STB0899_GPIO39CFG 0xf167 + +#define STB0899_IRQSTATUS_3 0xf120 +#define STB0899_IRQSTATUS_2 0xf121 +#define STB0899_IRQSTATUS_1 0xf122 +#define STB0899_IRQSTATUS_0 0xf123 + +#define STB0899_IRQMSK_3 0xf124 +#define STB0899_IRQMSK_2 0xf125 +#define STB0899_IRQMSK_1 0xf126 +#define STB0899_IRQMSK_0 0xf127 + +#define STB0899_IRQCFG 0xf128 + +#define STB0899_GHOSTREG 0xf000 + +#define STB0899_S2DEMOD 0xf3fc +#define STB0899_S2FEC 0xfafc + + +#endif diff --git a/drivers/media/dvb-frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c new file mode 100644 index 0000000000..d74e34677b --- /dev/null +++ b/drivers/media/dvb-frontends/stb6000.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* + Driver for ST STB6000 DVBS Silicon tuner + + Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) + + + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "stb6000.h" + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "stb6000: " args); \ + } while (0) + +struct stb6000_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u32 frequency; +}; + +static void stb6000_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int stb6000_sleep(struct dvb_frontend *fe) +{ + struct stb6000_priv *priv = fe->tuner_priv; + int ret; + u8 buf[] = { 10, 0 }; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + dprintk("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: i2c error\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int stb6000_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct stb6000_priv *priv = fe->tuner_priv; + unsigned int n, m; + int ret; + u32 freq_mhz; + int bandwidth; + u8 buf[12]; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 12 + }; + + dprintk("%s:\n", __func__); + + freq_mhz = p->frequency / 1000; + bandwidth = p->symbol_rate / 1000000; + + if (bandwidth > 31) + bandwidth = 31; + + if ((freq_mhz > 949) && (freq_mhz < 2151)) { + buf[0] = 0x01; + buf[1] = 0xac; + if (freq_mhz < 1950) + buf[1] = 0xaa; + if (freq_mhz < 1800) + buf[1] = 0xa8; + if (freq_mhz < 1650) + buf[1] = 0xa6; + if (freq_mhz < 1530) + buf[1] = 0xa5; + if (freq_mhz < 1470) + buf[1] = 0xa4; + if (freq_mhz < 1370) + buf[1] = 0xa2; + if (freq_mhz < 1300) + buf[1] = 0xa1; + if (freq_mhz < 1200) + buf[1] = 0xa0; + if (freq_mhz < 1075) + buf[1] = 0xbc; + if (freq_mhz < 1000) + buf[1] = 0xba; + if (freq_mhz < 1075) { + n = freq_mhz / 8; /* vco=lo*4 */ + m = 2; + } else { + n = freq_mhz / 16; /* vco=lo*2 */ + m = 1; + } + buf[2] = n >> 1; + buf[3] = (unsigned char)(((n & 1) << 7) | + (m * freq_mhz - n * 16) | 0x60); + buf[4] = 0x04; + buf[5] = 0x0e; + + buf[6] = (unsigned char)(bandwidth); + + buf[7] = 0xd8; + buf[8] = 0xd0; + buf[9] = 0x50; + buf[10] = 0xeb; + buf[11] = 0x4f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: i2c error\n", __func__); + + udelay(10); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + buf[0] = 0x07; + buf[1] = 0xdf; + buf[2] = 0xd0; + buf[3] = 0x50; + buf[4] = 0xfb; + msg.len = 5; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: i2c error\n", __func__); + + udelay(10); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + priv->frequency = freq_mhz * 1000; + + return (ret == 1) ? 0 : ret; + } + return -1; +} + +static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct stb6000_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static const struct dvb_tuner_ops stb6000_tuner_ops = { + .info = { + .name = "ST STB6000", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz + }, + .release = stb6000_release, + .sleep = stb6000_sleep, + .set_params = stb6000_set_params, + .get_frequency = stb6000_get_frequency, +}; + +struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c) +{ + struct stb6000_priv *priv = NULL; + u8 b0[] = { 0 }; + u8 b1[] = { 0, 0 }; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = 0, + .buf = b0, + .len = 0 + }, { + .addr = addr, + .flags = I2C_M_RD, + .buf = b1, + .len = 2 + } + }; + int ret; + + dprintk("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* is some i2c device here ? */ + ret = i2c_transfer(i2c, msg, 2); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) + return NULL; + + priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = addr; + priv->i2c = i2c; + + memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + return fe; +} +EXPORT_SYMBOL_GPL(stb6000_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("DVB STB6000 driver"); +MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stb6000.h b/drivers/media/dvb-frontends/stb6000.h new file mode 100644 index 0000000000..38da55af7e --- /dev/null +++ b/drivers/media/dvb-frontends/stb6000.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* + Driver for ST stb6000 DVBS Silicon tuner + + Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) + + + */ + +#ifndef __DVB_STB6000_H__ +#define __DVB_STB6000_H__ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +#if IS_REACHABLE(CONFIG_DVB_STB6000) +/** + * stb6000_attach - Attach a stb6000 tuner to the supplied frontend structure. + * + * @fe: Frontend to attach to. + * @addr: i2c address of the tuner. + * @i2c: i2c adapter to use. + * + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, + int addr, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_STB6000 */ + +#endif /* __DVB_STB6000_H__ */ diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c new file mode 100644 index 0000000000..c5818a15a0 --- /dev/null +++ b/drivers/media/dvb-frontends/stb6100.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + STB6100 Silicon Tuner + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <media/dvb_frontend.h> +#include "stb6100.h" + +static unsigned int verbose; +module_param(verbose, int, 0644); + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((x > FE_ERROR) && (x > y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((x > FE_NOTICE) && (x > y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((x > FE_INFO) && (x > y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((x > FE_DEBUG) && (x > y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (x > y) \ + printk(format, ##arg); \ + } \ +} while (0) + +struct stb6100_lkup { + u32 val_low; + u32 val_high; + u8 reg; +}; + +static void stb6100_release(struct dvb_frontend *fe); + +static const struct stb6100_lkup lkup[] = { + { 0, 950000, 0x0a }, + { 950000, 1000000, 0x0a }, + { 1000000, 1075000, 0x0c }, + { 1075000, 1200000, 0x00 }, + { 1200000, 1300000, 0x01 }, + { 1300000, 1370000, 0x02 }, + { 1370000, 1470000, 0x04 }, + { 1470000, 1530000, 0x05 }, + { 1530000, 1650000, 0x06 }, + { 1650000, 1800000, 0x08 }, + { 1800000, 1950000, 0x0a }, + { 1950000, 2150000, 0x0c }, + { 2150000, 9999999, 0x0c }, + { 0, 0, 0x00 } +}; + +/* Register names for easy debugging. */ +static const char *stb6100_regnames[] = { + [STB6100_LD] = "LD", + [STB6100_VCO] = "VCO", + [STB6100_NI] = "NI", + [STB6100_NF_LSB] = "NF", + [STB6100_K] = "K", + [STB6100_G] = "G", + [STB6100_F] = "F", + [STB6100_DLB] = "DLB", + [STB6100_TEST1] = "TEST1", + [STB6100_FCCK] = "FCCK", + [STB6100_LPEN] = "LPEN", + [STB6100_TEST3] = "TEST3", +}; + +/* Template for normalisation, i.e. setting unused or undocumented + * bits as required according to the documentation. + */ +struct stb6100_regmask { + u8 mask; + u8 set; +}; + +static const struct stb6100_regmask stb6100_template[] = { + [STB6100_LD] = { 0xff, 0x00 }, + [STB6100_VCO] = { 0xff, 0x00 }, + [STB6100_NI] = { 0xff, 0x00 }, + [STB6100_NF_LSB] = { 0xff, 0x00 }, + [STB6100_K] = { 0xc7, 0x38 }, + [STB6100_G] = { 0xef, 0x10 }, + [STB6100_F] = { 0x1f, 0xc0 }, + [STB6100_DLB] = { 0x38, 0xc4 }, + [STB6100_TEST1] = { 0x00, 0x8f }, + [STB6100_FCCK] = { 0x40, 0x0d }, + [STB6100_LPEN] = { 0xf0, 0x0b }, + [STB6100_TEST3] = { 0x00, 0xde }, +}; + +/* + * Currently unused. Some boards might need it in the future + */ +static __always_unused inline void stb6100_normalise_regs(u8 regs[]) +{ + int i; + + for (i = 0; i < STB6100_NUMREGS; i++) + regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set; +} + +static int stb6100_read_regs(struct stb6100_state *state, u8 regs[]) +{ + int rc; + struct i2c_msg msg = { + .addr = state->config->tuner_address, + .flags = I2C_M_RD, + .buf = regs, + .len = STB6100_NUMREGS + }; + + rc = i2c_transfer(state->i2c, &msg, 1); + if (unlikely(rc != 1)) { + dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]", + state->config->tuner_address, rc); + + return -EREMOTEIO; + } + if (unlikely(verbose > FE_DEBUG)) { + int i; + + dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); + for (i = 0; i < STB6100_NUMREGS; i++) + dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[i], regs[i]); + } + return 0; +} + +static int stb6100_read_reg(struct stb6100_state *state, u8 reg) +{ + u8 regs[STB6100_NUMREGS]; + + struct i2c_msg msg = { + .addr = state->config->tuner_address + reg, + .flags = I2C_M_RD, + .buf = regs, + .len = 1 + }; + + i2c_transfer(state->i2c, &msg, 1); + + if (unlikely(reg >= STB6100_NUMREGS)) { + dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); + return -EINVAL; + } + if (unlikely(verbose > FE_DEBUG)) { + dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); + dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[reg], regs[0]); + } + + return (unsigned int)regs[0]; +} + +static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len) +{ + int rc; + u8 cmdbuf[MAX_XFER_SIZE]; + struct i2c_msg msg = { + .addr = state->config->tuner_address, + .flags = 0, + .buf = cmdbuf, + .len = len + 1 + }; + + if (1 + len > sizeof(cmdbuf)) { + printk(KERN_WARNING + "%s: i2c wr: len=%d is too big!\n", + KBUILD_MODNAME, len); + return -EINVAL; + } + + if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) { + dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d", + start, len); + return -EINVAL; + } + memcpy(&cmdbuf[1], buf, len); + cmdbuf[0] = start; + + if (unlikely(verbose > FE_DEBUG)) { + int i; + + dprintk(verbose, FE_DEBUG, 1, " Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len); + for (i = 0; i < len; i++) + dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[start + i], buf[i]); + } + rc = i2c_transfer(state->i2c, &msg, 1); + if (unlikely(rc != 1)) { + dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]", + (unsigned int)state->config->tuner_address, start, len, rc); + return -EREMOTEIO; + } + return 0; +} + +static int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data) +{ + u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + if (unlikely(reg >= STB6100_NUMREGS)) { + dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); + return -EREMOTEIO; + } + tmp = (tmp & stb6100_template[reg].mask) | stb6100_template[reg].set; + return stb6100_write_reg_range(state, &tmp, reg, 1); +} + + +static int stb6100_get_status(struct dvb_frontend *fe, u32 *status) +{ + int rc; + struct stb6100_state *state = fe->tuner_priv; + + rc = stb6100_read_reg(state, STB6100_LD); + if (rc < 0) { + dprintk(verbose, FE_ERROR, 1, "%s failed", __func__); + return rc; + } + return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0; +} + +static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + int rc; + u8 f; + u32 bw; + struct stb6100_state *state = fe->tuner_priv; + + rc = stb6100_read_reg(state, STB6100_F); + if (rc < 0) + return rc; + f = rc & STB6100_F_F; + + bw = (f + 5) * 2000; /* x2 for ZIF */ + + *bandwidth = state->bandwidth = bw * 1000; + dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth); + return 0; +} + +static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + u32 tmp; + int rc; + struct stb6100_state *state = fe->tuner_priv; + + dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth); + + bandwidth /= 2; /* ZIF */ + + if (bandwidth >= 36000000) /* F[4:0] BW/2 max =31+5=36 mhz for F=31 */ + tmp = 31; + else if (bandwidth <= 5000000) /* bw/2 min = 5Mhz for F=0 */ + tmp = 0; + else /* if 5 < bw/2 < 36 */ + tmp = (bandwidth + 500000) / 1000000 - 5; + + /* Turn on LPF bandwidth setting clock control, + * set bandwidth, wait 10ms, turn off. + */ + rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK); + if (rc < 0) + return rc; + rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp); + if (rc < 0) + return rc; + + msleep(5); /* This is dangerous as another (related) thread may start */ + + rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d); + if (rc < 0) + return rc; + + msleep(10); /* This is dangerous as another (related) thread may start */ + + return 0; +} + +static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + int rc; + u32 nint, nfrac, fvco; + int psd2, odiv; + struct stb6100_state *state = fe->tuner_priv; + u8 regs[STB6100_NUMREGS]; + + rc = stb6100_read_regs(state, regs); + if (rc < 0) + return rc; + + odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT; + psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT; + nint = regs[STB6100_NI]; + nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB]; + fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2); + *frequency = state->frequency = fvco >> (odiv + 1); + + dprintk(verbose, FE_DEBUG, 1, + "frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u", + state->frequency, odiv, psd2, state->reference, fvco, nint, nfrac); + return 0; +} + + +static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + int rc; + const struct stb6100_lkup *ptr; + struct stb6100_state *state = fe->tuner_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + u32 srate = 0, fvco, nint, nfrac; + u8 regs[STB6100_NUMREGS]; + u8 g, psd2, odiv; + + dprintk(verbose, FE_DEBUG, 1, "Version 2010-8-14 13:51"); + + if (fe->ops.get_frontend) { + dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters"); + fe->ops.get_frontend(fe, p); + } + srate = p->symbol_rate; + + /* Set up tuner cleanly, LPF calibration on */ + rc = stb6100_write_reg(state, STB6100_FCCK, 0x4d | STB6100_FCCK_FCCK); + if (rc < 0) + return rc; /* allow LPF calibration */ + + /* PLL Loop disabled, bias on, VCO on, synth on */ + regs[STB6100_LPEN] = 0xeb; + rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]); + if (rc < 0) + return rc; + + /* Program the registers with their data values */ + + /* VCO divide ratio (LO divide ratio, VCO prescaler enable). */ + if (frequency <= 1075000) + odiv = 1; + else + odiv = 0; + + /* VCO enabled, search clock off as per LL3.7, 3.4.1 */ + regs[STB6100_VCO] = 0xe0 | (odiv << STB6100_VCO_ODIV_SHIFT); + + /* OSM */ + for (ptr = lkup; + (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high); + ptr++); + + if (ptr->val_high == 0) { + printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency); + return -EINVAL; + } + regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg; + rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); + if (rc < 0) + return rc; + + if ((frequency > 1075000) && (frequency <= 1325000)) + psd2 = 0; + else + psd2 = 1; + /* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4) */ + fvco = frequency << (1 + odiv); + /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */ + nint = fvco / (state->reference << psd2); + /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */ + nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2)) + << (9 - psd2), state->reference); + + /* NI */ + regs[STB6100_NI] = nint; + rc = stb6100_write_reg(state, STB6100_NI, regs[STB6100_NI]); + if (rc < 0) + return rc; + + /* NF */ + regs[STB6100_NF_LSB] = nfrac; + rc = stb6100_write_reg(state, STB6100_NF_LSB, regs[STB6100_NF_LSB]); + if (rc < 0) + return rc; + + /* K */ + regs[STB6100_K] = (0x38 & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT); + regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB); + rc = stb6100_write_reg(state, STB6100_K, regs[STB6100_K]); + if (rc < 0) + return rc; + + /* G Baseband gain. */ + if (srate >= 15000000) + g = 9; /* +4 dB */ + else if (srate >= 5000000) + g = 11; /* +8 dB */ + else + g = 14; /* +14 dB */ + + regs[STB6100_G] = (0x10 & ~STB6100_G_G) | g; + regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */ + regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */ + rc = stb6100_write_reg(state, STB6100_G, regs[STB6100_G]); + if (rc < 0) + return rc; + + /* F we don't write as it is set up in BW set */ + + /* DLB set DC servo loop BW to 160Hz (LLA 3.8 / 2.1) */ + regs[STB6100_DLB] = 0xcc; + rc = stb6100_write_reg(state, STB6100_DLB, regs[STB6100_DLB]); + if (rc < 0) + return rc; + + dprintk(verbose, FE_DEBUG, 1, + "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u", + frequency, srate, (unsigned int)g, (unsigned int)odiv, + (unsigned int)psd2, state->reference, + ptr->reg, fvco, nint, nfrac); + + /* Set up the test registers */ + regs[STB6100_TEST1] = 0x8f; + rc = stb6100_write_reg(state, STB6100_TEST1, regs[STB6100_TEST1]); + if (rc < 0) + return rc; + regs[STB6100_TEST3] = 0xde; + rc = stb6100_write_reg(state, STB6100_TEST3, regs[STB6100_TEST3]); + if (rc < 0) + return rc; + + /* Bring up tuner according to LLA 3.7 3.4.1, step 2 */ + regs[STB6100_LPEN] = 0xfb; /* PLL Loop enabled, bias on, VCO on, synth on */ + rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]); + if (rc < 0) + return rc; + + msleep(2); + + /* Bring up tuner according to LLA 3.7 3.4.1, step 3 */ + regs[STB6100_VCO] &= ~STB6100_VCO_OCK; /* VCO fast search */ + rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); + if (rc < 0) + return rc; + + msleep(10); /* This is dangerous as another (related) thread may start */ /* wait for LO to lock */ + + regs[STB6100_VCO] &= ~STB6100_VCO_OSCH; /* vco search disabled */ + regs[STB6100_VCO] |= STB6100_VCO_OCK; /* search clock off */ + rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); + if (rc < 0) + return rc; + + rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d); + if (rc < 0) + return rc; /* Stop LPF calibration */ + + msleep(10); /* This is dangerous as another (related) thread may start */ + /* wait for stabilisation, (should not be necessary) */ + return 0; +} + +static int stb6100_sleep(struct dvb_frontend *fe) +{ + /* TODO: power down */ + return 0; +} + +static int stb6100_init(struct dvb_frontend *fe) +{ + struct stb6100_state *state = fe->tuner_priv; + int refclk = 27000000; /* Hz */ + + /* + * iqsense = 1 + * tunerstep = 125000 + */ + state->bandwidth = 36000000; /* Hz */ + state->reference = refclk / 1000; /* kHz */ + + /* Set default bandwidth. Modified, PN 13-May-10 */ + return 0; +} + +static int stb6100_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->frequency > 0) + stb6100_set_frequency(fe, c->frequency); + + if (c->bandwidth_hz > 0) + stb6100_set_bandwidth(fe, c->bandwidth_hz); + + return 0; +} + +static const struct dvb_tuner_ops stb6100_ops = { + .info = { + .name = "STB6100 Silicon Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + }, + + .init = stb6100_init, + .sleep = stb6100_sleep, + .get_status = stb6100_get_status, + .set_params = stb6100_set_params, + .get_frequency = stb6100_get_frequency, + .get_bandwidth = stb6100_get_bandwidth, + .release = stb6100_release +}; + +struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, + const struct stb6100_config *config, + struct i2c_adapter *i2c) +{ + struct stb6100_state *state = NULL; + + state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->i2c = i2c; + state->frontend = fe; + state->reference = config->refclock / 1000; /* kHz */ + fe->tuner_priv = state; + fe->ops.tuner_ops = stb6100_ops; + + printk("%s: Attaching STB6100 \n", __func__); + return fe; +} + +static void stb6100_release(struct dvb_frontend *fe) +{ + struct stb6100_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); +} + +EXPORT_SYMBOL_GPL(stb6100_attach); +MODULE_PARM_DESC(verbose, "Set Verbosity level"); + +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("STB6100 Silicon tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h new file mode 100644 index 0000000000..902b851b51 --- /dev/null +++ b/drivers/media/dvb-frontends/stb6100.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB6100 Silicon Tuner + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STB_6100_REG_H +#define __STB_6100_REG_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +#define STB6100_LD 0x00 +#define STB6100_LD_LOCK (1 << 0) + +#define STB6100_VCO 0x01 +#define STB6100_VCO_OSCH (0x01 << 7) +#define STB6100_VCO_OSCH_SHIFT 7 +#define STB6100_VCO_OCK (0x03 << 5) +#define STB6100_VCO_OCK_SHIFT 5 +#define STB6100_VCO_ODIV (0x01 << 4) +#define STB6100_VCO_ODIV_SHIFT 4 +#define STB6100_VCO_OSM (0x0f << 0) + +#define STB6100_NI 0x02 +#define STB6100_NF_LSB 0x03 + +#define STB6100_K 0x04 +#define STB6100_K_PSD2 (0x01 << 2) +#define STB6100_K_PSD2_SHIFT 2 +#define STB6100_K_NF_MSB (0x03 << 0) + +#define STB6100_G 0x05 +#define STB6100_G_G (0x0f << 0) +#define STB6100_G_GCT (0x07 << 5) + +#define STB6100_F 0x06 +#define STB6100_F_F (0x1f << 0) + +#define STB6100_DLB 0x07 + +#define STB6100_TEST1 0x08 + +#define STB6100_FCCK 0x09 +#define STB6100_FCCK_FCCK (0x01 << 6) + +#define STB6100_LPEN 0x0a +#define STB6100_LPEN_LPEN (0x01 << 4) +#define STB6100_LPEN_SYNP (0x01 << 5) +#define STB6100_LPEN_OSCP (0x01 << 6) +#define STB6100_LPEN_BEN (0x01 << 7) + +#define STB6100_TEST3 0x0b + +#define STB6100_NUMREGS 0x0c + + +#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ + ((y <= val) && (val <= x)) ? 1 : 0) + +#define CHKRANGE(val, x, y) (((val >= x) && (val < y)) ? 1 : 0) + +struct stb6100_config { + u8 tuner_address; + u32 refclock; +}; + +struct stb6100_state { + struct i2c_adapter *i2c; + + const struct stb6100_config *config; + struct dvb_tuner_ops ops; + struct dvb_frontend *frontend; + + u32 frequency; + u32 srate; + u32 bandwidth; + u32 reference; +}; + +#if IS_REACHABLE(CONFIG_DVB_STB6100) + +extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, + const struct stb6100_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, + const struct stb6100_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif //CONFIG_DVB_STB6100 + +#endif diff --git a/drivers/media/dvb-frontends/stb6100_cfg.h b/drivers/media/dvb-frontends/stb6100_cfg.h new file mode 100644 index 0000000000..1408c0c449 --- /dev/null +++ b/drivers/media/dvb-frontends/stb6100_cfg.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB6100 Silicon Tuner + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + +*/ + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + int err = 0; + + if (tuner_ops->get_frequency) { + err = tuner_ops->get_frequency(fe, frequency); + if (err < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + return 0; +} + +static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 bw = c->bandwidth_hz; + int err = 0; + + c->frequency = frequency; + c->bandwidth_hz = 0; /* Don't adjust the bandwidth */ + + if (tuner_ops->set_params) { + err = tuner_ops->set_params(fe); + c->bandwidth_hz = bw; + if (err < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + return 0; +} + +static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + int err = 0; + + if (tuner_ops->get_bandwidth) { + err = tuner_ops->get_bandwidth(fe, bandwidth); + if (err < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + return 0; +} + +static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 freq = c->frequency; + int err = 0; + + c->bandwidth_hz = bandwidth; + c->frequency = 0; /* Don't adjust the frequency */ + + if (tuner_ops->set_params) { + err = tuner_ops->set_params(fe); + c->frequency = freq; + if (err < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + return 0; +} diff --git a/drivers/media/dvb-frontends/stb6100_proc.h b/drivers/media/dvb-frontends/stb6100_proc.h new file mode 100644 index 0000000000..af75a40e0f --- /dev/null +++ b/drivers/media/dvb-frontends/stb6100_proc.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STB6100 Silicon Tuner wrapper + Copyright (C)2009 Igor M. Liplianin (liplianin@me.by) + +*/ + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + int err = 0; + + if (tuner_ops->get_frequency) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->get_frequency(fe, frequency); + if (err < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 bw = c->bandwidth_hz; + int err = 0; + + c->frequency = frequency; + c->bandwidth_hz = 0; /* Don't adjust the bandwidth */ + + if (tuner_ops->set_params) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->set_params(fe); + c->bandwidth_hz = bw; + if (err < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + } + + return 0; +} + +static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + int err = 0; + + if (tuner_ops->get_bandwidth) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->get_bandwidth(fe, bandwidth); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 freq = c->frequency; + int err = 0; + + c->bandwidth_hz = bandwidth; + c->frequency = 0; /* Don't adjust the frequency */ + + if (tuner_ops->set_params) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->set_params(fe); + c->frequency = freq; + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + } + + return 0; +} diff --git a/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c new file mode 100644 index 0000000000..a5581bd60f --- /dev/null +++ b/drivers/media/dvb-frontends/stv0288.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for ST STV0288 demodulator + Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de + for Reel Multimedia + Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com> + Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by> + Removed stb6000 specific tuner code and revised some + procedures. + 2010-09-01 Josef Pavlik <josef@pavlik.it> + Fixed diseqc_msg, diseqc_burst and set_tone problems + + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "stv0288.h" + +struct stv0288_state { + struct i2c_adapter *i2c; + const struct stv0288_config *config; + struct dvb_frontend frontend; + + u8 initialised:1; + u32 tuner_frequency; + u32 symbol_rate; + enum fe_code_rate fec_inner; + int errmode; +}; + +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 + +static int debug; +static int debug_legacy_dish_switch; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "stv0288: " args); \ + } while (0) + + +static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return stv0288_writeregI(state, buf[0], buf[1]); +} + +static u8 stv0288_readreg(struct stv0288_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) +{ + struct stv0288_state *state = fe->demodulator_priv; + unsigned int temp; + unsigned char b[3]; + + if ((srate < 1000000) || (srate > 45000000)) + return -EINVAL; + + stv0288_writeregI(state, 0x22, 0); + stv0288_writeregI(state, 0x23, 0); + stv0288_writeregI(state, 0x2b, 0xff); + stv0288_writeregI(state, 0x2c, 0xf7); + + temp = (unsigned int)srate / 1000; + + temp = temp * 32768; + temp = temp / 25; + temp = temp / 125; + b[0] = (unsigned char)((temp >> 12) & 0xff); + b[1] = (unsigned char)((temp >> 4) & 0xff); + b[2] = (unsigned char)((temp << 4) & 0xf0); + stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ + stv0288_writeregI(state, 0x29, 0); /* SFRM */ + stv0288_writeregI(state, 0x2a, 0); /* SFRL */ + + stv0288_writeregI(state, 0x28, b[0]); + stv0288_writeregI(state, 0x29, b[1]); + stv0288_writeregI(state, 0x2a, b[2]); + dprintk("stv0288: stv0288_set_symbolrate\n"); + + return 0; +} + +static int stv0288_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct stv0288_state *state = fe->demodulator_priv; + + int i; + + dprintk("%s\n", __func__); + + stv0288_writeregI(state, 0x09, 0); + msleep(30); + stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */ + + for (i = 0; i < m->msg_len; i++) { + if (stv0288_writeregI(state, 0x06, m->msg[i])) + return -EREMOTEIO; + } + msleep(m->msg_len*12); + return 0; +} + +static int stv0288_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct stv0288_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */ + return -EREMOTEIO; + + if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff)) + return -EREMOTEIO; + + msleep(15); + if (stv0288_writeregI(state, 0x05, 0x12)) + return -EREMOTEIO; + + return 0; +} + +static int stv0288_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct stv0288_state *state = fe->demodulator_priv; + + switch (tone) { + case SEC_TONE_ON: + if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */ + return -EREMOTEIO; + break; + + case SEC_TONE_OFF: + if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/ + return -EREMOTEIO; + break; + + default: + return -EINVAL; + } + return 0; +} + +static u8 stv0288_inittab[] = { + 0x01, 0x15, + 0x02, 0x20, + 0x09, 0x0, + 0x0a, 0x4, + 0x0b, 0x0, + 0x0c, 0x0, + 0x0d, 0x0, + 0x0e, 0xd4, + 0x0f, 0x30, + 0x11, 0x80, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0x45, + 0x16, 0xb7, + 0x17, 0x9c, + 0x18, 0x0, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0x0, + 0x23, 0x0, + 0x2b, 0xff, + 0x2c, 0xf7, + 0x30, 0x0, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbe, + 0x3a, 0x0, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x20, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x50, 0x10, + 0x51, 0x38, + 0x52, 0x21, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x0, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0x0, + 0x5f, 0xff, + 0x70, 0x0, + 0x71, 0x0, + 0x72, 0x0, + 0x74, 0x0, + 0x75, 0x0, + 0x76, 0x0, + 0x81, 0x0, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x0, + 0x85, 0x0, + 0x88, 0x0, + 0x89, 0x0, + 0x8a, 0x0, + 0x8b, 0x0, + 0x8c, 0x0, + 0x90, 0x0, + 0x91, 0x0, + 0x92, 0x0, + 0x93, 0x0, + 0x94, 0x1c, + 0x97, 0x0, + 0xa0, 0x48, + 0xa1, 0x0, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x0, + 0xf0, 0x0, + 0xf1, 0x0, + 0xf2, 0xc0, + 0x51, 0x36, + 0x52, 0x09, + 0x53, 0x94, + 0x54, 0x62, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0xff, 0xff, +}; + +static int stv0288_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage volt) +{ + dprintk("%s: %s\n", __func__, + volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + return 0; +} + +static int stv0288_init(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + int i; + u8 reg; + u8 val; + + dprintk("stv0288: init chip\n"); + stv0288_writeregI(state, 0x41, 0x04); + msleep(50); + + /* we have default inittab */ + if (state->config->inittab == NULL) { + for (i = 0; !(stv0288_inittab[i] == 0xff && + stv0288_inittab[i + 1] == 0xff); i += 2) + stv0288_writeregI(state, stv0288_inittab[i], + stv0288_inittab[i + 1]); + } else { + for (i = 0; ; i += 2) { + reg = state->config->inittab[i]; + val = state->config->inittab[i+1]; + if (reg == 0xff && val == 0xff) + break; + stv0288_writeregI(state, reg, val); + } + } + return 0; +} + +static int stv0288_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct stv0288_state *state = fe->demodulator_priv; + + u8 sync = stv0288_readreg(state, 0x24); + if (sync == 255) + sync = 0; + + dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); + + *status = 0; + if (sync & 0x80) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + if (sync & 0x08) { + *status |= FE_HAS_LOCK; + dprintk("stv0288 has locked\n"); + } + + return 0; +} + +static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (state->errmode != STATUS_BER) + return 0; + *ber = (stv0288_readreg(state, 0x26) << 8) | + stv0288_readreg(state, 0x27); + dprintk("stv0288_read_ber %d\n", *ber); + + return 0; +} + + +static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stv0288_state *state = fe->demodulator_priv; + + s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8)); + + + signal = signal * 5 / 4; + *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; + dprintk("stv0288_read_signal_strength %d\n", *strength); + + return 0; +} +static int stv0288_sleep(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + + stv0288_writeregI(state, 0x41, 0x84); + state->initialised = 0; + + return 0; +} +static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stv0288_state *state = fe->demodulator_priv; + + s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8) + | stv0288_readreg(state, 0x2e)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + dprintk("stv0288_read_snr %d\n", *snr); + + return 0; +} + +static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (state->errmode != STATUS_BER) + return 0; + *ucblocks = (stv0288_readreg(state, 0x26) << 8) | + stv0288_readreg(state, 0x27); + dprintk("stv0288_read_ber %d\n", *ucblocks); + + return 0; +} + +static int stv0288_set_frontend(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + u8 tda[3], reg, time_out = 0; + s8 tm; + + dprintk("%s : FE_SET_FRONTEND\n", __func__); + + if (c->delivery_system != SYS_DVBS) { + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + /* only frequency & symbol_rate are used for tuner*/ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + udelay(10); + stv0288_set_symbolrate(fe, c->symbol_rate); + /* Carrier lock control register */ + stv0288_writeregI(state, 0x15, 0xc5); + + tda[2] = 0x0; /* CFRL */ + for (tm = -9; tm < 7;) { + /* Viterbi status */ + reg = stv0288_readreg(state, 0x24); + if (reg & 0x8) + break; + if (reg & 0x80) { + time_out++; + if (time_out > 10) + break; + tda[2] += 40; + if (tda[2] < 40) + tm++; + } else { + tm++; + tda[2] = 0; + time_out = 0; + } + tda[1] = (unsigned char)tm; + stv0288_writeregI(state, 0x2b, tda[1]); + stv0288_writeregI(state, 0x2c, tda[2]); + msleep(30); + } + state->tuner_frequency = c->frequency; + state->fec_inner = FEC_AUTO; + state->symbol_rate = c->symbol_rate; + + return 0; +} + +static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (enable) + stv0288_writeregI(state, 0x01, 0xb5); + else + stv0288_writeregI(state, 0x01, 0x35); + + udelay(1); + + return 0; +} + +static void stv0288_release(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops stv0288_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "ST STV0288 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = stv0288_release, + .init = stv0288_init, + .sleep = stv0288_sleep, + .write = stv0288_write, + .i2c_gate_ctrl = stv0288_i2c_gate_ctrl, + .read_status = stv0288_read_status, + .read_ber = stv0288_read_ber, + .read_signal_strength = stv0288_read_signal_strength, + .read_snr = stv0288_read_snr, + .read_ucblocks = stv0288_read_ucblocks, + .diseqc_send_master_cmd = stv0288_send_diseqc_msg, + .diseqc_send_burst = stv0288_send_diseqc_burst, + .set_tone = stv0288_set_tone, + .set_voltage = stv0288_set_voltage, + + .set_frontend = stv0288_set_frontend, +}; + +struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, + struct i2c_adapter *i2c) +{ + struct stv0288_state *state = NULL; + int id; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + state->errmode = STATUS_BER; + + stv0288_writeregI(state, 0x41, 0x04); + msleep(200); + id = stv0288_readreg(state, 0x00); + dprintk("stv0288 id %x\n", id); + + /* register 0x00 contains 0x11 for STV0288 */ + if (id != 0x11) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &stv0288_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL_GPL(stv0288_attach); + +module_param(debug_legacy_dish_switch, int, 0444); +MODULE_PARM_DESC(debug_legacy_dish_switch, + "Enable timing analysis for Dish Network legacy switches"); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver"); +MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/dvb-frontends/stv0288.h b/drivers/media/dvb-frontends/stv0288.h new file mode 100644 index 0000000000..8690aa61bd --- /dev/null +++ b/drivers/media/dvb-frontends/stv0288.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for ST STV0288 demodulator + + Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de + for Reel Multimedia + Copyright (C) 2008 TurboSight.com, <bob@turbosight.com> + Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by> + Removed stb6000 specific tuner code and revised some + procedures. + + +*/ + +#ifndef STV0288_H +#define STV0288_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +struct stv0288_config { + /* the demodulator's i2c address */ + u8 demod_address; + + u8* inittab; + + /* minimum delay before retuning */ + int min_delay_ms; + + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +}; + +#if IS_REACHABLE(CONFIG_DVB_STV0288) +extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_STV0288 */ + +static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val) +{ + int r = 0; + u8 buf[] = { reg, val }; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} + +#endif /* STV0288_H */ diff --git a/drivers/media/dvb-frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c new file mode 100644 index 0000000000..9d4dbd99a5 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0297.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for STV0297 demodulator + + Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> + Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de> + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "stv0297.h" + +struct stv0297_state { + struct i2c_adapter *i2c; + const struct stv0297_config *config; + struct dvb_frontend frontend; + + unsigned long last_ber; + unsigned long base_freq; +}; + +#if 1 +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define STV0297_CLOCK_KHZ 28900 + + +static int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int stv0297_readreg(struct stv0297_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} + }; + + // this device needs a STOP between the register and data + if (state->config->stop_during_read) { + if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); + return -1; + } + if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); + return -1; + } + } else { + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); + return -1; + } + } + + return b1[0]; +} + +static int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data) +{ + int val; + + val = stv0297_readreg(state, reg); + val &= ~mask; + val |= (data & mask); + stv0297_writereg(state, reg, val); + + return 0; +} + +static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len) +{ + int ret; + struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = + ®1,.len = 1}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len} + }; + + // this device needs a STOP between the register and data + if (state->config->stop_during_read) { + if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); + return -1; + } + if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); + return -1; + } + } else { + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); + return -1; + } + } + + return 0; +} + +static u32 stv0297_get_symbolrate(struct stv0297_state *state) +{ + u64 tmp; + + tmp = (u64)(stv0297_readreg(state, 0x55) + | (stv0297_readreg(state, 0x56) << 8) + | (stv0297_readreg(state, 0x57) << 16) + | (stv0297_readreg(state, 0x58) << 24)); + + tmp *= STV0297_CLOCK_KHZ; + tmp >>= 32; + + return (u32) tmp; +} + +static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate) +{ + long tmp; + + tmp = 131072L * srate; /* 131072 = 2^17 */ + tmp = tmp / (STV0297_CLOCK_KHZ / 4); /* 1/4 = 2^-2 */ + tmp = tmp * 8192L; /* 8192 = 2^13 */ + + stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF)); + stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8)); + stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16)); + stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24)); +} + +static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate) +{ + long tmp; + + tmp = (long) fshift *262144L; /* 262144 = 2*18 */ + tmp /= symrate; + tmp *= 1024; /* 1024 = 2*10 */ + + // adjust + if (tmp >= 0) { + tmp += 500000; + } else { + tmp -= 500000; + } + tmp /= 1000000; + + stv0297_writereg(state, 0x60, tmp & 0xFF); + stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0); +} + +static void stv0297_set_carrieroffset(struct stv0297_state *state, long offset) +{ + long tmp; + + /* symrate is hardcoded to 10000 */ + tmp = offset * 26844L; /* (2**28)/10000 */ + if (tmp < 0) + tmp += 0x10000000; + tmp &= 0x0FFFFFFF; + + stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF)); + stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8)); + stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16)); + stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f); +} + +/* +static long stv0297_get_carrieroffset(struct stv0297_state *state) +{ + s64 tmp; + + stv0297_writereg(state, 0x6B, 0x00); + + tmp = stv0297_readreg(state, 0x66); + tmp |= (stv0297_readreg(state, 0x67) << 8); + tmp |= (stv0297_readreg(state, 0x68) << 16); + tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24; + + tmp *= stv0297_get_symbolrate(state); + tmp >>= 28; + + return (s32) tmp; +} +*/ + +static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq) +{ + s32 tmp; + + if (freq > 10000) + freq -= STV0297_CLOCK_KHZ; + + tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16); + tmp = (freq * 1000) / tmp; + if (tmp > 0xffff) + tmp = 0xffff; + + stv0297_writereg_mask(state, 0x25, 0x80, 0x80); + stv0297_writereg(state, 0x21, tmp >> 8); + stv0297_writereg(state, 0x20, tmp); +} + +static int stv0297_set_qam(struct stv0297_state *state, + enum fe_modulation modulation) +{ + int val = 0; + + switch (modulation) { + case QAM_16: + val = 0; + break; + + case QAM_32: + val = 1; + break; + + case QAM_64: + val = 4; + break; + + case QAM_128: + val = 2; + break; + + case QAM_256: + val = 3; + break; + + default: + return -EINVAL; + } + + stv0297_writereg_mask(state, 0x00, 0x70, val << 4); + + return 0; +} + +static int stv0297_set_inversion(struct stv0297_state *state, + enum fe_spectral_inversion inversion) +{ + int val = 0; + + switch (inversion) { + case INVERSION_OFF: + val = 0; + break; + + case INVERSION_ON: + val = 1; + break; + + default: + return -EINVAL; + } + + stv0297_writereg_mask(state, 0x83, 0x08, val << 3); + + return 0; +} + +static int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0297_state *state = fe->demodulator_priv; + + if (enable) { + stv0297_writereg(state, 0x87, 0x78); + stv0297_writereg(state, 0x86, 0xc8); + } + + return 0; +} + +static int stv0297_init(struct dvb_frontend *fe) +{ + struct stv0297_state *state = fe->demodulator_priv; + int i; + + /* load init table */ + for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) + stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); + msleep(200); + + state->last_ber = 0; + + return 0; +} + +static int stv0297_sleep(struct dvb_frontend *fe) +{ + struct stv0297_state *state = fe->demodulator_priv; + + stv0297_writereg_mask(state, 0x80, 1, 1); + + return 0; +} + +static int stv0297_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct stv0297_state *state = fe->demodulator_priv; + + u8 sync = stv0297_readreg(state, 0xDF); + + *status = 0; + if (sync & 0x80) + *status |= + FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; + return 0; +} + +static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct stv0297_state *state = fe->demodulator_priv; + u8 BER[3]; + + stv0297_readregs(state, 0xA0, BER, 3); + if (!(BER[0] & 0x80)) { + state->last_ber = BER[2] << 8 | BER[1]; + stv0297_writereg_mask(state, 0xA0, 0x80, 0x80); + } + + *ber = state->last_ber; + + return 0; +} + + +static int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct stv0297_state *state = fe->demodulator_priv; + u8 STRENGTH[3]; + u16 tmp; + + stv0297_readregs(state, 0x41, STRENGTH, 3); + tmp = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0]; + if (STRENGTH[2] & 0x20) { + if (tmp < 0x200) + tmp = 0; + else + tmp = tmp - 0x200; + } else { + if (tmp > 0x1ff) + tmp = 0; + else + tmp = 0x1ff - tmp; + } + *strength = (tmp << 7) | (tmp >> 2); + return 0; +} + +static int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + struct stv0297_state *state = fe->demodulator_priv; + u8 SNR[2]; + + stv0297_readregs(state, 0x07, SNR, 2); + *snr = SNR[1] << 8 | SNR[0]; + + return 0; +} + +static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +{ + struct stv0297_state *state = fe->demodulator_priv; + + stv0297_writereg_mask(state, 0xDF, 0x03, 0x03); /* freeze the counters */ + + *ucblocks = (stv0297_readreg(state, 0xD5) << 8) + | stv0297_readreg(state, 0xD4); + + stv0297_writereg_mask(state, 0xDF, 0x03, 0x02); /* clear the counters */ + stv0297_writereg_mask(state, 0xDF, 0x03, 0x01); /* re-enable the counters */ + + return 0; +} + +static int stv0297_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct stv0297_state *state = fe->demodulator_priv; + int u_threshold; + int initial_u; + int blind_u; + int delay; + int sweeprate; + int carrieroffset; + unsigned long timeout; + enum fe_spectral_inversion inversion; + + switch (p->modulation) { + case QAM_16: + case QAM_32: + case QAM_64: + delay = 100; + sweeprate = 1000; + break; + + case QAM_128: + case QAM_256: + delay = 200; + sweeprate = 500; + break; + + default: + return -EINVAL; + } + + // determine inversion dependent parameters + inversion = p->inversion; + if (state->config->invert) + inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; + carrieroffset = -330; + switch (inversion) { + case INVERSION_OFF: + break; + + case INVERSION_ON: + sweeprate = -sweeprate; + carrieroffset = -carrieroffset; + break; + + default: + return -EINVAL; + } + + stv0297_init(fe); + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* clear software interrupts */ + stv0297_writereg(state, 0x82, 0x0); + + /* set initial demodulation frequency */ + stv0297_set_initialdemodfreq(state, 7250); + + /* setup AGC */ + stv0297_writereg_mask(state, 0x43, 0x10, 0x00); + stv0297_writereg(state, 0x41, 0x00); + stv0297_writereg_mask(state, 0x42, 0x03, 0x01); + stv0297_writereg_mask(state, 0x36, 0x60, 0x00); + stv0297_writereg_mask(state, 0x36, 0x18, 0x00); + stv0297_writereg_mask(state, 0x71, 0x80, 0x80); + stv0297_writereg(state, 0x72, 0x00); + stv0297_writereg(state, 0x73, 0x00); + stv0297_writereg_mask(state, 0x74, 0x0F, 0x00); + stv0297_writereg_mask(state, 0x43, 0x08, 0x00); + stv0297_writereg_mask(state, 0x71, 0x80, 0x00); + + /* setup STL */ + stv0297_writereg_mask(state, 0x5a, 0x20, 0x20); + stv0297_writereg_mask(state, 0x5b, 0x02, 0x02); + stv0297_writereg_mask(state, 0x5b, 0x02, 0x00); + stv0297_writereg_mask(state, 0x5b, 0x01, 0x00); + stv0297_writereg_mask(state, 0x5a, 0x40, 0x40); + + /* disable frequency sweep */ + stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); + + /* reset deinterleaver */ + stv0297_writereg_mask(state, 0x81, 0x01, 0x01); + stv0297_writereg_mask(state, 0x81, 0x01, 0x00); + + /* ??? */ + stv0297_writereg_mask(state, 0x83, 0x20, 0x20); + stv0297_writereg_mask(state, 0x83, 0x20, 0x00); + + /* reset equaliser */ + u_threshold = stv0297_readreg(state, 0x00) & 0xf; + initial_u = stv0297_readreg(state, 0x01) >> 4; + blind_u = stv0297_readreg(state, 0x01) & 0xf; + stv0297_writereg_mask(state, 0x84, 0x01, 0x01); + stv0297_writereg_mask(state, 0x84, 0x01, 0x00); + stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold); + stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4); + stv0297_writereg_mask(state, 0x01, 0x0f, blind_u); + + /* data comes from internal A/D */ + stv0297_writereg_mask(state, 0x87, 0x80, 0x00); + + /* clear phase registers */ + stv0297_writereg(state, 0x63, 0x00); + stv0297_writereg(state, 0x64, 0x00); + stv0297_writereg(state, 0x65, 0x00); + stv0297_writereg(state, 0x66, 0x00); + stv0297_writereg(state, 0x67, 0x00); + stv0297_writereg(state, 0x68, 0x00); + stv0297_writereg_mask(state, 0x69, 0x0f, 0x00); + + /* set parameters */ + stv0297_set_qam(state, p->modulation); + stv0297_set_symbolrate(state, p->symbol_rate / 1000); + stv0297_set_sweeprate(state, sweeprate, p->symbol_rate / 1000); + stv0297_set_carrieroffset(state, carrieroffset); + stv0297_set_inversion(state, inversion); + + /* kick off lock */ + /* Disable corner detection for higher QAMs */ + if (p->modulation == QAM_128 || + p->modulation == QAM_256) + stv0297_writereg_mask(state, 0x88, 0x08, 0x00); + else + stv0297_writereg_mask(state, 0x88, 0x08, 0x08); + + stv0297_writereg_mask(state, 0x5a, 0x20, 0x00); + stv0297_writereg_mask(state, 0x6a, 0x01, 0x01); + stv0297_writereg_mask(state, 0x43, 0x40, 0x40); + stv0297_writereg_mask(state, 0x5b, 0x30, 0x00); + stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c); + stv0297_writereg_mask(state, 0x03, 0x03, 0x03); + stv0297_writereg_mask(state, 0x43, 0x10, 0x10); + + /* wait for WGAGC lock */ + timeout = jiffies + msecs_to_jiffies(2000); + while (time_before(jiffies, timeout)) { + msleep(10); + if (stv0297_readreg(state, 0x43) & 0x08) + break; + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + msleep(20); + + /* wait for equaliser partial convergence */ + timeout = jiffies + msecs_to_jiffies(500); + while (time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0x82) & 0x04) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + + /* wait for equaliser full convergence */ + timeout = jiffies + msecs_to_jiffies(delay); + while (time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0x82) & 0x08) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + + /* disable sweep */ + stv0297_writereg_mask(state, 0x6a, 1, 0); + stv0297_writereg_mask(state, 0x88, 8, 0); + + /* wait for main lock */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0xDF) & 0x80) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + msleep(100); + + /* is it still locked after that delay? */ + if (!(stv0297_readreg(state, 0xDF) & 0x80)) { + goto timeout; + } + + /* success!! */ + stv0297_writereg_mask(state, 0x5a, 0x40, 0x00); + state->base_freq = p->frequency; + return 0; + +timeout: + stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); + return 0; +} + +static int stv0297_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv0297_state *state = fe->demodulator_priv; + int reg_00, reg_83; + + reg_00 = stv0297_readreg(state, 0x00); + reg_83 = stv0297_readreg(state, 0x83); + + p->frequency = state->base_freq; + p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF; + if (state->config->invert) + p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; + p->symbol_rate = stv0297_get_symbolrate(state) * 1000; + p->fec_inner = FEC_NONE; + + switch ((reg_00 >> 4) & 0x7) { + case 0: + p->modulation = QAM_16; + break; + case 1: + p->modulation = QAM_32; + break; + case 2: + p->modulation = QAM_128; + break; + case 3: + p->modulation = QAM_256; + break; + case 4: + p->modulation = QAM_64; + break; + } + + return 0; +} + +static void stv0297_release(struct dvb_frontend *fe) +{ + struct stv0297_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops stv0297_ops; + +struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, + struct i2c_adapter *i2c) +{ + struct stv0297_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->last_ber = 0; + state->base_freq = 0; + + /* check if the demod is there */ + if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &stv0297_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops stv0297_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "ST STV0297 DVB-C", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO}, + + .release = stv0297_release, + + .init = stv0297_init, + .sleep = stv0297_sleep, + .i2c_gate_ctrl = stv0297_i2c_gate_ctrl, + + .set_frontend = stv0297_set_frontend, + .get_frontend = stv0297_get_frontend, + + .read_status = stv0297_read_status, + .read_ber = stv0297_read_ber, + .read_signal_strength = stv0297_read_signal_strength, + .read_snr = stv0297_read_snr, + .read_ucblocks = stv0297_read_ucblocks, +}; + +MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); +MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(stv0297_attach); diff --git a/drivers/media/dvb-frontends/stv0297.h b/drivers/media/dvb-frontends/stv0297.h new file mode 100644 index 0000000000..dfa0f84513 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0297.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for STV0297 demodulator + + Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de> + +*/ + +#ifndef STV0297_H +#define STV0297_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +struct stv0297_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* inittab - array of pairs of values. + * First of each pair is the register, second is the value. + * List should be terminated with an 0xff, 0xff pair. + */ + u8* inittab; + + /* does the "inversion" need inverted? */ + u8 invert:1; + + /* set to 1 if the device requires an i2c STOP during reading */ + u8 stop_during_read:1; +}; + +#if IS_REACHABLE(CONFIG_DVB_STV0297) +extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_STV0297 + +#endif // STV0297_H diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c new file mode 100644 index 0000000000..da7ff2c2e8 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0299.c @@ -0,0 +1,755 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for ST STV0299 demodulator + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + <ralph@convergence.de>, + <holger@convergence.de>, + <js@convergence.de> + + + Philips SU1278/SH + + Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de> + + + LG TDQF-S001F + + Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net> + & Andreas Oberritter <obi@linuxtv.org> + + + Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B + + Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>: + + Support for Philips SU1278 on Technotrend hardware + + Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> + + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "stv0299.h" + +struct stv0299_state { + struct i2c_adapter* i2c; + const struct stv0299_config* config; + struct dvb_frontend frontend; + + u8 initialised:1; + u32 tuner_frequency; + u32 symbol_rate; + enum fe_code_rate fec_inner; + int errmode; + u32 ucblocks; + u8 mcr_reg; +}; + +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 + +static int debug; +static int debug_legacy_dish_switch; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "stv0299: " args); \ + } while (0) + + +static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer (state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int stv0299_write(struct dvb_frontend* fe, const u8 buf[], int len) +{ + struct stv0299_state* state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return stv0299_writeregI(state, buf[0], buf[1]); +} + +static u8 stv0299_readreg (struct stv0299_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (ret == %i)\n", __func__, ret); + + return ret == 2 ? 0 : ret; +} + +static int stv0299_set_FEC(struct stv0299_state *state, enum fe_code_rate fec) +{ + dprintk ("%s\n", __func__); + + switch (fec) { + case FEC_AUTO: + { + return stv0299_writeregI (state, 0x31, 0x1f); + } + case FEC_1_2: + { + return stv0299_writeregI (state, 0x31, 0x01); + } + case FEC_2_3: + { + return stv0299_writeregI (state, 0x31, 0x02); + } + case FEC_3_4: + { + return stv0299_writeregI (state, 0x31, 0x04); + } + case FEC_5_6: + { + return stv0299_writeregI (state, 0x31, 0x08); + } + case FEC_7_8: + { + return stv0299_writeregI (state, 0x31, 0x10); + } + default: + { + return -EINVAL; + } + } +} + +static enum fe_code_rate stv0299_get_fec(struct stv0299_state *state) +{ + static const enum fe_code_rate fec_tab[] = { + FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_1_2 + }; + u8 index; + + dprintk ("%s\n", __func__); + + index = stv0299_readreg (state, 0x1b); + index &= 0x7; + + if (index > 4) + return FEC_AUTO; + + return fec_tab [index]; +} + +static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) +{ + unsigned long start = jiffies; + + dprintk ("%s\n", __func__); + + while (stv0299_readreg(state, 0x0a) & 1) { + if (time_is_before_jiffies(start + timeout)) { + dprintk ("%s: timeout!!\n", __func__); + return -ETIMEDOUT; + } + msleep(10); + } + + return 0; +} + +static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) +{ + unsigned long start = jiffies; + + dprintk ("%s\n", __func__); + + while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) { + if (time_is_before_jiffies(start + timeout)) { + dprintk ("%s: timeout!!\n", __func__); + return -ETIMEDOUT; + } + msleep(10); + } + + return 0; +} + +static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate) +{ + struct stv0299_state* state = fe->demodulator_priv; + u64 big = srate; + u32 ratio; + + // check rate is within limits + if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + + // calculate value to program + big = big << 20; + big += (state->config->mclk-1); // round correctly + do_div(big, state->config->mclk); + ratio = big << 4; + + return state->config->set_symbol_rate(fe, srate, ratio); +} + +static int stv0299_get_symbolrate (struct stv0299_state* state) +{ + u32 Mclk = state->config->mclk / 4096L; + u32 srate; + s32 offset; + u8 sfr[3]; + s8 rtf; + + dprintk ("%s\n", __func__); + + stv0299_readregs (state, 0x1f, sfr, 3); + stv0299_readregs (state, 0x1a, (u8 *)&rtf, 1); + + srate = (sfr[0] << 8) | sfr[1]; + srate *= Mclk; + srate /= 16; + srate += (sfr[2] >> 4) * Mclk / 256; + offset = (s32) rtf * (srate / 4096L); + offset /= 128; + + dprintk ("%s : srate = %i\n", __func__, srate); + dprintk ("%s : ofset = %i\n", __func__, offset); + + srate += offset; + + srate += 1000; + srate /= 2000; + srate *= 2000; + + return srate; +} + +static int stv0299_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + struct stv0299_state* state = fe->demodulator_priv; + u8 val; + int i; + + dprintk ("%s\n", __func__); + + if (stv0299_wait_diseqc_idle (state, 100) < 0) + return -ETIMEDOUT; + + val = stv0299_readreg (state, 0x08); + + if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ + return -EREMOTEIO; + + for (i=0; i<m->msg_len; i++) { + if (stv0299_wait_diseqc_fifo (state, 100) < 0) + return -ETIMEDOUT; + + if (stv0299_writeregI (state, 0x09, m->msg[i])) + return -EREMOTEIO; + } + + if (stv0299_wait_diseqc_idle (state, 100) < 0) + return -ETIMEDOUT; + + return 0; +} + +static int stv0299_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct stv0299_state* state = fe->demodulator_priv; + u8 val; + + dprintk ("%s\n", __func__); + + if (stv0299_wait_diseqc_idle (state, 100) < 0) + return -ETIMEDOUT; + + val = stv0299_readreg (state, 0x08); + + if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ + return -EREMOTEIO; + + if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) + return -EREMOTEIO; + + if (stv0299_wait_diseqc_idle (state, 100) < 0) + return -ETIMEDOUT; + + if (stv0299_writeregI (state, 0x08, val)) + return -EREMOTEIO; + + return 0; +} + +static int stv0299_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct stv0299_state* state = fe->demodulator_priv; + u8 val; + + if (stv0299_wait_diseqc_idle (state, 100) < 0) + return -ETIMEDOUT; + + val = stv0299_readreg (state, 0x08); + + switch (tone) { + case SEC_TONE_ON: + return stv0299_writeregI (state, 0x08, val | 0x3); + + case SEC_TONE_OFF: + return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02); + + default: + return -EINVAL; + } +} + +static int stv0299_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct stv0299_state* state = fe->demodulator_priv; + u8 reg0x08; + u8 reg0x0c; + + dprintk("%s: %s\n", __func__, + voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + reg0x08 = stv0299_readreg (state, 0x08); + reg0x0c = stv0299_readreg (state, 0x0c); + + /* + * H/V switching over OP0, OP1 and OP2 are LNB power enable bits + */ + reg0x0c &= 0x0f; + reg0x08 = (reg0x08 & 0x3f) | (state->config->lock_output << 6); + + switch (voltage) { + case SEC_VOLTAGE_13: + if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) + reg0x0c |= 0x10; /* OP1 off, OP0 on */ + else + reg0x0c |= 0x40; /* OP1 on, OP0 off */ + break; + case SEC_VOLTAGE_18: + reg0x0c |= 0x50; /* OP1 on, OP0 on */ + break; + case SEC_VOLTAGE_OFF: + /* LNB power off! */ + reg0x08 = 0x00; + reg0x0c = 0x00; + break; + default: + return -EINVAL; + } + + if (state->config->op0_off) + reg0x0c &= ~0x10; + + stv0299_writeregI(state, 0x08, reg0x08); + return stv0299_writeregI(state, 0x0c, reg0x0c); +} + +static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long cmd) +{ + struct stv0299_state* state = fe->demodulator_priv; + u8 reg0x08; + u8 reg0x0c; + u8 lv_mask = 0x40; + u8 last = 1; + int i; + ktime_t nexttime; + ktime_t tv[10]; + + reg0x08 = stv0299_readreg (state, 0x08); + reg0x0c = stv0299_readreg (state, 0x0c); + reg0x0c &= 0x0f; + stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6)); + if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) + lv_mask = 0x10; + + cmd = cmd << 1; + if (debug_legacy_dish_switch) + printk ("%s switch command: 0x%04lx\n",__func__, cmd); + + nexttime = ktime_get_boottime(); + if (debug_legacy_dish_switch) + tv[0] = nexttime; + stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ + + dvb_frontend_sleep_until(&nexttime, 32000); + + for (i=0; i<9; i++) { + if (debug_legacy_dish_switch) + tv[i+1] = ktime_get_boottime(); + if((cmd & 0x01) != last) { + /* set voltage to (last ? 13V : 18V) */ + stv0299_writeregI (state, 0x0c, reg0x0c | (last ? lv_mask : 0x50)); + last = (last) ? 0 : 1; + } + + cmd = cmd >> 1; + + if (i != 8) + dvb_frontend_sleep_until(&nexttime, 8000); + } + if (debug_legacy_dish_switch) { + printk ("%s(%d): switch delay (should be 32k followed by all 8k\n", + __func__, fe->dvb->num); + for (i = 1; i < 10; i++) + printk("%d: %d\n", i, + (int) ktime_us_delta(tv[i], tv[i-1])); + } + + return 0; +} + +static int stv0299_init (struct dvb_frontend* fe) +{ + struct stv0299_state* state = fe->demodulator_priv; + int i; + u8 reg; + u8 val; + + dprintk("stv0299: init chip\n"); + + stv0299_writeregI(state, 0x02, 0x30 | state->mcr_reg); + msleep(50); + + for (i = 0; ; i += 2) { + reg = state->config->inittab[i]; + val = state->config->inittab[i+1]; + if (reg == 0xff && val == 0xff) + break; + if (reg == 0x0c && state->config->op0_off) + val &= ~0x10; + if (reg == 0x2) + state->mcr_reg = val & 0xf; + stv0299_writeregI(state, reg, val); + } + + return 0; +} + +static int stv0299_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct stv0299_state* state = fe->demodulator_priv; + + u8 signal = 0xff - stv0299_readreg (state, 0x18); + u8 sync = stv0299_readreg (state, 0x1b); + + dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x80) + *status |= FE_HAS_CARRIER; + + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + + if (sync & 0x08) + *status |= FE_HAS_SYNC; + + if ((sync & 0x98) == 0x98) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct stv0299_state* state = fe->demodulator_priv; + + if (state->errmode != STATUS_BER) + return -ENOSYS; + + *ber = stv0299_readreg(state, 0x1e) | (stv0299_readreg(state, 0x1d) << 8); + + return 0; +} + +static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct stv0299_state* state = fe->demodulator_priv; + + s32 signal = 0xffff - ((stv0299_readreg (state, 0x18) << 8) + | stv0299_readreg (state, 0x19)); + + dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __func__, + stv0299_readreg (state, 0x18), + stv0299_readreg (state, 0x19), (int) signal); + + signal = signal * 5 / 4; + *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; + + return 0; +} + +static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct stv0299_state* state = fe->demodulator_priv; + + s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8) + | stv0299_readreg (state, 0x25)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + + return 0; +} + +static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct stv0299_state* state = fe->demodulator_priv; + + if (state->errmode != STATUS_UCBLOCKS) + return -ENOSYS; + + state->ucblocks += stv0299_readreg(state, 0x1e); + state->ucblocks += (stv0299_readreg(state, 0x1d) << 8); + *ucblocks = state->ucblocks; + + return 0; +} + +static int stv0299_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct stv0299_state* state = fe->demodulator_priv; + int invval = 0; + + dprintk ("%s : FE_SET_FRONTEND\n", __func__); + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + // set the inversion + if (p->inversion == INVERSION_OFF) invval = 0; + else if (p->inversion == INVERSION_ON) invval = 1; + else { + printk("stv0299 does not support auto-inversion\n"); + return -EINVAL; + } + if (state->config->invert) invval = (~invval) & 1; + stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + stv0299_set_FEC(state, p->fec_inner); + stv0299_set_symbolrate(fe, p->symbol_rate); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); + + state->tuner_frequency = p->frequency; + state->fec_inner = p->fec_inner; + state->symbol_rate = p->symbol_rate; + + return 0; +} + +static int stv0299_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv0299_state* state = fe->demodulator_priv; + s32 derot_freq; + int invval; + + derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8) + | stv0299_readreg (state, 0x23)); + + derot_freq *= (state->config->mclk >> 16); + derot_freq += 500; + derot_freq /= 1000; + + p->frequency += derot_freq; + + invval = stv0299_readreg (state, 0x0c) & 1; + if (state->config->invert) invval = (~invval) & 1; + p->inversion = invval ? INVERSION_ON : INVERSION_OFF; + + p->fec_inner = stv0299_get_fec(state); + p->symbol_rate = stv0299_get_symbolrate(state); + + return 0; +} + +static int stv0299_sleep(struct dvb_frontend* fe) +{ + struct stv0299_state* state = fe->demodulator_priv; + + stv0299_writeregI(state, 0x02, 0xb0 | state->mcr_reg); + state->initialised = 0; + + return 0; +} + +static int stv0299_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct stv0299_state* state = fe->demodulator_priv; + + if (enable) { + stv0299_writeregI(state, 0x05, 0xb5); + } else { + stv0299_writeregI(state, 0x05, 0x35); + } + udelay(1); + return 0; +} + +static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + struct stv0299_state* state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + fesettings->min_delay_ms = state->config->min_delay_ms; + if (p->symbol_rate < 10000000) { + fesettings->step_size = p->symbol_rate / 32000; + fesettings->max_drift = 5000; + } else { + fesettings->step_size = p->symbol_rate / 16000; + fesettings->max_drift = p->symbol_rate / 2000; + } + return 0; +} + +static void stv0299_release(struct dvb_frontend* fe) +{ + struct stv0299_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops stv0299_ops; + +struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, + struct i2c_adapter* i2c) +{ + struct stv0299_state* state = NULL; + int id; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + state->errmode = STATUS_BER; + + /* check if the demod is there */ + stv0299_writeregI(state, 0x02, 0x30); /* standby off */ + msleep(200); + id = stv0299_readreg(state, 0x00); + + /* register 0x00 contains 0xa1 for STV0299 and STV0299B */ + /* register 0x00 might contain 0x80 when returning from standby */ + if (id != 0xa1 && id != 0x80) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &stv0299_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops stv0299_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "ST STV0299 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = stv0299_release, + + .init = stv0299_init, + .sleep = stv0299_sleep, + .write = stv0299_write, + .i2c_gate_ctrl = stv0299_i2c_gate_ctrl, + + .set_frontend = stv0299_set_frontend, + .get_frontend = stv0299_get_frontend, + .get_tune_settings = stv0299_get_tune_settings, + + .read_status = stv0299_read_status, + .read_ber = stv0299_read_ber, + .read_signal_strength = stv0299_read_signal_strength, + .read_snr = stv0299_read_snr, + .read_ucblocks = stv0299_read_ucblocks, + + .diseqc_send_master_cmd = stv0299_send_diseqc_msg, + .diseqc_send_burst = stv0299_send_diseqc_burst, + .set_tone = stv0299_set_tone, + .set_voltage = stv0299_set_voltage, + .dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd, +}; + +module_param(debug_legacy_dish_switch, int, 0444); +MODULE_PARM_DESC(debug_legacy_dish_switch, "Enable timing analysis for Dish Network legacy switches"); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(stv0299_attach); diff --git a/drivers/media/dvb-frontends/stv0299.h b/drivers/media/dvb-frontends/stv0299.h new file mode 100644 index 0000000000..4f97c3d15a --- /dev/null +++ b/drivers/media/dvb-frontends/stv0299.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for ST STV0299 demodulator + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + <ralph@convergence.de>, + <holger@convergence.de>, + <js@convergence.de> + + + Philips SU1278/SH + + Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de> + + + LG TDQF-S001F + + Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net> + & Andreas Oberritter <obi@linuxtv.org> + + + Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B + + Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>: + + Support for Philips SU1278 on Technotrend hardware + + Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> + + +*/ + +#ifndef STV0299_H +#define STV0299_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +#define STV0299_LOCKOUTPUT_0 0 +#define STV0299_LOCKOUTPUT_1 1 +#define STV0299_LOCKOUTPUT_CF 2 +#define STV0299_LOCKOUTPUT_LK 3 + +#define STV0299_VOLT13_OP0 0 +#define STV0299_VOLT13_OP1 1 + +struct stv0299_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* inittab - array of pairs of values. + * First of each pair is the register, second is the value. + * List should be terminated with an 0xff, 0xff pair. + */ + const u8* inittab; + + /* master clock to use */ + u32 mclk; + + /* does the inversion require inversion? */ + u8 invert:1; + + /* Skip reinitialisation? */ + u8 skip_reinit:1; + + /* LOCK OUTPUT setting */ + u8 lock_output:2; + + /* Is 13v controlled by OP0 or OP1? */ + u8 volt13_op0_op1:1; + + /* Turn-off OP0? */ + u8 op0_off:1; + + /* minimum delay before retuning */ + int min_delay_ms; + + /* Set the symbol rate */ + int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio); + + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +}; + +#if IS_REACHABLE(CONFIG_DVB_STV0299) +extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_STV0299 + +static inline int stv0299_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} + +#endif // STV0299_H diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c new file mode 100644 index 0000000000..4832643448 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0367.c @@ -0,0 +1,3350 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * stv0367.c + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/i2c.h> + +#include <linux/int_log.h> + +#include "stv0367.h" +#include "stv0367_defs.h" +#include "stv0367_regs.h" +#include "stv0367_priv.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +static int stvdebug; +module_param_named(debug, stvdebug, int, 0644); + +static int i2cdebug; +module_param_named(i2c_debug, i2cdebug, int, 0644); + +#define dprintk(args...) \ + do { \ + if (stvdebug) \ + printk(KERN_DEBUG args); \ + } while (0) + /* DVB-C */ + +enum active_demod_state { demod_none, demod_ter, demod_cab }; + +struct stv0367cab_state { + enum stv0367_cab_signal_type state; + u32 mclk; + u32 adc_clk; + s32 search_range; + s32 derot_offset; + /* results */ + int locked; /* channel found */ + u32 freq_khz; /* found frequency (in kHz) */ + u32 symbol_rate; /* found symbol rate (in Bds) */ + enum fe_spectral_inversion spect_inv; /* Spectrum Inversion */ + u32 qamfec_status_reg; /* status reg to poll for FEC Lock */ +}; + +struct stv0367ter_state { + /* DVB-T */ + enum stv0367_ter_signal_type state; + enum stv0367_ter_if_iq_mode if_iq_mode; + enum stv0367_ter_mode mode;/* mode 2K or 8K */ + enum fe_guard_interval guard; + enum stv0367_ter_hierarchy hierarchy; + u32 frequency; + enum fe_spectral_inversion sense; /* current search spectrum */ + u8 force; /* force mode/guard */ + u8 bw; /* channel width 6, 7 or 8 in MHz */ + u8 pBW; /* channel width used during previous lock */ + u32 pBER; + u32 pPER; + u32 ucblocks; + s8 echo_pos; /* echo position */ + u8 first_lock; + u8 unlock_counter; + u32 agc_val; +}; + +struct stv0367_state { + struct dvb_frontend fe; + struct i2c_adapter *i2c; + /* config settings */ + const struct stv0367_config *config; + u8 chip_id; + /* DVB-C */ + struct stv0367cab_state *cab_state; + /* DVB-T */ + struct stv0367ter_state *ter_state; + /* flags for operation control */ + u8 use_i2c_gatectrl; + u8 deftabs; + u8 reinit_on_setfrontend; + u8 auto_if_khz; + enum active_demod_state activedemod; +}; + +#define RF_LOOKUP_TABLE_SIZE 31 +#define RF_LOOKUP_TABLE2_SIZE 16 +/* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/ +static const s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { + {/*AGC1*/ + 48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 80, 83, 85, 88, + }, {/*RF(dbm)*/ + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 46, 47, + 49, 50, 52, 53, 54, 55, 56, + } +}; +/* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/ +static const s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = { + {/*AGC2*/ + 28, 29, 31, 32, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, + }, {/*RF(dbm)*/ + 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + } +}; + +static +int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len) +{ + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = len + 2 + }; + int ret; + + if (2 + len > sizeof(buf)) { + printk(KERN_WARNING + "%s: i2c wr reg=%04x: len=%d is too big!\n", + KBUILD_MODNAME, reg, len); + return -EINVAL; + } + + + buf[0] = MSB(reg); + buf[1] = LSB(reg); + memcpy(buf + 2, data, len); + + if (i2cdebug) + printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__, + state->config->demod_address, reg, buf[2]); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) + printk(KERN_ERR "%s: i2c write error! ([%02x] %02x: %02x)\n", + __func__, state->config->demod_address, reg, buf[2]); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int stv0367_writereg(struct stv0367_state *state, u16 reg, u8 data) +{ + u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return stv0367_writeregs(state, reg, &tmp, 1); +} + +static u8 stv0367_readreg(struct stv0367_state *state, u16 reg) +{ + u8 b0[] = { 0, 0 }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 2 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + int ret; + + b0[0] = MSB(reg); + b0[1] = LSB(reg); + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) + printk(KERN_ERR "%s: i2c read error ([%02x] %02x: %02x)\n", + __func__, state->config->demod_address, reg, b1[0]); + + if (i2cdebug) + printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__, + state->config->demod_address, reg, b1[0]); + + return b1[0]; +} + +static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) +{ + u8 position = 0, i = 0; + + (*mask) = label & 0xff; + + while ((position == 0) && (i < 8)) { + position = ((*mask) >> i) & 0x01; + i++; + } + + (*pos) = (i - 1); +} + +static void stv0367_writebits(struct stv0367_state *state, u32 label, u8 val) +{ + u8 reg, mask, pos; + + reg = stv0367_readreg(state, (label >> 16) & 0xffff); + extract_mask_pos(label, &mask, &pos); + + val = mask & (val << pos); + + reg = (reg & (~mask)) | val; + stv0367_writereg(state, (label >> 16) & 0xffff, reg); + +} + +static void stv0367_setbits(u8 *reg, u32 label, u8 val) +{ + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + val = mask & (val << pos); + + (*reg) = ((*reg) & (~mask)) | val; +} + +static u8 stv0367_readbits(struct stv0367_state *state, u32 label) +{ + u8 val = 0xff; + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + val = stv0367_readreg(state, label >> 16); + val = (val & mask) >> pos; + + return val; +} + +#if 0 /* Currently, unused */ +static u8 stv0367_getbits(u8 reg, u32 label) +{ + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + return (reg & mask) >> pos; +} +#endif + +static void stv0367_write_table(struct stv0367_state *state, + const struct st_register *deftab) +{ + int i = 0; + + while (1) { + if (!deftab[i].addr) + break; + stv0367_writereg(state, deftab[i].addr, deftab[i].value); + i++; + } +} + +static void stv0367_pll_setup(struct stv0367_state *state, + u32 icspeed, u32 xtal) +{ + /* note on regs: R367TER_* and R367CAB_* defines each point to + * 0xf0d8, so just use R367TER_ for both cases + */ + + switch (icspeed) { + case STV0367_ICSPEED_58000: + switch (xtal) { + default: + case 27000000: + dprintk("STV0367 SetCLKgen for 58MHz IC and 27Mhz crystal\n"); + /* PLLMDIV: 27, PLLNDIV: 232 */ + stv0367_writereg(state, R367TER_PLLMDIV, 0x1b); + stv0367_writereg(state, R367TER_PLLNDIV, 0xe8); + break; + } + break; + default: + case STV0367_ICSPEED_53125: + switch (xtal) { + /* set internal freq to 53.125MHz */ + case 16000000: + stv0367_writereg(state, R367TER_PLLMDIV, 0x2); + stv0367_writereg(state, R367TER_PLLNDIV, 0x1b); + break; + case 25000000: + stv0367_writereg(state, R367TER_PLLMDIV, 0xa); + stv0367_writereg(state, R367TER_PLLNDIV, 0x55); + break; + default: + case 27000000: + dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n"); + stv0367_writereg(state, R367TER_PLLMDIV, 0x1); + stv0367_writereg(state, R367TER_PLLNDIV, 0x8); + break; + case 30000000: + stv0367_writereg(state, R367TER_PLLMDIV, 0xc); + stv0367_writereg(state, R367TER_PLLNDIV, 0x55); + break; + } + } + + stv0367_writereg(state, R367TER_PLLSETUP, 0x18); +} + +static int stv0367_get_if_khz(struct stv0367_state *state, u32 *ifkhz) +{ + if (state->auto_if_khz && state->fe.ops.tuner_ops.get_if_frequency) { + state->fe.ops.tuner_ops.get_if_frequency(&state->fe, ifkhz); + *ifkhz = *ifkhz / 1000; /* hz -> khz */ + } else + *ifkhz = state->config->if_khz; + + return 0; +} + +static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0367_state *state = fe->demodulator_priv; + u8 tmp = stv0367_readreg(state, R367TER_I2CRPT); + + dprintk("%s:\n", __func__); + + if (enable) { + stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 0); + stv0367_setbits(&tmp, F367TER_I2CT_ON, 1); + } else { + stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 1); + stv0367_setbits(&tmp, F367TER_I2CT_ON, 0); + } + + stv0367_writereg(state, R367TER_I2CRPT, tmp); + + return 0; +} + +static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + u32 freq = 0; + int err = 0; + + dprintk("%s:\n", __func__); + + if (tuner_ops->get_frequency) { + err = tuner_ops->get_frequency(fe, &freq); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + dprintk("%s: frequency=%d\n", __func__, freq); + + } else + return -1; + + return freq; +} + +static u16 CellsCoeffs_8MHz_367cofdm[3][6][5] = { + { + {0x10EF, 0xE205, 0x10EF, 0xCE49, 0x6DA7}, /* CELL 1 COEFFS 27M*/ + {0x2151, 0xc557, 0x2151, 0xc705, 0x6f93}, /* CELL 2 COEFFS */ + {0x2503, 0xc000, 0x2503, 0xc375, 0x7194}, /* CELL 3 COEFFS */ + {0x20E9, 0xca94, 0x20e9, 0xc153, 0x7194}, /* CELL 4 COEFFS */ + {0x06EF, 0xF852, 0x06EF, 0xC057, 0x7207}, /* CELL 5 COEFFS */ + {0x0000, 0x0ECC, 0x0ECC, 0x0000, 0x3647} /* CELL 6 COEFFS */ + }, { + {0x10A0, 0xE2AF, 0x10A1, 0xCE76, 0x6D6D}, /* CELL 1 COEFFS 25M*/ + {0x20DC, 0xC676, 0x20D9, 0xC80A, 0x6F29}, + {0x2532, 0xC000, 0x251D, 0xC391, 0x706F}, + {0x1F7A, 0xCD2B, 0x2032, 0xC15E, 0x711F}, + {0x0698, 0xFA5E, 0x0568, 0xC059, 0x7193}, + {0x0000, 0x0918, 0x149C, 0x0000, 0x3642} /* CELL 6 COEFFS */ + }, { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} + } +}; + +static u16 CellsCoeffs_7MHz_367cofdm[3][6][5] = { + { + {0x12CA, 0xDDAF, 0x12CA, 0xCCEB, 0x6FB1}, /* CELL 1 COEFFS 27M*/ + {0x2329, 0xC000, 0x2329, 0xC6B0, 0x725F}, /* CELL 2 COEFFS */ + {0x2394, 0xC000, 0x2394, 0xC2C7, 0x7410}, /* CELL 3 COEFFS */ + {0x251C, 0xC000, 0x251C, 0xC103, 0x74D9}, /* CELL 4 COEFFS */ + {0x0804, 0xF546, 0x0804, 0xC040, 0x7544}, /* CELL 5 COEFFS */ + {0x0000, 0x0CD9, 0x0CD9, 0x0000, 0x370A} /* CELL 6 COEFFS */ + }, { + {0x1285, 0xDE47, 0x1285, 0xCD17, 0x6F76}, /*25M*/ + {0x234C, 0xC000, 0x2348, 0xC6DA, 0x7206}, + {0x23B4, 0xC000, 0x23AC, 0xC2DB, 0x73B3}, + {0x253D, 0xC000, 0x25B6, 0xC10B, 0x747F}, + {0x0721, 0xF79C, 0x065F, 0xC041, 0x74EB}, + {0x0000, 0x08FA, 0x1162, 0x0000, 0x36FF} + }, { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} + } +}; + +static u16 CellsCoeffs_6MHz_367cofdm[3][6][5] = { + { + {0x1699, 0xD5B8, 0x1699, 0xCBC3, 0x713B}, /* CELL 1 COEFFS 27M*/ + {0x2245, 0xC000, 0x2245, 0xC568, 0x74D5}, /* CELL 2 COEFFS */ + {0x227F, 0xC000, 0x227F, 0xC1FC, 0x76C6}, /* CELL 3 COEFFS */ + {0x235E, 0xC000, 0x235E, 0xC0A7, 0x778A}, /* CELL 4 COEFFS */ + {0x0ECB, 0xEA0B, 0x0ECB, 0xC027, 0x77DD}, /* CELL 5 COEFFS */ + {0x0000, 0x0B68, 0x0B68, 0x0000, 0xC89A}, /* CELL 6 COEFFS */ + }, { + {0x1655, 0xD64E, 0x1658, 0xCBEF, 0x70FE}, /*25M*/ + {0x225E, 0xC000, 0x2256, 0xC589, 0x7489}, + {0x2293, 0xC000, 0x2295, 0xC209, 0x767E}, + {0x2377, 0xC000, 0x23AA, 0xC0AB, 0x7746}, + {0x0DC7, 0xEBC8, 0x0D07, 0xC027, 0x7799}, + {0x0000, 0x0888, 0x0E9C, 0x0000, 0x3757} + + }, { + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} + } +}; + +static u32 stv0367ter_get_mclk(struct stv0367_state *state, u32 ExtClk_Hz) +{ + u32 mclk_Hz = 0; /* master clock frequency (Hz) */ + u32 m, n, p; + + dprintk("%s:\n", __func__); + + if (stv0367_readbits(state, F367TER_BYPASS_PLLXN) == 0) { + n = (u32)stv0367_readbits(state, F367TER_PLL_NDIV); + if (n == 0) + n = n + 1; + + m = (u32)stv0367_readbits(state, F367TER_PLL_MDIV); + if (m == 0) + m = m + 1; + + p = (u32)stv0367_readbits(state, F367TER_PLL_PDIV); + if (p > 5) + p = 5; + + mclk_Hz = ((ExtClk_Hz / 2) * n) / (m * (1 << p)); + + dprintk("N=%d M=%d P=%d mclk_Hz=%d ExtClk_Hz=%d\n", + n, m, p, mclk_Hz, ExtClk_Hz); + } else + mclk_Hz = ExtClk_Hz; + + dprintk("%s: mclk_Hz=%d\n", __func__, mclk_Hz); + + return mclk_Hz; +} + +static int stv0367ter_filt_coeff_init(struct stv0367_state *state, + u16 CellsCoeffs[3][6][5], u32 DemodXtal) +{ + int i, j, k, freq; + + dprintk("%s:\n", __func__); + + freq = stv0367ter_get_mclk(state, DemodXtal); + + if (freq == 53125000) + k = 1; /* equivalent to Xtal 25M on 362*/ + else if (freq == 54000000) + k = 0; /* equivalent to Xtal 27M on 362*/ + else if (freq == 52500000) + k = 2; /* equivalent to Xtal 30M on 362*/ + else + return 0; + + for (i = 1; i <= 6; i++) { + stv0367_writebits(state, F367TER_IIR_CELL_NB, i - 1); + + for (j = 1; j <= 5; j++) { + stv0367_writereg(state, + (R367TER_IIRCX_COEFF1_MSB + 2 * (j - 1)), + MSB(CellsCoeffs[k][i-1][j-1])); + stv0367_writereg(state, + (R367TER_IIRCX_COEFF1_LSB + 2 * (j - 1)), + LSB(CellsCoeffs[k][i-1][j-1])); + } + } + + return 1; + +} + +static void stv0367ter_agc_iir_lock_detect_set(struct stv0367_state *state) +{ + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367TER_LOCK_DETECT_LSB, 0x00); + + /* Lock detect 1 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x00); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); + + /* Lock detect 2 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x01); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); + + /* Lock detect 3 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x02); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); + + /* Lock detect 4 */ + stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x03); + stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); + +} + +static int stv0367_iir_filt_init(struct stv0367_state *state, u8 Bandwidth, + u32 DemodXtalValue) +{ + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367TER_NRST_IIR, 0); + + switch (Bandwidth) { + case 6: + if (!stv0367ter_filt_coeff_init(state, + CellsCoeffs_6MHz_367cofdm, + DemodXtalValue)) + return 0; + break; + case 7: + if (!stv0367ter_filt_coeff_init(state, + CellsCoeffs_7MHz_367cofdm, + DemodXtalValue)) + return 0; + break; + case 8: + if (!stv0367ter_filt_coeff_init(state, + CellsCoeffs_8MHz_367cofdm, + DemodXtalValue)) + return 0; + break; + default: + return 0; + } + + stv0367_writebits(state, F367TER_NRST_IIR, 1); + + return 1; +} + +static void stv0367ter_agc_iir_rst(struct stv0367_state *state) +{ + + u8 com_n; + + dprintk("%s:\n", __func__); + + com_n = stv0367_readbits(state, F367TER_COM_N); + + stv0367_writebits(state, F367TER_COM_N, 0x07); + + stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x00); + stv0367_writebits(state, F367TER_COM_AGC_ON, 0x00); + + stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x01); + stv0367_writebits(state, F367TER_COM_AGC_ON, 0x01); + + stv0367_writebits(state, F367TER_COM_N, com_n); + +} + +static int stv0367ter_duration(s32 mode, int tempo1, int tempo2, int tempo3) +{ + int local_tempo = 0; + switch (mode) { + case 0: + local_tempo = tempo1; + break; + case 1: + local_tempo = tempo2; + break ; + + case 2: + local_tempo = tempo3; + break; + + default: + break; + } + /* msleep(local_tempo); */ + return local_tempo; +} + +static enum +stv0367_ter_signal_type stv0367ter_check_syr(struct stv0367_state *state) +{ + int wd = 100; + unsigned short int SYR_var; + s32 SYRStatus; + + dprintk("%s:\n", __func__); + + SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); + + while ((!SYR_var) && (wd > 0)) { + usleep_range(2000, 3000); + wd -= 2; + SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); + } + + if (!SYR_var) + SYRStatus = FE_TER_NOSYMBOL; + else + SYRStatus = FE_TER_SYMBOLOK; + + dprintk("stv0367ter_check_syr SYRStatus %s\n", + SYR_var == 0 ? "No Symbol" : "OK"); + + return SYRStatus; +} + +static enum +stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state, + s32 FFTmode) +{ + + s32 CPAMPvalue = 0, CPAMPStatus, CPAMPMin; + int wd = 0; + + dprintk("%s:\n", __func__); + + switch (FFTmode) { + case 0: /*2k mode*/ + CPAMPMin = 20; + wd = 10; + break; + case 1: /*8k mode*/ + CPAMPMin = 80; + wd = 55; + break; + case 2: /*4k mode*/ + CPAMPMin = 40; + wd = 30; + break; + default: + CPAMPMin = 0xffff; /*drives to NOCPAMP */ + break; + } + + dprintk("%s: CPAMPMin=%d wd=%d\n", __func__, CPAMPMin, wd); + + CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); + while ((CPAMPvalue < CPAMPMin) && (wd > 0)) { + usleep_range(1000, 2000); + wd -= 1; + CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); + /*dprintk("CPAMPvalue= %d at wd=%d\n",CPAMPvalue,wd); */ + } + dprintk("******last CPAMPvalue= %d at wd=%d\n", CPAMPvalue, wd); + if (CPAMPvalue < CPAMPMin) { + CPAMPStatus = FE_TER_NOCPAMP; + dprintk("%s: CPAMP failed\n", __func__); + } else { + dprintk("%s: CPAMP OK !\n", __func__); + CPAMPStatus = FE_TER_CPAMPOK; + } + + return CPAMPStatus; +} + +static enum stv0367_ter_signal_type +stv0367ter_lock_algo(struct stv0367_state *state) +{ + enum stv0367_ter_signal_type ret_flag; + short int wd, tempo; + u8 try, u_var1 = 0, u_var2 = 0, u_var3 = 0, u_var4 = 0, mode, guard; + u8 tmp, tmp2; + + dprintk("%s:\n", __func__); + + if (state == NULL) + return FE_TER_SWNOK; + + try = 0; + do { + ret_flag = FE_TER_LOCKOK; + + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + + if (state->config->if_iq_mode != 0) + stv0367_writebits(state, F367TER_COM_N, 0x07); + + stv0367_writebits(state, F367TER_GUARD, 3);/* suggest 2k 1/4 */ + stv0367_writebits(state, F367TER_MODE, 0); + stv0367_writebits(state, F367TER_SYR_TR_DIS, 0); + usleep_range(5000, 10000); + + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + + + if (stv0367ter_check_syr(state) == FE_TER_NOSYMBOL) + return FE_TER_NOSYMBOL; + else { /* + if chip locked on wrong mode first try, + it must lock correctly second try */ + mode = stv0367_readbits(state, F367TER_SYR_MODE); + if (stv0367ter_check_cpamp(state, mode) == + FE_TER_NOCPAMP) { + if (try == 0) + ret_flag = FE_TER_NOCPAMP; + + } + } + + try++; + } while ((try < 10) && (ret_flag != FE_TER_LOCKOK)); + + tmp = stv0367_readreg(state, R367TER_SYR_STAT); + tmp2 = stv0367_readreg(state, R367TER_STATUS); + dprintk("state=%p\n", state); + dprintk("LOCK OK! mode=%d SYR_STAT=0x%x R367TER_STATUS=0x%x\n", + mode, tmp, tmp2); + + tmp = stv0367_readreg(state, R367TER_PRVIT); + tmp2 = stv0367_readreg(state, R367TER_I2CRPT); + dprintk("PRVIT=0x%x I2CRPT=0x%x\n", tmp, tmp2); + + tmp = stv0367_readreg(state, R367TER_GAIN_SRC1); + dprintk("GAIN_SRC1=0x%x\n", tmp); + + if ((mode != 0) && (mode != 1) && (mode != 2)) + return FE_TER_SWNOK; + + /*guard=stv0367_readbits(state,F367TER_SYR_GUARD); */ + + /*suppress EPQ auto for SYR_GARD 1/16 or 1/32 + and set channel predictor in automatic */ +#if 0 + switch (guard) { + + case 0: + case 1: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); + stv0367_writereg(state, R367TER_CHC_CTL, 0x01); + break; + case 2: + case 3: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); + stv0367_writereg(state, R367TER_CHC_CTL, 0x11); + break; + + default: + return FE_TER_SWNOK; + } +#endif + + /*reset fec an reedsolo FOR 367 only*/ + stv0367_writebits(state, F367TER_RST_SFEC, 1); + stv0367_writebits(state, F367TER_RST_REEDSOLO, 1); + usleep_range(1000, 2000); + stv0367_writebits(state, F367TER_RST_SFEC, 0); + stv0367_writebits(state, F367TER_RST_REEDSOLO, 0); + + u_var1 = stv0367_readbits(state, F367TER_LK); + u_var2 = stv0367_readbits(state, F367TER_PRF); + u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); + /* u_var4=stv0367_readbits(state,F367TER_TSFIFO_LINEOK); */ + + wd = stv0367ter_duration(mode, 125, 500, 250); + tempo = stv0367ter_duration(mode, 4, 16, 8); + + /*while ( ((!u_var1)||(!u_var2)||(!u_var3)||(!u_var4)) && (wd>=0)) */ + while (((!u_var1) || (!u_var2) || (!u_var3)) && (wd >= 0)) { + usleep_range(1000 * tempo, 1000 * (tempo + 1)); + wd -= tempo; + u_var1 = stv0367_readbits(state, F367TER_LK); + u_var2 = stv0367_readbits(state, F367TER_PRF); + u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); + /*u_var4=stv0367_readbits(state, F367TER_TSFIFO_LINEOK); */ + } + + if (!u_var1) + return FE_TER_NOLOCK; + + + if (!u_var2) + return FE_TER_NOPRFOUND; + + if (!u_var3) + return FE_TER_NOTPS; + + guard = stv0367_readbits(state, F367TER_SYR_GUARD); + stv0367_writereg(state, R367TER_CHC_CTL, 0x11); + switch (guard) { + case 0: + case 1: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); + /*stv0367_writereg(state,R367TER_CHC_CTL, 0x1);*/ + stv0367_writebits(state, F367TER_SYR_FILTER, 0); + break; + case 2: + case 3: + stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); + /*stv0367_writereg(state,R367TER_CHC_CTL, 0x11);*/ + stv0367_writebits(state, F367TER_SYR_FILTER, 1); + break; + + default: + return FE_TER_SWNOK; + } + + /* apply Sfec workaround if 8K 64QAM CR!=1/2*/ + if ((stv0367_readbits(state, F367TER_TPS_CONST) == 2) && + (mode == 1) && + (stv0367_readbits(state, F367TER_TPS_HPCODE) != 0)) { + stv0367_writereg(state, R367TER_SFDLYSETH, 0xc0); + stv0367_writereg(state, R367TER_SFDLYSETM, 0x60); + stv0367_writereg(state, R367TER_SFDLYSETL, 0x0); + } else + stv0367_writereg(state, R367TER_SFDLYSETH, 0x0); + + wd = stv0367ter_duration(mode, 125, 500, 250); + u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); + + while ((!u_var4) && (wd >= 0)) { + usleep_range(1000 * tempo, 1000 * (tempo + 1)); + wd -= tempo; + u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); + } + + if (!u_var4) + return FE_TER_NOLOCK; + + /* for 367 leave COM_N at 0x7 for IQ_mode*/ + /*if(ter_state->if_iq_mode!=FE_TER_NORMAL_IF_TUNER) { + tempo=0; + while ((stv0367_readbits(state,F367TER_COM_USEGAINTRK)!=1) && + (stv0367_readbits(state,F367TER_COM_AGCLOCK)!=1)&&(tempo<100)) { + ChipWaitOrAbort(state,1); + tempo+=1; + } + + stv0367_writebits(state,F367TER_COM_N,0x17); + } */ + + stv0367_writebits(state, F367TER_SYR_TR_DIS, 1); + + dprintk("FE_TER_LOCKOK !!!\n"); + + return FE_TER_LOCKOK; + +} + +static void stv0367ter_set_ts_mode(struct stv0367_state *state, + enum stv0367_ts_mode PathTS) +{ + + dprintk("%s:\n", __func__); + + if (state == NULL) + return; + + stv0367_writebits(state, F367TER_TS_DIS, 0); + switch (PathTS) { + default: + /*for removing warning :default we can assume in parallel mode*/ + case STV0367_PARALLEL_PUNCT_CLOCK: + stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 0); + stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 0); + break; + case STV0367_SERIAL_PUNCT_CLOCK: + stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 1); + stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 1); + break; + } +} + +static void stv0367ter_set_clk_pol(struct stv0367_state *state, + enum stv0367_clk_pol clock) +{ + + dprintk("%s:\n", __func__); + + if (state == NULL) + return; + + switch (clock) { + case STV0367_RISINGEDGE_CLOCK: + stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 1); + break; + case STV0367_FALLINGEDGE_CLOCK: + stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); + break; + /*case FE_TER_CLOCK_POLARITY_DEFAULT:*/ + default: + stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); + break; + } +} + +#if 0 +static void stv0367ter_core_sw(struct stv0367_state *state) +{ + + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + msleep(350); +} +#endif +static int stv0367ter_standby(struct dvb_frontend *fe, u8 standby_on) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + if (standby_on) { + stv0367_writebits(state, F367TER_STDBY, 1); + stv0367_writebits(state, F367TER_STDBY_FEC, 1); + stv0367_writebits(state, F367TER_STDBY_CORE, 1); + } else { + stv0367_writebits(state, F367TER_STDBY, 0); + stv0367_writebits(state, F367TER_STDBY_FEC, 0); + stv0367_writebits(state, F367TER_STDBY_CORE, 0); + } + + return 0; +} + +static int stv0367ter_sleep(struct dvb_frontend *fe) +{ + return stv0367ter_standby(fe, 1); +} + +static int stv0367ter_init(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + + dprintk("%s:\n", __func__); + + ter_state->pBER = 0; + + stv0367_write_table(state, + stv0367_deftabs[state->deftabs][STV0367_TAB_TER]); + + stv0367_pll_setup(state, STV0367_ICSPEED_53125, state->config->xtal); + + stv0367_writereg(state, R367TER_I2CRPT, 0xa0); + stv0367_writereg(state, R367TER_ANACTRL, 0x00); + + /*Set TS1 and TS2 to serial or parallel mode */ + stv0367ter_set_ts_mode(state, state->config->ts_mode); + stv0367ter_set_clk_pol(state, state->config->clk_pol); + + state->chip_id = stv0367_readreg(state, R367TER_ID); + ter_state->first_lock = 0; + ter_state->unlock_counter = 2; + + return 0; +} + +static int stv0367ter_algo(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + int offset = 0, tempo = 0; + u8 u_var; + u8 /*constell,*/ counter; + s8 step; + s32 timing_offset = 0; + u32 trl_nomrate = 0, InternalFreq = 0, temp = 0, ifkhz = 0; + + dprintk("%s:\n", __func__); + + stv0367_get_if_khz(state, &ifkhz); + + ter_state->frequency = p->frequency; + ter_state->force = FE_TER_FORCENONE + + stv0367_readbits(state, F367TER_FORCE) * 2; + ter_state->if_iq_mode = state->config->if_iq_mode; + switch (state->config->if_iq_mode) { + case FE_TER_NORMAL_IF_TUNER: /* Normal IF mode */ + dprintk("ALGO: FE_TER_NORMAL_IF_TUNER selected\n"); + stv0367_writebits(state, F367TER_TUNER_BB, 0); + stv0367_writebits(state, F367TER_LONGPATH_IF, 0); + stv0367_writebits(state, F367TER_DEMUX_SWAP, 0); + break; + case FE_TER_LONGPATH_IF_TUNER: /* Long IF mode */ + dprintk("ALGO: FE_TER_LONGPATH_IF_TUNER selected\n"); + stv0367_writebits(state, F367TER_TUNER_BB, 0); + stv0367_writebits(state, F367TER_LONGPATH_IF, 1); + stv0367_writebits(state, F367TER_DEMUX_SWAP, 1); + break; + case FE_TER_IQ_TUNER: /* IQ mode */ + dprintk("ALGO: FE_TER_IQ_TUNER selected\n"); + stv0367_writebits(state, F367TER_TUNER_BB, 1); + stv0367_writebits(state, F367TER_PPM_INVSEL, 0); + break; + default: + printk(KERN_ERR "ALGO: wrong TUNER type selected\n"); + return -EINVAL; + } + + usleep_range(5000, 7000); + + switch (p->inversion) { + case INVERSION_AUTO: + default: + dprintk("%s: inversion AUTO\n", __func__); + if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) + stv0367_writebits(state, F367TER_IQ_INVERT, + ter_state->sense); + else + stv0367_writebits(state, F367TER_INV_SPECTR, + ter_state->sense); + + break; + case INVERSION_ON: + case INVERSION_OFF: + if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) + stv0367_writebits(state, F367TER_IQ_INVERT, + p->inversion); + else + stv0367_writebits(state, F367TER_INV_SPECTR, + p->inversion); + + break; + } + + if ((ter_state->if_iq_mode != FE_TER_NORMAL_IF_TUNER) && + (ter_state->pBW != ter_state->bw)) { + stv0367ter_agc_iir_lock_detect_set(state); + + /*set fine agc target to 180 for LPIF or IQ mode*/ + /* set Q_AGCTarget */ + stv0367_writebits(state, F367TER_SEL_IQNTAR, 1); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); + /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ + + /* set Q_AGCTarget */ + stv0367_writebits(state, F367TER_SEL_IQNTAR, 0); + stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); + /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ + + if (!stv0367_iir_filt_init(state, ter_state->bw, + state->config->xtal)) + return -EINVAL; + /*set IIR filter once for 6,7 or 8MHz BW*/ + ter_state->pBW = ter_state->bw; + + stv0367ter_agc_iir_rst(state); + } + + if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) + stv0367_writebits(state, F367TER_BDI_LPSEL, 0x01); + else + stv0367_writebits(state, F367TER_BDI_LPSEL, 0x00); + + InternalFreq = stv0367ter_get_mclk(state, state->config->xtal) / 1000; + temp = (int) + ((((ter_state->bw * 64 * (1 << 15) * 100) + / (InternalFreq)) * 10) / 7); + + stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, temp % 2); + temp = temp / 2; + stv0367_writebits(state, F367TER_TRL_NOMRATE_HI, temp / 256); + stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, temp % 256); + + temp = stv0367_readbits(state, F367TER_TRL_NOMRATE_HI) * 512 + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB); + temp = (int)(((1 << 17) * ter_state->bw * 1000) / (7 * (InternalFreq))); + stv0367_writebits(state, F367TER_GAIN_SRC_HI, temp / 256); + stv0367_writebits(state, F367TER_GAIN_SRC_LO, temp % 256); + temp = stv0367_readbits(state, F367TER_GAIN_SRC_HI) * 256 + + stv0367_readbits(state, F367TER_GAIN_SRC_LO); + + temp = (int) + ((InternalFreq - ifkhz) * (1 << 16) / (InternalFreq)); + + dprintk("DEROT temp=0x%x\n", temp); + stv0367_writebits(state, F367TER_INC_DEROT_HI, temp / 256); + stv0367_writebits(state, F367TER_INC_DEROT_LO, temp % 256); + + ter_state->echo_pos = 0; + ter_state->ucblocks = 0; /* liplianin */ + ter_state->pBER = 0; /* liplianin */ + stv0367_writebits(state, F367TER_LONG_ECHO, ter_state->echo_pos); + + if (stv0367ter_lock_algo(state) != FE_TER_LOCKOK) + return 0; + + ter_state->state = FE_TER_LOCKOK; + + ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE); + ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD); + + ter_state->first_lock = 1; /* we know sense now :) */ + + ter_state->agc_val = + (stv0367_readbits(state, F367TER_AGC1_VAL_LO) << 16) + + (stv0367_readbits(state, F367TER_AGC1_VAL_HI) << 24) + + stv0367_readbits(state, F367TER_AGC2_VAL_LO) + + (stv0367_readbits(state, F367TER_AGC2_VAL_HI) << 8); + + /* Carrier offset calculation */ + stv0367_writebits(state, F367TER_FREEZE, 1); + offset = (stv0367_readbits(state, F367TER_CRL_FOFFSET_VHI) << 16) ; + offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_HI) << 8); + offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_LO)); + stv0367_writebits(state, F367TER_FREEZE, 0); + if (offset > 8388607) + offset -= 16777216; + + offset = offset * 2 / 16384; + + if (ter_state->mode == FE_TER_MODE_2K) + offset = (offset * 4464) / 1000;/*** 1 FFT BIN=4.464khz***/ + else if (ter_state->mode == FE_TER_MODE_4K) + offset = (offset * 223) / 100;/*** 1 FFT BIN=2.23khz***/ + else if (ter_state->mode == FE_TER_MODE_8K) + offset = (offset * 111) / 100;/*** 1 FFT BIN=1.1khz***/ + + if (stv0367_readbits(state, F367TER_PPM_INVSEL) == 1) { + if ((stv0367_readbits(state, F367TER_INV_SPECTR) == + (stv0367_readbits(state, + F367TER_STATUS_INV_SPECRUM) == 1))) + offset = offset * -1; + } + + if (ter_state->bw == 6) + offset = (offset * 6) / 8; + else if (ter_state->bw == 7) + offset = (offset * 7) / 8; + + ter_state->frequency += offset; + + tempo = 10; /* exit even if timing_offset stays null */ + while ((timing_offset == 0) && (tempo > 0)) { + usleep_range(10000, 20000); /*was 20ms */ + /* fine tuning of timing offset if required */ + timing_offset = stv0367_readbits(state, F367TER_TRL_TOFFSET_LO) + + 256 * stv0367_readbits(state, + F367TER_TRL_TOFFSET_HI); + if (timing_offset >= 32768) + timing_offset -= 65536; + trl_nomrate = (512 * stv0367_readbits(state, + F367TER_TRL_NOMRATE_HI) + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 + + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB)); + + timing_offset = ((signed)(1000000 / trl_nomrate) * + timing_offset) / 2048; + tempo--; + } + + if (timing_offset <= 0) { + timing_offset = (timing_offset - 11) / 22; + step = -1; + } else { + timing_offset = (timing_offset + 11) / 22; + step = 1; + } + + for (counter = 0; counter < abs(timing_offset); counter++) { + trl_nomrate += step; + stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, + trl_nomrate % 2); + stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, + trl_nomrate / 2); + usleep_range(1000, 2000); + } + + usleep_range(5000, 6000); + /* unlocks could happen in case of trl centring big step, + then a core off/on restarts demod */ + u_var = stv0367_readbits(state, F367TER_LK); + + if (!u_var) { + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + msleep(20); + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + } + + return 0; +} + +static int stv0367ter_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + + /*u8 trials[2]; */ + s8 num_trials, index; + u8 SenseTrials[] = { INVERSION_ON, INVERSION_OFF }; + + if (state->reinit_on_setfrontend) + stv0367ter_init(fe); + + if (fe->ops.tuner_ops.set_params) { + if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + switch (p->transmission_mode) { + default: + case TRANSMISSION_MODE_AUTO: + case TRANSMISSION_MODE_2K: + ter_state->mode = FE_TER_MODE_2K; + break; +/* case TRANSMISSION_MODE_4K: + pLook.mode = FE_TER_MODE_4K; + break;*/ + case TRANSMISSION_MODE_8K: + ter_state->mode = FE_TER_MODE_8K; + break; + } + + switch (p->guard_interval) { + default: + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_1_16: + case GUARD_INTERVAL_1_8: + case GUARD_INTERVAL_1_4: + ter_state->guard = p->guard_interval; + break; + case GUARD_INTERVAL_AUTO: + ter_state->guard = GUARD_INTERVAL_1_32; + break; + } + + switch (p->bandwidth_hz) { + case 6000000: + ter_state->bw = FE_TER_CHAN_BW_6M; + break; + case 7000000: + ter_state->bw = FE_TER_CHAN_BW_7M; + break; + case 8000000: + default: + ter_state->bw = FE_TER_CHAN_BW_8M; + } + + ter_state->hierarchy = FE_TER_HIER_NONE; + + switch (p->inversion) { + case INVERSION_OFF: + case INVERSION_ON: + num_trials = 1; + break; + default: + num_trials = 2; + if (ter_state->first_lock) + num_trials = 1; + break; + } + + ter_state->state = FE_TER_NOLOCK; + index = 0; + + while (((index) < num_trials) && (ter_state->state != FE_TER_LOCKOK)) { + if (!ter_state->first_lock) { + if (p->inversion == INVERSION_AUTO) + ter_state->sense = SenseTrials[index]; + + } + stv0367ter_algo(fe); + + if ((ter_state->state == FE_TER_LOCKOK) && + (p->inversion == INVERSION_AUTO) && + (index == 1)) { + /* invert spectrum sense */ + SenseTrials[index] = SenseTrials[0]; + SenseTrials[(index + 1) % 2] = (SenseTrials[1] + 1) % 2; + } + + index++; + } + + return 0; +} + +static int stv0367ter_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + u32 errs = 0; + + /*wait for counting completion*/ + if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) { + errs = + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) + * (1 << 16)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) + * (1 << 8)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); + ter_state->ucblocks = errs; + } + + (*ucblocks) = ter_state->ucblocks; + + return 0; +} + +static int stv0367ter_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + enum stv0367_ter_mode mode; + int constell = 0,/* snr = 0,*/ Data = 0; + + p->frequency = stv0367_get_tuner_freq(fe); + if ((int)p->frequency < 0) + p->frequency = -p->frequency; + + constell = stv0367_readbits(state, F367TER_TPS_CONST); + if (constell == 0) + p->modulation = QPSK; + else if (constell == 1) + p->modulation = QAM_16; + else + p->modulation = QAM_64; + + p->inversion = stv0367_readbits(state, F367TER_INV_SPECTR); + + /* Get the Hierarchical mode */ + Data = stv0367_readbits(state, F367TER_TPS_HIERMODE); + + switch (Data) { + case 0: + p->hierarchy = HIERARCHY_NONE; + break; + case 1: + p->hierarchy = HIERARCHY_1; + break; + case 2: + p->hierarchy = HIERARCHY_2; + break; + case 3: + p->hierarchy = HIERARCHY_4; + break; + default: + p->hierarchy = HIERARCHY_AUTO; + break; /* error */ + } + + /* Get the FEC Rate */ + if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) + Data = stv0367_readbits(state, F367TER_TPS_LPCODE); + else + Data = stv0367_readbits(state, F367TER_TPS_HPCODE); + + switch (Data) { + case 0: + p->code_rate_HP = FEC_1_2; + break; + case 1: + p->code_rate_HP = FEC_2_3; + break; + case 2: + p->code_rate_HP = FEC_3_4; + break; + case 3: + p->code_rate_HP = FEC_5_6; + break; + case 4: + p->code_rate_HP = FEC_7_8; + break; + default: + p->code_rate_HP = FEC_AUTO; + break; /* error */ + } + + mode = stv0367_readbits(state, F367TER_SYR_MODE); + + switch (mode) { + case FE_TER_MODE_2K: + p->transmission_mode = TRANSMISSION_MODE_2K; + break; +/* case FE_TER_MODE_4K: + p->transmission_mode = TRANSMISSION_MODE_4K; + break;*/ + case FE_TER_MODE_8K: + p->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + p->transmission_mode = TRANSMISSION_MODE_AUTO; + } + + p->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD); + + return 0; +} + +static u32 stv0367ter_snr_readreg(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 snru32 = 0; + int cpt = 0; + u8 cut = stv0367_readbits(state, F367TER_IDENTIFICATIONREG); + + while (cpt < 10) { + usleep_range(2000, 3000); + if (cut == 0x50) /*cut 1.0 cut 1.1*/ + snru32 += stv0367_readbits(state, F367TER_CHCSNR) / 4; + else /*cu2.0*/ + snru32 += 125 * stv0367_readbits(state, F367TER_CHCSNR); + + cpt++; + } + snru32 /= 10;/*average on 10 values*/ + + return snru32; +} + +static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + u32 snrval = stv0367ter_snr_readreg(fe); + + *snr = snrval / 1000; + + return 0; +} + +#if 0 +static int stv0367ter_status(struct dvb_frontend *fe) +{ + + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + int locked = FALSE; + + locked = (stv0367_readbits(state, F367TER_LK)); + if (!locked) + ter_state->unlock_counter += 1; + else + ter_state->unlock_counter = 0; + + if (ter_state->unlock_counter > 2) { + if (!stv0367_readbits(state, F367TER_TPS_LOCK) || + (!stv0367_readbits(state, F367TER_LK))) { + stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); + usleep_range(2000, 3000); + stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); + msleep(350); + locked = (stv0367_readbits(state, F367TER_TPS_LOCK)) && + (stv0367_readbits(state, F367TER_LK)); + } + + } + + return locked; +} +#endif +static int stv0367ter_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + *status = 0; + + if (stv0367_readbits(state, F367TER_LK)) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI + | FE_HAS_SYNC | FE_HAS_LOCK; + dprintk("%s: stv0367 has locked\n", __func__); + } + + return 0; +} + +static int stv0367ter_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367ter_state *ter_state = state->ter_state; + u32 Errors = 0, tber = 0, temporary = 0; + int abc = 0, def = 0; + + + /*wait for counting completion*/ + if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) + Errors = ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT) + * (1 << 16)) + + ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT_HI) + * (1 << 8)) + + ((u32)stv0367_readbits(state, + F367TER_SFEC_ERR_CNT_LO)); + /*measurement not completed, load previous value*/ + else { + tber = ter_state->pBER; + return 0; + } + + abc = stv0367_readbits(state, F367TER_SFEC_ERR_SOURCE); + def = stv0367_readbits(state, F367TER_SFEC_NUM_EVENT); + + if (Errors == 0) { + tber = 0; + } else if (abc == 0x7) { + if (Errors <= 4) { + temporary = (Errors * 1000000000) / (8 * (1 << 14)); + } else if (Errors <= 42) { + temporary = (Errors * 100000000) / (8 * (1 << 14)); + temporary = temporary * 10; + } else if (Errors <= 429) { + temporary = (Errors * 10000000) / (8 * (1 << 14)); + temporary = temporary * 100; + } else if (Errors <= 4294) { + temporary = (Errors * 1000000) / (8 * (1 << 14)); + temporary = temporary * 1000; + } else if (Errors <= 42949) { + temporary = (Errors * 100000) / (8 * (1 << 14)); + temporary = temporary * 10000; + } else if (Errors <= 429496) { + temporary = (Errors * 10000) / (8 * (1 << 14)); + temporary = temporary * 100000; + } else { /*if (Errors<4294967) 2^22 max error*/ + temporary = (Errors * 1000) / (8 * (1 << 14)); + temporary = temporary * 100000; /* still to *10 */ + } + + /* Byte error*/ + if (def == 2) + /*tber=Errors/(8*(1 <<14));*/ + tber = temporary; + else if (def == 3) + /*tber=Errors/(8*(1 <<16));*/ + tber = temporary / 4; + else if (def == 4) + /*tber=Errors/(8*(1 <<18));*/ + tber = temporary / 16; + else if (def == 5) + /*tber=Errors/(8*(1 <<20));*/ + tber = temporary / 64; + else if (def == 6) + /*tber=Errors/(8*(1 <<22));*/ + tber = temporary / 256; + else + /* should not pass here*/ + tber = 0; + + if ((Errors < 4294967) && (Errors > 429496)) + tber *= 10; + + } + + /* save actual value */ + ter_state->pBER = tber; + + (*ber) = tber; + + return 0; +} +#if 0 +static u32 stv0367ter_get_per(struct stv0367_state *state) +{ + struct stv0367ter_state *ter_state = state->ter_state; + u32 Errors = 0, Per = 0, temporary = 0; + int abc = 0, def = 0, cpt = 0; + + while (((stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 1) && + (cpt < 400)) || ((Errors == 0) && (cpt < 400))) { + usleep_range(1000, 2000); + Errors = ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) + * (1 << 16)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) + * (1 << 8)) + + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); + cpt++; + } + abc = stv0367_readbits(state, F367TER_ERR_SRC1); + def = stv0367_readbits(state, F367TER_NUM_EVT1); + + if (Errors == 0) + Per = 0; + else if (abc == 0x9) { + if (Errors <= 4) { + temporary = (Errors * 1000000000) / (8 * (1 << 8)); + } else if (Errors <= 42) { + temporary = (Errors * 100000000) / (8 * (1 << 8)); + temporary = temporary * 10; + } else if (Errors <= 429) { + temporary = (Errors * 10000000) / (8 * (1 << 8)); + temporary = temporary * 100; + } else if (Errors <= 4294) { + temporary = (Errors * 1000000) / (8 * (1 << 8)); + temporary = temporary * 1000; + } else if (Errors <= 42949) { + temporary = (Errors * 100000) / (8 * (1 << 8)); + temporary = temporary * 10000; + } else { /*if(Errors<=429496) 2^16 errors max*/ + temporary = (Errors * 10000) / (8 * (1 << 8)); + temporary = temporary * 100000; + } + + /* pkt error*/ + if (def == 2) + /*Per=Errors/(1 << 8);*/ + Per = temporary; + else if (def == 3) + /*Per=Errors/(1 << 10);*/ + Per = temporary / 4; + else if (def == 4) + /*Per=Errors/(1 << 12);*/ + Per = temporary / 16; + else if (def == 5) + /*Per=Errors/(1 << 14);*/ + Per = temporary / 64; + else if (def == 6) + /*Per=Errors/(1 << 16);*/ + Per = temporary / 256; + else + Per = 0; + + } + /* save actual value */ + ter_state->pPER = Per; + + return Per; +} +#endif +static int stv0367_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 1000; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + + return 0; +} + +static void stv0367_release(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + + kfree(state->ter_state); + kfree(state->cab_state); + kfree(state); +} + +static const struct dvb_frontend_ops stv0367ter_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "ST STV0367 DVB-T", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 15625, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | + FE_CAN_INVERSION_AUTO | + FE_CAN_MUTE_TS + }, + .release = stv0367_release, + .init = stv0367ter_init, + .sleep = stv0367ter_sleep, + .i2c_gate_ctrl = stv0367ter_gate_ctrl, + .set_frontend = stv0367ter_set_frontend, + .get_frontend = stv0367ter_get_frontend, + .get_tune_settings = stv0367_get_tune_settings, + .read_status = stv0367ter_read_status, + .read_ber = stv0367ter_read_ber,/* too slow */ +/* .read_signal_strength = stv0367_read_signal_strength,*/ + .read_snr = stv0367ter_read_snr, + .read_ucblocks = stv0367ter_read_ucblocks, +}; + +struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + struct stv0367_state *state = NULL; + struct stv0367ter_state *ter_state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); + if (state == NULL) + goto error; + ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL); + if (ter_state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->config = config; + state->ter_state = ter_state; + state->fe.ops = stv0367ter_ops; + state->fe.demodulator_priv = state; + state->chip_id = stv0367_readreg(state, 0xf000); + + /* demod operation options */ + state->use_i2c_gatectrl = 1; + state->deftabs = STV0367_DEFTAB_GENERIC; + state->reinit_on_setfrontend = 1; + state->auto_if_khz = 0; + + dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); + + /* check if the demod is there */ + if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) + goto error; + + return &state->fe; + +error: + kfree(ter_state); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stv0367ter_attach); + +static int stv0367cab_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + stv0367_writebits(state, F367CAB_I2CT_ON, (enable > 0) ? 1 : 0); + + return 0; +} + +static u32 stv0367cab_get_mclk(struct dvb_frontend *fe, u32 ExtClk_Hz) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 mclk_Hz = 0;/* master clock frequency (Hz) */ + u32 M, N, P; + + + if (stv0367_readbits(state, F367CAB_BYPASS_PLLXN) == 0) { + N = (u32)stv0367_readbits(state, F367CAB_PLL_NDIV); + if (N == 0) + N = N + 1; + + M = (u32)stv0367_readbits(state, F367CAB_PLL_MDIV); + if (M == 0) + M = M + 1; + + P = (u32)stv0367_readbits(state, F367CAB_PLL_PDIV); + + if (P > 5) + P = 5; + + mclk_Hz = ((ExtClk_Hz / 2) * N) / (M * (1 << P)); + dprintk("stv0367cab_get_mclk BYPASS_PLLXN mclk_Hz=%d\n", + mclk_Hz); + } else + mclk_Hz = ExtClk_Hz; + + dprintk("stv0367cab_get_mclk final mclk_Hz=%d\n", mclk_Hz); + + return mclk_Hz; +} + +static u32 stv0367cab_get_adc_freq(struct dvb_frontend *fe, u32 ExtClk_Hz) +{ + return stv0367cab_get_mclk(fe, ExtClk_Hz); +} + +static enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state, + u32 SymbolRate, + enum stv0367cab_mod QAMSize) +{ + /* Set QAM size */ + stv0367_writebits(state, F367CAB_QAM_MODE, QAMSize); + + /* Set Registers settings specific to the QAM size */ + switch (QAMSize) { + case FE_CAB_MOD_QAM4: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + break; + case FE_CAB_MOD_QAM16: + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x64); + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x8a); + break; + case FE_CAB_MOD_QAM32: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x6e); + stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xb7); + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x9d); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); + break; + case FE_CAB_MOD_QAM64: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x82); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); + if (SymbolRate > 4500000) { + stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa5); + } else if (SymbolRate > 2500000) { + stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); + } else { + stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + } + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x99); + break; + case FE_CAB_MOD_QAM128: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x76); + stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xb1); + if (SymbolRate > 4500000) + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + else if (SymbolRate > 2500000) + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); + else + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0x97); + + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x8e); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); + break; + case FE_CAB_MOD_QAM256: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x94); + stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); + stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); + if (SymbolRate > 4500000) + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + else if (SymbolRate > 2500000) + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); + else + stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); + + stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); + stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x85); + stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); + stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); + break; + case FE_CAB_MOD_QAM512: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + break; + case FE_CAB_MOD_QAM1024: + stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); + break; + default: + break; + } + + return QAMSize; +} + +static u32 stv0367cab_set_derot_freq(struct stv0367_state *state, + u32 adc_hz, s32 derot_hz) +{ + u32 sampled_if = 0; + u32 adc_khz; + + adc_khz = adc_hz / 1000; + + dprintk("%s: adc_hz=%d derot_hz=%d\n", __func__, adc_hz, derot_hz); + + if (adc_khz != 0) { + if (derot_hz < 1000000) + derot_hz = adc_hz / 4; /* ZIF operation */ + if (derot_hz > adc_hz) + derot_hz = derot_hz - adc_hz; + sampled_if = (u32)derot_hz / 1000; + sampled_if *= 32768; + sampled_if /= adc_khz; + sampled_if *= 256; + } + + if (sampled_if > 8388607) + sampled_if = 8388607; + + dprintk("%s: sampled_if=0x%x\n", __func__, sampled_if); + + stv0367_writereg(state, R367CAB_MIX_NCO_LL, sampled_if); + stv0367_writereg(state, R367CAB_MIX_NCO_HL, (sampled_if >> 8)); + stv0367_writebits(state, F367CAB_MIX_NCO_INC_HH, (sampled_if >> 16)); + + return derot_hz; +} + +static u32 stv0367cab_get_derot_freq(struct stv0367_state *state, u32 adc_hz) +{ + u32 sampled_if; + + sampled_if = stv0367_readbits(state, F367CAB_MIX_NCO_INC_LL) + + (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HL) << 8) + + (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HH) << 16); + + sampled_if /= 256; + sampled_if *= (adc_hz / 1000); + sampled_if += 1; + sampled_if /= 32768; + + return sampled_if; +} + +static u32 stv0367cab_set_srate(struct stv0367_state *state, u32 adc_hz, + u32 mclk_hz, u32 SymbolRate, + enum stv0367cab_mod QAMSize) +{ + u32 QamSizeCorr = 0; + u32 u32_tmp = 0, u32_tmp1 = 0; + u32 adp_khz; + + dprintk("%s:\n", __func__); + + /* Set Correction factor of SRC gain */ + switch (QAMSize) { + case FE_CAB_MOD_QAM4: + QamSizeCorr = 1110; + break; + case FE_CAB_MOD_QAM16: + QamSizeCorr = 1032; + break; + case FE_CAB_MOD_QAM32: + QamSizeCorr = 954; + break; + case FE_CAB_MOD_QAM64: + QamSizeCorr = 983; + break; + case FE_CAB_MOD_QAM128: + QamSizeCorr = 957; + break; + case FE_CAB_MOD_QAM256: + QamSizeCorr = 948; + break; + case FE_CAB_MOD_QAM512: + QamSizeCorr = 0; + break; + case FE_CAB_MOD_QAM1024: + QamSizeCorr = 944; + break; + default: + break; + } + + /* Transfer ratio calculation */ + if (adc_hz != 0) { + u32_tmp = 256 * SymbolRate; + u32_tmp = u32_tmp / adc_hz; + } + stv0367_writereg(state, R367CAB_EQU_CRL_TFR, (u8)u32_tmp); + + /* Symbol rate and SRC gain calculation */ + adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ + if (adp_khz != 0) { + u32_tmp = SymbolRate; + u32_tmp1 = SymbolRate; + + if (u32_tmp < 2097152) { /* 2097152 = 2^21 */ + /* Symbol rate calculation */ + u32_tmp *= 2048; /* 2048 = 2^11 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2^14 */ + u32_tmp /= 125 ; /* 125 = 1000/2^3 */ + u32_tmp = u32_tmp * 8; /* 8 = 2^3 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 2048; /* *2*2^10 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 10000000; + + } else if (u32_tmp < 4194304) { /* 4194304 = 2**22 */ + /* Symbol rate calculation */ + u32_tmp *= 1024 ; /* 1024 = 2**10 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ + u32_tmp /= 125 ; /* 125 = 1000/2**3 */ + u32_tmp = u32_tmp * 16; /* 16 = 2**4 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 1024; /* *2*2^9 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz)*/ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 5000000; + } else if (u32_tmp < 8388607) { /* 8388607 = 2**23 */ + /* Symbol rate calculation */ + u32_tmp *= 512 ; /* 512 = 2**9 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ + u32_tmp /= 125 ; /* 125 = 1000/2**3 */ + u32_tmp = u32_tmp * 32; /* 32 = 2**5 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 512; /* *2*2^8 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 2500000; + } else { + /* Symbol rate calculation */ + u32_tmp *= 256 ; /* 256 = 2**8 */ + u32_tmp = u32_tmp / adp_khz; + u32_tmp = u32_tmp * 16384; /* 16384 = 2**13 */ + u32_tmp /= 125 ; /* 125 = 1000/2**3 */ + u32_tmp = u32_tmp * 64; /* 64 = 2**6 */ + + /* SRC Gain Calculation */ + u32_tmp1 *= 256; /* 2*2^7 */ + u32_tmp1 /= 439; /* *2/878 */ + u32_tmp1 *= 256; /* *2^8 */ + u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ + u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ + u32_tmp1 = u32_tmp1 / 1250000; + } + } +#if 0 + /* Filters' coefficients are calculated and written + into registers only if the filters are enabled */ + if (stv0367_readbits(state, F367CAB_ADJ_EN)) { + stv0367cab_SetIirAdjacentcoefficient(state, mclk_hz, + SymbolRate); + /* AllPass filter must be enabled + when the adjacents filter is used */ + stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 1); + stv0367cab_SetAllPasscoefficient(state, mclk_hz, SymbolRate); + } else + /* AllPass filter must be disabled + when the adjacents filter is not used */ +#endif + stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); + + stv0367_writereg(state, R367CAB_SRC_NCO_LL, u32_tmp); + stv0367_writereg(state, R367CAB_SRC_NCO_LH, (u32_tmp >> 8)); + stv0367_writereg(state, R367CAB_SRC_NCO_HL, (u32_tmp >> 16)); + stv0367_writereg(state, R367CAB_SRC_NCO_HH, (u32_tmp >> 24)); + + stv0367_writereg(state, R367CAB_IQDEM_GAIN_SRC_L, u32_tmp1 & 0x00ff); + stv0367_writebits(state, F367CAB_GAIN_SRC_HI, (u32_tmp1 >> 8) & 0x00ff); + + return SymbolRate ; +} + +static u32 stv0367cab_GetSymbolRate(struct stv0367_state *state, u32 mclk_hz) +{ + u32 regsym; + u32 adp_khz; + + regsym = stv0367_readreg(state, R367CAB_SRC_NCO_LL) + + (stv0367_readreg(state, R367CAB_SRC_NCO_LH) << 8) + + (stv0367_readreg(state, R367CAB_SRC_NCO_HL) << 16) + + (stv0367_readreg(state, R367CAB_SRC_NCO_HH) << 24); + + adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ + + if (regsym < 134217728) { /* 134217728L = 2**27*/ + regsym = regsym * 32; /* 32 = 2**5 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3 */ + regsym /= 2048 ; /* 2048 = 2**11 */ + } else if (regsym < 268435456) { /* 268435456L = 2**28 */ + regsym = regsym * 16; /* 16 = 2**4 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3*/ + regsym /= 1024 ; /* 256 = 2**10*/ + } else if (regsym < 536870912) { /* 536870912L = 2**29*/ + regsym = regsym * 8; /* 8 = 2**3 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3 */ + regsym /= 512 ; /* 128 = 2**9 */ + } else { + regsym = regsym * 4; /* 4 = 2**2 */ + regsym = regsym / 32768; /* 32768L = 2**15 */ + regsym = adp_khz * regsym; /* AdpClk in kHz */ + regsym = regsym / 128; /* 128 = 2**7 */ + regsym *= 125 ; /* 125 = 1000/2**3 */ + regsym /= 256 ; /* 64 = 2**8 */ + } + + return regsym; +} + +static u32 stv0367cab_fsm_status(struct stv0367_state *state) +{ + return stv0367_readbits(state, F367CAB_FSM_STATUS); +} + +static u32 stv0367cab_qamfec_lock(struct stv0367_state *state) +{ + return stv0367_readbits(state, + (state->cab_state->qamfec_status_reg ? + state->cab_state->qamfec_status_reg : + F367CAB_QAMFEC_LOCK)); +} + +static +enum stv0367_cab_signal_type stv0367cab_fsm_signaltype(u32 qam_fsm_status) +{ + enum stv0367_cab_signal_type signaltype = FE_CAB_NOAGC; + + switch (qam_fsm_status) { + case 1: + signaltype = FE_CAB_NOAGC; + break; + case 2: + signaltype = FE_CAB_NOTIMING; + break; + case 3: + signaltype = FE_CAB_TIMINGOK; + break; + case 4: + signaltype = FE_CAB_NOCARRIER; + break; + case 5: + signaltype = FE_CAB_CARRIEROK; + break; + case 7: + signaltype = FE_CAB_NOBLIND; + break; + case 8: + signaltype = FE_CAB_BLINDOK; + break; + case 10: + signaltype = FE_CAB_NODEMOD; + break; + case 11: + signaltype = FE_CAB_DEMODOK; + break; + case 12: + signaltype = FE_CAB_DEMODOK; + break; + case 13: + signaltype = FE_CAB_NODEMOD; + break; + case 14: + signaltype = FE_CAB_NOBLIND; + break; + case 15: + signaltype = FE_CAB_NOSIGNAL; + break; + default: + break; + } + + return signaltype; +} + +static int stv0367cab_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + *status = 0; + + /* update cab_state->state from QAM_FSM_STATUS */ + state->cab_state->state = stv0367cab_fsm_signaltype( + stv0367cab_fsm_status(state)); + + if (stv0367cab_qamfec_lock(state)) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI + | FE_HAS_SYNC | FE_HAS_LOCK; + dprintk("%s: stv0367 has locked\n", __func__); + } else { + if (state->cab_state->state > FE_CAB_NOSIGNAL) + *status |= FE_HAS_SIGNAL; + + if (state->cab_state->state > FE_CAB_NOCARRIER) + *status |= FE_HAS_CARRIER; + + if (state->cab_state->state >= FE_CAB_DEMODOK) + *status |= FE_HAS_VITERBI; + + if (state->cab_state->state >= FE_CAB_DATAOK) + *status |= FE_HAS_SYNC; + } + + return 0; +} + +static int stv0367cab_standby(struct dvb_frontend *fe, u8 standby_on) +{ + struct stv0367_state *state = fe->demodulator_priv; + + dprintk("%s:\n", __func__); + + if (standby_on) { + stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x03); + stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x01); + stv0367_writebits(state, F367CAB_STDBY, 1); + stv0367_writebits(state, F367CAB_STDBY_CORE, 1); + stv0367_writebits(state, F367CAB_EN_BUFFER_I, 0); + stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 0); + stv0367_writebits(state, F367CAB_POFFQ, 1); + stv0367_writebits(state, F367CAB_POFFI, 1); + } else { + stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x00); + stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x00); + stv0367_writebits(state, F367CAB_STDBY, 0); + stv0367_writebits(state, F367CAB_STDBY_CORE, 0); + stv0367_writebits(state, F367CAB_EN_BUFFER_I, 1); + stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 1); + stv0367_writebits(state, F367CAB_POFFQ, 0); + stv0367_writebits(state, F367CAB_POFFI, 0); + } + + return 0; +} + +static int stv0367cab_sleep(struct dvb_frontend *fe) +{ + return stv0367cab_standby(fe, 1); +} + +static int stv0367cab_init(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367cab_state *cab_state = state->cab_state; + + dprintk("%s:\n", __func__); + + stv0367_write_table(state, + stv0367_deftabs[state->deftabs][STV0367_TAB_CAB]); + + switch (state->config->ts_mode) { + case STV0367_DVBCI_CLOCK: + dprintk("Setting TSMode = STV0367_DVBCI_CLOCK\n"); + stv0367_writebits(state, F367CAB_OUTFORMAT, 0x03); + break; + case STV0367_SERIAL_PUNCT_CLOCK: + case STV0367_SERIAL_CONT_CLOCK: + stv0367_writebits(state, F367CAB_OUTFORMAT, 0x01); + break; + case STV0367_PARALLEL_PUNCT_CLOCK: + case STV0367_OUTPUTMODE_DEFAULT: + stv0367_writebits(state, F367CAB_OUTFORMAT, 0x00); + break; + } + + switch (state->config->clk_pol) { + case STV0367_RISINGEDGE_CLOCK: + stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x00); + break; + case STV0367_FALLINGEDGE_CLOCK: + case STV0367_CLOCKPOLARITY_DEFAULT: + stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x01); + break; + } + + stv0367_writebits(state, F367CAB_SYNC_STRIP, 0x00); + + stv0367_writebits(state, F367CAB_CT_NBST, 0x01); + + stv0367_writebits(state, F367CAB_TS_SWAP, 0x01); + + stv0367_writebits(state, F367CAB_FIFO_BYPASS, 0x00); + + stv0367_writereg(state, R367CAB_ANACTRL, 0x00);/*PLL enabled and used */ + + cab_state->mclk = stv0367cab_get_mclk(fe, state->config->xtal); + cab_state->adc_clk = stv0367cab_get_adc_freq(fe, state->config->xtal); + + return 0; +} +static +enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state, + struct dtv_frontend_properties *p) +{ + struct stv0367cab_state *cab_state = state->cab_state; + enum stv0367_cab_signal_type signalType = FE_CAB_NOAGC; + u32 QAMFEC_Lock, QAM_Lock, u32_tmp, ifkhz, + LockTime, TRLTimeOut, AGCTimeOut, CRLSymbols, + CRLTimeOut, EQLTimeOut, DemodTimeOut, FECTimeOut; + u8 TrackAGCAccum; + s32 tmp; + + dprintk("%s:\n", __func__); + + stv0367_get_if_khz(state, &ifkhz); + + /* Timeouts calculation */ + /* A max lock time of 25 ms is allowed for delayed AGC */ + AGCTimeOut = 25; + /* 100000 symbols needed by the TRL as a maximum value */ + TRLTimeOut = 100000000 / p->symbol_rate; + /* CRLSymbols is the needed number of symbols to achieve a lock + within [-4%, +4%] of the symbol rate. + CRL timeout is calculated + for a lock within [-search_range, +search_range]. + EQL timeout can be changed depending on + the micro-reflections we want to handle. + A characterization must be performed + with these echoes to get new timeout values. + */ + switch (p->modulation) { + case QAM_16: + CRLSymbols = 150000; + EQLTimeOut = 100; + break; + case QAM_32: + CRLSymbols = 250000; + EQLTimeOut = 100; + break; + case QAM_64: + CRLSymbols = 200000; + EQLTimeOut = 100; + break; + case QAM_128: + CRLSymbols = 250000; + EQLTimeOut = 100; + break; + case QAM_256: + CRLSymbols = 250000; + EQLTimeOut = 100; + break; + default: + CRLSymbols = 200000; + EQLTimeOut = 100; + break; + } +#if 0 + if (pIntParams->search_range < 0) { + CRLTimeOut = (25 * CRLSymbols * + (-pIntParams->search_range / 1000)) / + (pIntParams->symbol_rate / 1000); + } else +#endif + CRLTimeOut = (25 * CRLSymbols * (cab_state->search_range / 1000)) / + (p->symbol_rate / 1000); + + CRLTimeOut = (1000 * CRLTimeOut) / p->symbol_rate; + /* Timeouts below 50ms are coerced */ + if (CRLTimeOut < 50) + CRLTimeOut = 50; + /* A maximum of 100 TS packets is needed to get FEC lock even in case + the spectrum inversion needs to be changed. + This is equal to 20 ms in case of the lowest symbol rate of 0.87Msps + */ + FECTimeOut = 20; + DemodTimeOut = AGCTimeOut + TRLTimeOut + CRLTimeOut + EQLTimeOut; + + dprintk("%s: DemodTimeOut=%d\n", __func__, DemodTimeOut); + + /* Reset the TRL to ensure nothing starts until the + AGC is stable which ensures a better lock time + */ + stv0367_writereg(state, R367CAB_CTRL_1, 0x04); + /* Set AGC accumulation time to minimum and lock threshold to maximum + in order to speed up the AGC lock */ + TrackAGCAccum = stv0367_readbits(state, F367CAB_AGC_ACCUMRSTSEL); + stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, 0x0); + /* Modulus Mapper is disabled */ + stv0367_writebits(state, F367CAB_MODULUSMAP_EN, 0); + /* Disable the sweep function */ + stv0367_writebits(state, F367CAB_SWEEP_EN, 0); + /* The sweep function is never used, Sweep rate must be set to 0 */ + /* Set the derotator frequency in Hz */ + stv0367cab_set_derot_freq(state, cab_state->adc_clk, + (1000 * (s32)ifkhz + cab_state->derot_offset)); + /* Disable the Allpass Filter when the symbol rate is out of range */ + if ((p->symbol_rate > 10800000) | (p->symbol_rate < 1800000)) { + stv0367_writebits(state, F367CAB_ADJ_EN, 0); + stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); + } +#if 0 + /* Check if the tuner is locked */ + tuner_lock = stv0367cab_tuner_get_status(fe); + if (tuner_lock == 0) + return FE_367CAB_NOTUNER; +#endif + /* Release the TRL to start demodulator acquisition */ + /* Wait for QAM lock */ + LockTime = 0; + stv0367_writereg(state, R367CAB_CTRL_1, 0x00); + do { + QAM_Lock = stv0367cab_fsm_status(state); + if ((LockTime >= (DemodTimeOut - EQLTimeOut)) && + (QAM_Lock == 0x04)) + /* + * We don't wait longer, the frequency/phase offset + * must be too big + */ + LockTime = DemodTimeOut; + else if ((LockTime >= (AGCTimeOut + TRLTimeOut)) && + (QAM_Lock == 0x02)) + /* + * We don't wait longer, either there is no signal or + * it is not the right symbol rate or it is an analog + * carrier + */ + { + LockTime = DemodTimeOut; + u32_tmp = stv0367_readbits(state, + F367CAB_AGC_PWR_WORD_LO) + + (stv0367_readbits(state, + F367CAB_AGC_PWR_WORD_ME) << 8) + + (stv0367_readbits(state, + F367CAB_AGC_PWR_WORD_HI) << 16); + if (u32_tmp >= 131072) + u32_tmp = 262144 - u32_tmp; + u32_tmp = u32_tmp / (1 << (11 - stv0367_readbits(state, + F367CAB_AGC_IF_BWSEL))); + + if (u32_tmp < stv0367_readbits(state, + F367CAB_AGC_PWRREF_LO) + + 256 * stv0367_readbits(state, + F367CAB_AGC_PWRREF_HI) - 10) + QAM_Lock = 0x0f; + } else { + usleep_range(10000, 20000); + LockTime += 10; + } + dprintk("QAM_Lock=0x%x LockTime=%d\n", QAM_Lock, LockTime); + tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); + + dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); + + } while (((QAM_Lock != 0x0c) && (QAM_Lock != 0x0b)) && + (LockTime < DemodTimeOut)); + + dprintk("QAM_Lock=0x%x\n", QAM_Lock); + + tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); + dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); + tmp = stv0367_readreg(state, R367CAB_IT_STATUS2); + dprintk("R367CAB_IT_STATUS2=0x%x\n", tmp); + + tmp = stv0367cab_get_derot_freq(state, cab_state->adc_clk); + dprintk("stv0367cab_get_derot_freq=0x%x\n", tmp); + + if ((QAM_Lock == 0x0c) || (QAM_Lock == 0x0b)) { + /* Wait for FEC lock */ + LockTime = 0; + do { + usleep_range(5000, 7000); + LockTime += 5; + QAMFEC_Lock = stv0367cab_qamfec_lock(state); + } while (!QAMFEC_Lock && (LockTime < FECTimeOut)); + } else + QAMFEC_Lock = 0; + + if (QAMFEC_Lock) { + signalType = FE_CAB_DATAOK; + cab_state->spect_inv = stv0367_readbits(state, + F367CAB_QUAD_INV); +#if 0 +/* not clear for me */ + if (ifkhz != 0) { + if (ifkhz > cab_state->adc_clk / 1000) { + cab_state->freq_khz = + FE_Cab_TunerGetFrequency(pIntParams->hTuner) + - stv0367cab_get_derot_freq(state, cab_state->adc_clk) + - cab_state->adc_clk / 1000 + ifkhz; + } else { + cab_state->freq_khz = + FE_Cab_TunerGetFrequency(pIntParams->hTuner) + - stv0367cab_get_derot_freq(state, cab_state->adc_clk) + + ifkhz; + } + } else { + cab_state->freq_khz = + FE_Cab_TunerGetFrequency(pIntParams->hTuner) + + stv0367cab_get_derot_freq(state, + cab_state->adc_clk) - + cab_state->adc_clk / 4000; + } +#endif + cab_state->symbol_rate = stv0367cab_GetSymbolRate(state, + cab_state->mclk); + cab_state->locked = 1; + + /* stv0367_setbits(state, F367CAB_AGC_ACCUMRSTSEL,7);*/ + } else + signalType = stv0367cab_fsm_signaltype(QAM_Lock); + + /* Set the AGC control values to tracking values */ + stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, TrackAGCAccum); + return signalType; +} + +static int stv0367cab_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367cab_state *cab_state = state->cab_state; + enum stv0367cab_mod QAMSize = 0; + + dprintk("%s: freq = %d, srate = %d\n", __func__, + p->frequency, p->symbol_rate); + + cab_state->derot_offset = 0; + + switch (p->modulation) { + case QAM_16: + QAMSize = FE_CAB_MOD_QAM16; + break; + case QAM_32: + QAMSize = FE_CAB_MOD_QAM32; + break; + case QAM_64: + QAMSize = FE_CAB_MOD_QAM64; + break; + case QAM_128: + QAMSize = FE_CAB_MOD_QAM128; + break; + case QAM_256: + QAMSize = FE_CAB_MOD_QAM256; + break; + default: + break; + } + + if (state->reinit_on_setfrontend) + stv0367cab_init(fe); + + /* Tuner Frequency Setting */ + if (fe->ops.tuner_ops.set_params) { + if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + stv0367cab_SetQamSize( + state, + p->symbol_rate, + QAMSize); + + stv0367cab_set_srate(state, + cab_state->adc_clk, + cab_state->mclk, + p->symbol_rate, + QAMSize); + /* Search algorithm launch, [-1.1*RangeOffset, +1.1*RangeOffset] scan */ + cab_state->state = stv0367cab_algo(state, p); + return 0; +} + +static int stv0367cab_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct stv0367cab_state *cab_state = state->cab_state; + u32 ifkhz = 0; + + enum stv0367cab_mod QAMSize; + + dprintk("%s:\n", __func__); + + stv0367_get_if_khz(state, &ifkhz); + p->symbol_rate = stv0367cab_GetSymbolRate(state, cab_state->mclk); + + QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); + switch (QAMSize) { + case FE_CAB_MOD_QAM16: + p->modulation = QAM_16; + break; + case FE_CAB_MOD_QAM32: + p->modulation = QAM_32; + break; + case FE_CAB_MOD_QAM64: + p->modulation = QAM_64; + break; + case FE_CAB_MOD_QAM128: + p->modulation = QAM_128; + break; + case FE_CAB_MOD_QAM256: + p->modulation = QAM_256; + break; + default: + break; + } + + p->frequency = stv0367_get_tuner_freq(fe); + + dprintk("%s: tuner frequency = %d\n", __func__, p->frequency); + + if (ifkhz == 0) { + p->frequency += + (stv0367cab_get_derot_freq(state, cab_state->adc_clk) - + cab_state->adc_clk / 4000); + return 0; + } + + if (ifkhz > cab_state->adc_clk / 1000) + p->frequency += (ifkhz + - stv0367cab_get_derot_freq(state, cab_state->adc_clk) + - cab_state->adc_clk / 1000); + else + p->frequency += (ifkhz + - stv0367cab_get_derot_freq(state, cab_state->adc_clk)); + + return 0; +} + +#if 0 +void stv0367cab_GetErrorCount(state, enum stv0367cab_mod QAMSize, + u32 symbol_rate, FE_367qam_Monitor *Monitor_results) +{ + stv0367cab_OptimiseNByteAndGetBER(state, QAMSize, symbol_rate, Monitor_results); + stv0367cab_GetPacketsCount(state, Monitor_results); + + return; +} + +static int stv0367cab_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0367_state *state = fe->demodulator_priv; + + return 0; +} +#endif +static s32 stv0367cab_get_rf_lvl(struct stv0367_state *state) +{ + s32 rfLevel = 0; + s32 RfAgcPwm = 0, IfAgcPwm = 0; + u8 i; + + stv0367_writebits(state, F367CAB_STDBY_ADCGP, 0x0); + + RfAgcPwm = + (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_LO) & 0x03) + + (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_HI) << 2); + RfAgcPwm = 100 * RfAgcPwm / 1023; + + IfAgcPwm = + stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_LO) + + (stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_HI) << 8); + if (IfAgcPwm >= 2048) + IfAgcPwm -= 2048; + else + IfAgcPwm += 2048; + + IfAgcPwm = 100 * IfAgcPwm / 4095; + + /* For DTT75467 on NIM */ + if (RfAgcPwm < 90 && IfAgcPwm < 28) { + for (i = 0; i < RF_LOOKUP_TABLE_SIZE; i++) { + if (RfAgcPwm <= stv0367cab_RF_LookUp1[0][i]) { + rfLevel = (-1) * stv0367cab_RF_LookUp1[1][i]; + break; + } + } + if (i == RF_LOOKUP_TABLE_SIZE) + rfLevel = -56; + } else { /*if IF AGC>10*/ + for (i = 0; i < RF_LOOKUP_TABLE2_SIZE; i++) { + if (IfAgcPwm <= stv0367cab_RF_LookUp2[0][i]) { + rfLevel = (-1) * stv0367cab_RF_LookUp2[1][i]; + break; + } + } + if (i == RF_LOOKUP_TABLE2_SIZE) + rfLevel = -72; + } + return rfLevel; +} + +static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stv0367_state *state = fe->demodulator_priv; + + s32 signal = stv0367cab_get_rf_lvl(state); + + dprintk("%s: signal=%d dBm\n", __func__, signal); + + if (signal <= -72) + *strength = 65535; + else + *strength = (22 + signal) * (-1311); + + dprintk("%s: strength=%d\n", __func__, (*strength)); + + return 0; +} + +static int stv0367cab_snr_power(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + enum stv0367cab_mod QAMSize; + + QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); + switch (QAMSize) { + case FE_CAB_MOD_QAM4: + return 21904; + case FE_CAB_MOD_QAM16: + return 20480; + case FE_CAB_MOD_QAM32: + return 23040; + case FE_CAB_MOD_QAM64: + return 21504; + case FE_CAB_MOD_QAM128: + return 23616; + case FE_CAB_MOD_QAM256: + return 21760; + case FE_CAB_MOD_QAM1024: + return 21280; + default: + break; + } + + return 1; +} + +static int stv0367cab_snr_readreg(struct dvb_frontend *fe, int avgdiv) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 regval = 0; + int i; + + for (i = 0; i < 10; i++) { + regval += (stv0367_readbits(state, F367CAB_SNR_LO) + + 256 * stv0367_readbits(state, F367CAB_SNR_HI)); + } + + if (avgdiv) + regval /= 10; + + return regval; +} + +static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 noisepercentage; + u32 regval = 0, temp = 0; + int power; + + power = stv0367cab_snr_power(fe); + regval = stv0367cab_snr_readreg(fe, 1); + + if (regval != 0) { + temp = power + * (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER))); + temp /= regval; + } + + /* table values, not needed to calculate logarithms */ + if (temp >= 5012) + noisepercentage = 100; + else if (temp >= 3981) + noisepercentage = 93; + else if (temp >= 3162) + noisepercentage = 86; + else if (temp >= 2512) + noisepercentage = 79; + else if (temp >= 1995) + noisepercentage = 72; + else if (temp >= 1585) + noisepercentage = 65; + else if (temp >= 1259) + noisepercentage = 58; + else if (temp >= 1000) + noisepercentage = 50; + else if (temp >= 794) + noisepercentage = 43; + else if (temp >= 501) + noisepercentage = 36; + else if (temp >= 316) + noisepercentage = 29; + else if (temp >= 200) + noisepercentage = 22; + else if (temp >= 158) + noisepercentage = 14; + else if (temp >= 126) + noisepercentage = 7; + else + noisepercentage = 0; + + dprintk("%s: noisepercentage=%d\n", __func__, noisepercentage); + + *snr = (noisepercentage * 65535) / 100; + + return 0; +} + +static int stv0367cab_read_ucblcks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct stv0367_state *state = fe->demodulator_priv; + int corrected, tscount; + + *ucblocks = (stv0367_readreg(state, R367CAB_RS_COUNTER_5) << 8) + | stv0367_readreg(state, R367CAB_RS_COUNTER_4); + corrected = (stv0367_readreg(state, R367CAB_RS_COUNTER_3) << 8) + | stv0367_readreg(state, R367CAB_RS_COUNTER_2); + tscount = (stv0367_readreg(state, R367CAB_RS_COUNTER_2) << 8) + | stv0367_readreg(state, R367CAB_RS_COUNTER_1); + + dprintk("%s: uncorrected blocks=%d corrected blocks=%d tscount=%d\n", + __func__, *ucblocks, corrected, tscount); + + return 0; +}; + +static const struct dvb_frontend_ops stv0367cab_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "ST STV0367 DVB-C", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .caps = 0x400 |/* FE_CAN_QAM_4 */ + FE_CAN_QAM_16 | FE_CAN_QAM_32 | + FE_CAN_QAM_64 | FE_CAN_QAM_128 | + FE_CAN_QAM_256 | FE_CAN_FEC_AUTO + }, + .release = stv0367_release, + .init = stv0367cab_init, + .sleep = stv0367cab_sleep, + .i2c_gate_ctrl = stv0367cab_gate_ctrl, + .set_frontend = stv0367cab_set_frontend, + .get_frontend = stv0367cab_get_frontend, + .read_status = stv0367cab_read_status, +/* .read_ber = stv0367cab_read_ber, */ + .read_signal_strength = stv0367cab_read_strength, + .read_snr = stv0367cab_read_snr, + .read_ucblocks = stv0367cab_read_ucblcks, + .get_tune_settings = stv0367_get_tune_settings, +}; + +struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + struct stv0367_state *state = NULL; + struct stv0367cab_state *cab_state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); + if (state == NULL) + goto error; + cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL); + if (cab_state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->config = config; + cab_state->search_range = 280000; + cab_state->qamfec_status_reg = F367CAB_QAMFEC_LOCK; + state->cab_state = cab_state; + state->fe.ops = stv0367cab_ops; + state->fe.demodulator_priv = state; + state->chip_id = stv0367_readreg(state, 0xf000); + + /* demod operation options */ + state->use_i2c_gatectrl = 1; + state->deftabs = STV0367_DEFTAB_GENERIC; + state->reinit_on_setfrontend = 1; + state->auto_if_khz = 0; + + dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); + + /* check if the demod is there */ + if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) + goto error; + + return &state->fe; + +error: + kfree(cab_state); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stv0367cab_attach); + +/* + * Functions for operation on Digital Devices hardware + */ + +static void stv0367ddb_setup_ter(struct stv0367_state *state) +{ + stv0367_writereg(state, R367TER_DEBUG_LT4, 0x00); + stv0367_writereg(state, R367TER_DEBUG_LT5, 0x00); + stv0367_writereg(state, R367TER_DEBUG_LT6, 0x00); /* R367CAB_CTRL_1 */ + stv0367_writereg(state, R367TER_DEBUG_LT7, 0x00); /* R367CAB_CTRL_2 */ + stv0367_writereg(state, R367TER_DEBUG_LT8, 0x00); + stv0367_writereg(state, R367TER_DEBUG_LT9, 0x00); + + /* Tuner Setup */ + /* Buffer Q disabled, I Enabled, unsigned ADC */ + stv0367_writereg(state, R367TER_ANADIGCTRL, 0x89); + stv0367_writereg(state, R367TER_DUAL_AD12, 0x04); /* ADCQ disabled */ + + /* Clock setup */ + /* PLL bypassed and disabled */ + stv0367_writereg(state, R367TER_ANACTRL, 0x0D); + stv0367_writereg(state, R367TER_TOPCTRL, 0x00); /* Set OFDM */ + + /* IC runs at 54 MHz with a 27 MHz crystal */ + stv0367_pll_setup(state, STV0367_ICSPEED_53125, state->config->xtal); + + msleep(50); + /* PLL enabled and used */ + stv0367_writereg(state, R367TER_ANACTRL, 0x00); + + state->activedemod = demod_ter; +} + +static void stv0367ddb_setup_cab(struct stv0367_state *state) +{ + stv0367_writereg(state, R367TER_DEBUG_LT4, 0x00); + stv0367_writereg(state, R367TER_DEBUG_LT5, 0x01); + stv0367_writereg(state, R367TER_DEBUG_LT6, 0x06); /* R367CAB_CTRL_1 */ + stv0367_writereg(state, R367TER_DEBUG_LT7, 0x03); /* R367CAB_CTRL_2 */ + stv0367_writereg(state, R367TER_DEBUG_LT8, 0x00); + stv0367_writereg(state, R367TER_DEBUG_LT9, 0x00); + + /* Tuner Setup */ + /* Buffer Q disabled, I Enabled, signed ADC */ + stv0367_writereg(state, R367TER_ANADIGCTRL, 0x8B); + /* ADCQ disabled */ + stv0367_writereg(state, R367TER_DUAL_AD12, 0x04); + + /* Clock setup */ + /* PLL bypassed and disabled */ + stv0367_writereg(state, R367TER_ANACTRL, 0x0D); + /* Set QAM */ + stv0367_writereg(state, R367TER_TOPCTRL, 0x10); + + /* IC runs at 58 MHz with a 27 MHz crystal */ + stv0367_pll_setup(state, STV0367_ICSPEED_58000, state->config->xtal); + + msleep(50); + /* PLL enabled and used */ + stv0367_writereg(state, R367TER_ANACTRL, 0x00); + + state->cab_state->mclk = stv0367cab_get_mclk(&state->fe, + state->config->xtal); + state->cab_state->adc_clk = stv0367cab_get_adc_freq(&state->fe, + state->config->xtal); + + state->activedemod = demod_cab; +} + +static int stv0367ddb_set_frontend(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + + switch (fe->dtv_property_cache.delivery_system) { + case SYS_DVBT: + if (state->activedemod != demod_ter) + stv0367ddb_setup_ter(state); + + return stv0367ter_set_frontend(fe); + case SYS_DVBC_ANNEX_A: + if (state->activedemod != demod_cab) + stv0367ddb_setup_cab(state); + + /* protect against division error oopses */ + if (fe->dtv_property_cache.symbol_rate == 0) { + printk(KERN_ERR "Invalid symbol rate\n"); + return -EINVAL; + } + + return stv0367cab_set_frontend(fe); + default: + break; + } + + return -EINVAL; +} + +static void stv0367ddb_read_signal_strength(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 signalstrength; + + switch (state->activedemod) { + case demod_cab: + signalstrength = stv0367cab_get_rf_lvl(state) * 1000; + break; + default: + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].uvalue = signalstrength; +} + +static void stv0367ddb_read_snr(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int cab_pwr; + u32 regval, tmpval, snrval = 0; + + switch (state->activedemod) { + case demod_ter: + snrval = stv0367ter_snr_readreg(fe); + break; + case demod_cab: + cab_pwr = stv0367cab_snr_power(fe); + regval = stv0367cab_snr_readreg(fe, 0); + + /* prevent division by zero */ + if (!regval) { + snrval = 0; + break; + } + + tmpval = (cab_pwr * 320) / regval; + snrval = ((tmpval != 0) ? (intlog2(tmpval) / 5581) : 0); + break; + default: + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].uvalue = snrval; +} + +static void stv0367ddb_read_ucblocks(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 ucblocks = 0; + + switch (state->activedemod) { + case demod_ter: + stv0367ter_read_ucblocks(fe, &ucblocks); + break; + case demod_cab: + stv0367cab_read_ucblcks(fe, &ucblocks); + break; + default: + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue = ucblocks; +} + +static int stv0367ddb_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int ret = 0; + + switch (state->activedemod) { + case demod_ter: + ret = stv0367ter_read_status(fe, status); + break; + case demod_cab: + ret = stv0367cab_read_status(fe, status); + break; + default: + break; + } + + /* stop and report on *_read_status failure */ + if (ret) + return ret; + + stv0367ddb_read_signal_strength(fe); + + /* read carrier/noise when a carrier is detected */ + if (*status & FE_HAS_CARRIER) + stv0367ddb_read_snr(fe); + else + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* read uncorrected blocks on FE_HAS_LOCK */ + if (*status & FE_HAS_LOCK) + stv0367ddb_read_ucblocks(fe); + else + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +} + +static int stv0367ddb_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv0367_state *state = fe->demodulator_priv; + + switch (state->activedemod) { + case demod_ter: + return stv0367ter_get_frontend(fe, p); + case demod_cab: + return stv0367cab_get_frontend(fe, p); + default: + break; + } + + return 0; +} + +static int stv0367ddb_sleep(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + + switch (state->activedemod) { + case demod_ter: + state->activedemod = demod_none; + return stv0367ter_sleep(fe); + case demod_cab: + state->activedemod = demod_none; + return stv0367cab_sleep(fe); + default: + break; + } + + return -EINVAL; +} + +static int stv0367ddb_init(struct stv0367_state *state) +{ + struct stv0367ter_state *ter_state = state->ter_state; + struct dtv_frontend_properties *p = &state->fe.dtv_property_cache; + + stv0367_writereg(state, R367TER_TOPCTRL, 0x10); + + if (stv0367_deftabs[state->deftabs][STV0367_TAB_BASE]) + stv0367_write_table(state, + stv0367_deftabs[state->deftabs][STV0367_TAB_BASE]); + + stv0367_write_table(state, + stv0367_deftabs[state->deftabs][STV0367_TAB_CAB]); + + stv0367_writereg(state, R367TER_TOPCTRL, 0x00); + stv0367_write_table(state, + stv0367_deftabs[state->deftabs][STV0367_TAB_TER]); + + stv0367_writereg(state, R367TER_GAIN_SRC1, 0x2A); + stv0367_writereg(state, R367TER_GAIN_SRC2, 0xD6); + stv0367_writereg(state, R367TER_INC_DEROT1, 0x55); + stv0367_writereg(state, R367TER_INC_DEROT2, 0x55); + stv0367_writereg(state, R367TER_TRL_CTL, 0x14); + stv0367_writereg(state, R367TER_TRL_NOMRATE1, 0xAE); + stv0367_writereg(state, R367TER_TRL_NOMRATE2, 0x56); + stv0367_writereg(state, R367TER_FEPATH_CFG, 0x0); + + /* OFDM TS Setup */ + + stv0367_writereg(state, R367TER_TSCFGH, 0x70); + stv0367_writereg(state, R367TER_TSCFGM, 0xC0); + stv0367_writereg(state, R367TER_TSCFGL, 0x20); + stv0367_writereg(state, R367TER_TSSPEED, 0x40); /* Fixed at 54 MHz */ + + stv0367_writereg(state, R367TER_TSCFGH, 0x71); + stv0367_writereg(state, R367TER_TSCFGH, 0x70); + + stv0367_writereg(state, R367TER_TOPCTRL, 0x10); + + /* Also needed for QAM */ + stv0367_writereg(state, R367TER_AGC12C, 0x01); /* AGC Pin setup */ + + stv0367_writereg(state, R367TER_AGCCTRL1, 0x8A); + + /* QAM TS setup, note exact format also depends on descrambler */ + /* settings */ + /* Inverted Clock, Swap, serial */ + stv0367_writereg(state, R367CAB_OUTFORMAT_0, 0x85); + + /* Clock setup (PLL bypassed and disabled) */ + stv0367_writereg(state, R367TER_ANACTRL, 0x0D); + + /* IC runs at 58 MHz with a 27 MHz crystal */ + stv0367_pll_setup(state, STV0367_ICSPEED_58000, state->config->xtal); + + /* Tuner setup */ + /* Buffer Q disabled, I Enabled, signed ADC */ + stv0367_writereg(state, R367TER_ANADIGCTRL, 0x8b); + stv0367_writereg(state, R367TER_DUAL_AD12, 0x04); /* ADCQ disabled */ + + /* Improves the C/N lock limit */ + stv0367_writereg(state, R367CAB_FSM_SNR2_HTH, 0x23); + /* ZIF/IF Automatic mode */ + stv0367_writereg(state, R367CAB_IQ_QAM, 0x01); + /* Improving burst noise performances */ + stv0367_writereg(state, R367CAB_EQU_FFE_LEAKAGE, 0x83); + /* Improving ACI performances */ + stv0367_writereg(state, R367CAB_IQDEM_ADJ_EN, 0x05); + + /* PLL enabled and used */ + stv0367_writereg(state, R367TER_ANACTRL, 0x00); + + stv0367_writereg(state, R367TER_I2CRPT, (0x08 | ((5 & 0x07) << 4))); + + ter_state->pBER = 0; + ter_state->first_lock = 0; + ter_state->unlock_counter = 2; + + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; +} + +static const struct dvb_frontend_ops stv0367ddb_ops = { + .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBT }, + .info = { + .name = "ST STV0367 DDB DVB-C/T", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 865 * MHz, + .frequency_stepsize_hz = 166667, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .caps = /* DVB-C */ + 0x400 |/* FE_CAN_QAM_4 */ + FE_CAN_QAM_16 | FE_CAN_QAM_32 | + FE_CAN_QAM_64 | FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + /* DVB-T */ + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_RECOVER | FE_CAN_INVERSION_AUTO | + FE_CAN_MUTE_TS + }, + .release = stv0367_release, + .sleep = stv0367ddb_sleep, + .i2c_gate_ctrl = stv0367cab_gate_ctrl, /* valid for TER and CAB */ + .set_frontend = stv0367ddb_set_frontend, + .get_frontend = stv0367ddb_get_frontend, + .get_tune_settings = stv0367_get_tune_settings, + .read_status = stv0367ddb_read_status, +}; + +struct dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + struct stv0367_state *state = NULL; + struct stv0367ter_state *ter_state = NULL; + struct stv0367cab_state *cab_state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); + if (state == NULL) + goto error; + ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL); + if (ter_state == NULL) + goto error; + cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL); + if (cab_state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->config = config; + state->ter_state = ter_state; + cab_state->search_range = 280000; + cab_state->qamfec_status_reg = F367CAB_DESCR_SYNCSTATE; + state->cab_state = cab_state; + state->fe.ops = stv0367ddb_ops; + state->fe.demodulator_priv = state; + state->chip_id = stv0367_readreg(state, R367TER_ID); + + /* demod operation options */ + state->use_i2c_gatectrl = 0; + state->deftabs = STV0367_DEFTAB_DDB; + state->reinit_on_setfrontend = 0; + state->auto_if_khz = 1; + state->activedemod = demod_none; + + dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); + + /* check if the demod is there */ + if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) + goto error; + + dev_info(&i2c->dev, "Found %s with ChipID %02X at adr %02X\n", + state->fe.ops.info.name, state->chip_id, + config->demod_address); + + stv0367ddb_init(state); + + return &state->fe; + +error: + kfree(cab_state); + kfree(ter_state); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stv0367ddb_attach); + +MODULE_PARM_DESC(debug, "Set debug"); +MODULE_PARM_DESC(i2c_debug, "Set i2c debug"); + +MODULE_AUTHOR("Igor M. Liplianin"); +MODULE_DESCRIPTION("ST STV0367 DVB-C/T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stv0367.h b/drivers/media/dvb-frontends/stv0367.h new file mode 100644 index 0000000000..d18ae0f94e --- /dev/null +++ b/drivers/media/dvb-frontends/stv0367.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0367.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0367_H +#define STV0367_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +#define STV0367_ICSPEED_53125 53125000 +#define STV0367_ICSPEED_58000 58000000 + +struct stv0367_config { + u8 demod_address; + u32 xtal; + u32 if_khz;/*4500*/ + int if_iq_mode; + int ts_mode; + int clk_pol; +}; + +#if IS_REACHABLE(CONFIG_DVB_STV0367) +extern struct +dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c); +extern struct +dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c); +extern struct +dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c); +#else +static inline struct +dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct +dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct +dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/stv0367_defs.h b/drivers/media/dvb-frontends/stv0367_defs.h new file mode 100644 index 0000000000..c520858064 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0367_defs.h @@ -0,0 +1,1291 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0367_defs.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0367_DEFS_H +#define STV0367_DEFS_H + +#include "stv0367_regs.h" + +#define STV0367_DEFTAB_GENERIC 0 +#define STV0367_DEFTAB_DDB 1 +#define STV0367_DEFTAB_MAX 2 + +#define STV0367_TAB_TER 0 +#define STV0367_TAB_CAB 1 +#define STV0367_TAB_BASE 2 +#define STV0367_TAB_MAX 3 + +struct st_register { + u16 addr; + u8 value; +}; + +/* values for STV4100 XTAL=30M int clk=53.125M*/ +static const struct st_register def0367ter[] = { + {R367TER_ID, 0x60}, + {R367TER_I2CRPT, 0xa0}, + /* {R367TER_I2CRPT, 0x22},*/ + {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */ + {R367TER_IOCFG0, 0x40}, + {R367TER_DAC0R, 0x00}, + {R367TER_IOCFG1, 0x00}, + {R367TER_DAC1R, 0x00}, + {R367TER_IOCFG2, 0x62}, + {R367TER_SDFR, 0x00}, + {R367TER_STATUS, 0xf8}, + {R367TER_AUX_CLK, 0x0a}, + {R367TER_FREESYS1, 0x00}, + {R367TER_FREESYS2, 0x00}, + {R367TER_FREESYS3, 0x00}, + {R367TER_GPIO_CFG, 0x55}, + {R367TER_GPIO_CMD, 0x00}, + {R367TER_AGC2MAX, 0xff}, + {R367TER_AGC2MIN, 0x00}, + {R367TER_AGC1MAX, 0xff}, + {R367TER_AGC1MIN, 0x00}, + {R367TER_AGCR, 0xbc}, + {R367TER_AGC2TH, 0x00}, + {R367TER_AGC12C, 0x00}, + {R367TER_AGCCTRL1, 0x85}, + {R367TER_AGCCTRL2, 0x1f}, + {R367TER_AGC1VAL1, 0x00}, + {R367TER_AGC1VAL2, 0x00}, + {R367TER_AGC2VAL1, 0x6f}, + {R367TER_AGC2VAL2, 0x05}, + {R367TER_AGC2PGA, 0x00}, + {R367TER_OVF_RATE1, 0x00}, + {R367TER_OVF_RATE2, 0x00}, + {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */ + {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */ + {R367TER_INC_DEROT1, 0x55}, + {R367TER_INC_DEROT2, 0x55}, + {R367TER_PPM_CPAMP_DIR, 0x2c}, + {R367TER_PPM_CPAMP_INV, 0x00}, + {R367TER_FREESTFE_1, 0x00}, + {R367TER_FREESTFE_2, 0x1c}, + {R367TER_DCOFFSET, 0x00}, + {R367TER_EN_PROCESS, 0x05}, + {R367TER_SDI_SMOOTHER, 0x80}, + {R367TER_FE_LOOP_OPEN, 0x1c}, + {R367TER_FREQOFF1, 0x00}, + {R367TER_FREQOFF2, 0x00}, + {R367TER_FREQOFF3, 0x00}, + {R367TER_TIMOFF1, 0x00}, + {R367TER_TIMOFF2, 0x00}, + {R367TER_EPQ, 0x02}, + {R367TER_EPQAUTO, 0x01}, + {R367TER_SYR_UPDATE, 0xf5}, + {R367TER_CHPFREE, 0x00}, + {R367TER_PPM_STATE_MAC, 0x23}, + {R367TER_INR_THRESHOLD, 0xff}, + {R367TER_EPQ_TPS_ID_CELL, 0xf9}, + {R367TER_EPQ_CFG, 0x00}, + {R367TER_EPQ_STATUS, 0x01}, + {R367TER_AUTORELOCK, 0x81}, + {R367TER_BER_THR_VMSB, 0x00}, + {R367TER_BER_THR_MSB, 0x00}, + {R367TER_BER_THR_LSB, 0x00}, + {R367TER_CCD, 0x83}, + {R367TER_SPECTR_CFG, 0x00}, + {R367TER_CHC_DUMMY, 0x18}, + {R367TER_INC_CTL, 0x88}, + {R367TER_INCTHRES_COR1, 0xb4}, + {R367TER_INCTHRES_COR2, 0x96}, + {R367TER_INCTHRES_DET1, 0x0e}, + {R367TER_INCTHRES_DET2, 0x11}, + {R367TER_IIR_CELLNB, 0x8d}, + {R367TER_IIRCX_COEFF1_MSB, 0x00}, + {R367TER_IIRCX_COEFF1_LSB, 0x00}, + {R367TER_IIRCX_COEFF2_MSB, 0x09}, + {R367TER_IIRCX_COEFF2_LSB, 0x18}, + {R367TER_IIRCX_COEFF3_MSB, 0x14}, + {R367TER_IIRCX_COEFF3_LSB, 0x9c}, + {R367TER_IIRCX_COEFF4_MSB, 0x00}, + {R367TER_IIRCX_COEFF4_LSB, 0x00}, + {R367TER_IIRCX_COEFF5_MSB, 0x36}, + {R367TER_IIRCX_COEFF5_LSB, 0x42}, + {R367TER_FEPATH_CFG, 0x00}, + {R367TER_PMC1_FUNC, 0x65}, + {R367TER_PMC1_FOR, 0x00}, + {R367TER_PMC2_FUNC, 0x00}, + {R367TER_STATUS_ERR_DA, 0xe0}, + {R367TER_DIG_AGC_R, 0xfe}, + {R367TER_COMAGC_TARMSB, 0x0b}, + {R367TER_COM_AGC_TAR_ENMODE, 0x41}, + {R367TER_COM_AGC_CFG, 0x3e}, + {R367TER_COM_AGC_GAIN1, 0x39}, + {R367TER_AUT_AGC_TARGETMSB, 0x0b}, + {R367TER_LOCK_DET_MSB, 0x01}, + {R367TER_AGCTAR_LOCK_LSBS, 0x40}, + {R367TER_AUT_GAIN_EN, 0xf4}, + {R367TER_AUT_CFG, 0xf0}, + {R367TER_LOCKN, 0x23}, + {R367TER_INT_X_3, 0x00}, + {R367TER_INT_X_2, 0x03}, + {R367TER_INT_X_1, 0x8d}, + {R367TER_INT_X_0, 0xa0}, + {R367TER_MIN_ERRX_MSB, 0x00}, + {R367TER_COR_CTL, 0x23}, + {R367TER_COR_STAT, 0xf6}, + {R367TER_COR_INTEN, 0x00}, + {R367TER_COR_INTSTAT, 0x3f}, + {R367TER_COR_MODEGUARD, 0x03}, + {R367TER_AGC_CTL, 0x08}, + {R367TER_AGC_MANUAL1, 0x00}, + {R367TER_AGC_MANUAL2, 0x00}, + {R367TER_AGC_TARG, 0x16}, + {R367TER_AGC_GAIN1, 0x53}, + {R367TER_AGC_GAIN2, 0x1d}, + {R367TER_RESERVED_1, 0x00}, + {R367TER_RESERVED_2, 0x00}, + {R367TER_RESERVED_3, 0x00}, + {R367TER_CAS_CTL, 0x44}, + {R367TER_CAS_FREQ, 0xb3}, + {R367TER_CAS_DAGCGAIN, 0x12}, + {R367TER_SYR_CTL, 0x04}, + {R367TER_SYR_STAT, 0x10}, + {R367TER_SYR_NCO1, 0x00}, + {R367TER_SYR_NCO2, 0x00}, + {R367TER_SYR_OFFSET1, 0x00}, + {R367TER_SYR_OFFSET2, 0x00}, + {R367TER_FFT_CTL, 0x00}, + {R367TER_SCR_CTL, 0x70}, + {R367TER_PPM_CTL1, 0xf8}, + {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */ + {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */ + {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */ + {R367TER_TRL_TIME1, 0x1d}, + {R367TER_TRL_TIME2, 0xfc}, + {R367TER_CRL_CTL, 0x24}, + {R367TER_CRL_FREQ1, 0xad}, + {R367TER_CRL_FREQ2, 0x9d}, + {R367TER_CRL_FREQ3, 0xff}, + {R367TER_CHC_CTL, 0x01}, + {R367TER_CHC_SNR, 0xf0}, + {R367TER_BDI_CTL, 0x00}, + {R367TER_DMP_CTL, 0x00}, + {R367TER_TPS_RCVD1, 0x30}, + {R367TER_TPS_RCVD2, 0x02}, + {R367TER_TPS_RCVD3, 0x01}, + {R367TER_TPS_RCVD4, 0x00}, + {R367TER_TPS_ID_CELL1, 0x00}, + {R367TER_TPS_ID_CELL2, 0x00}, + {R367TER_TPS_RCVD5_SET1, 0x02}, + {R367TER_TPS_SET2, 0x02}, + {R367TER_TPS_SET3, 0x01}, + {R367TER_TPS_CTL, 0x00}, + {R367TER_CTL_FFTOSNUM, 0x34}, + {R367TER_TESTSELECT, 0x09}, + {R367TER_MSC_REV, 0x0a}, + {R367TER_PIR_CTL, 0x00}, + {R367TER_SNR_CARRIER1, 0xa1}, + {R367TER_SNR_CARRIER2, 0x9a}, + {R367TER_PPM_CPAMP, 0x2c}, + {R367TER_TSM_AP0, 0x00}, + {R367TER_TSM_AP1, 0x00}, + {R367TER_TSM_AP2, 0x00}, + {R367TER_TSM_AP3, 0x00}, + {R367TER_TSM_AP4, 0x00}, + {R367TER_TSM_AP5, 0x00}, + {R367TER_TSM_AP6, 0x00}, + {R367TER_TSM_AP7, 0x00}, + {R367TER_TSTRES, 0x00}, + {R367TER_ANACTRL, 0x0D},/* PLL stopped, restart at init!!! */ + {R367TER_TSTBUS, 0x00}, + {R367TER_TSTRATE, 0x00}, + {R367TER_CONSTMODE, 0x01}, + {R367TER_CONSTCARR1, 0x00}, + {R367TER_CONSTCARR2, 0x00}, + {R367TER_ICONSTEL, 0x0a}, + {R367TER_QCONSTEL, 0x15}, + {R367TER_TSTBISTRES0, 0x00}, + {R367TER_TSTBISTRES1, 0x00}, + {R367TER_TSTBISTRES2, 0x28}, + {R367TER_TSTBISTRES3, 0x00}, + {R367TER_RF_AGC1, 0xff}, + {R367TER_RF_AGC2, 0x83}, + {R367TER_ANADIGCTRL, 0x19}, + {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */ + {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */ + {R367TER_PLLSETUP, 0x18}, + {R367TER_DUAL_AD12, 0x0C},/* for xc5000 AGC voltage 1.6V */ + {R367TER_TSTBIST, 0x00}, + {R367TER_PAD_COMP_CTRL, 0x00}, + {R367TER_PAD_COMP_WR, 0x00}, + {R367TER_PAD_COMP_RD, 0xe0}, + {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00}, + {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00}, + {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00}, + {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00}, + {R367TER_SYR_FLAG, 0x00}, + {R367TER_CRL_TARGET1, 0x00}, + {R367TER_CRL_TARGET2, 0x00}, + {R367TER_CRL_TARGET3, 0x00}, + {R367TER_CRL_TARGET4, 0x00}, + {R367TER_CRL_FLAG, 0x00}, + {R367TER_TRL_TARGET1, 0x00}, + {R367TER_TRL_TARGET2, 0x00}, + {R367TER_TRL_CHC, 0x00}, + {R367TER_CHC_SNR_TARG, 0x00}, + {R367TER_TOP_TRACK, 0x00}, + {R367TER_TRACKER_FREE1, 0x00}, + {R367TER_ERROR_CRL1, 0x00}, + {R367TER_ERROR_CRL2, 0x00}, + {R367TER_ERROR_CRL3, 0x00}, + {R367TER_ERROR_CRL4, 0x00}, + {R367TER_DEC_NCO1, 0x2c}, + {R367TER_DEC_NCO2, 0x0f}, + {R367TER_DEC_NCO3, 0x20}, + {R367TER_SNR, 0xf1}, + {R367TER_SYR_FFTADJ1, 0x00}, + {R367TER_SYR_FFTADJ2, 0x00}, + {R367TER_SYR_CHCADJ1, 0x00}, + {R367TER_SYR_CHCADJ2, 0x00}, + {R367TER_SYR_OFF, 0x00}, + {R367TER_PPM_OFFSET1, 0x00}, + {R367TER_PPM_OFFSET2, 0x03}, + {R367TER_TRACKER_FREE2, 0x00}, + {R367TER_DEBG_LT10, 0x00}, + {R367TER_DEBG_LT11, 0x00}, + {R367TER_DEBG_LT12, 0x00}, + {R367TER_DEBG_LT13, 0x00}, + {R367TER_DEBG_LT14, 0x00}, + {R367TER_DEBG_LT15, 0x00}, + {R367TER_DEBG_LT16, 0x00}, + {R367TER_DEBG_LT17, 0x00}, + {R367TER_DEBG_LT18, 0x00}, + {R367TER_DEBG_LT19, 0x00}, + {R367TER_DEBG_LT1A, 0x00}, + {R367TER_DEBG_LT1B, 0x00}, + {R367TER_DEBG_LT1C, 0x00}, + {R367TER_DEBG_LT1D, 0x00}, + {R367TER_DEBG_LT1E, 0x00}, + {R367TER_DEBG_LT1F, 0x00}, + {R367TER_RCCFGH, 0x00}, + {R367TER_RCCFGM, 0x00}, + {R367TER_RCCFGL, 0x00}, + {R367TER_RCINSDELH, 0x00}, + {R367TER_RCINSDELM, 0x00}, + {R367TER_RCINSDELL, 0x00}, + {R367TER_RCSTATUS, 0x00}, + {R367TER_RCSPEED, 0x6f}, + {R367TER_RCDEBUGM, 0xe7}, + {R367TER_RCDEBUGL, 0x9b}, + {R367TER_RCOBSCFG, 0x00}, + {R367TER_RCOBSM, 0x00}, + {R367TER_RCOBSL, 0x00}, + {R367TER_RCFECSPY, 0x00}, + {R367TER_RCFSPYCFG, 0x00}, + {R367TER_RCFSPYDATA, 0x00}, + {R367TER_RCFSPYOUT, 0x00}, + {R367TER_RCFSTATUS, 0x00}, + {R367TER_RCFGOODPACK, 0x00}, + {R367TER_RCFPACKCNT, 0x00}, + {R367TER_RCFSPYMISC, 0x00}, + {R367TER_RCFBERCPT4, 0x00}, + {R367TER_RCFBERCPT3, 0x00}, + {R367TER_RCFBERCPT2, 0x00}, + {R367TER_RCFBERCPT1, 0x00}, + {R367TER_RCFBERCPT0, 0x00}, + {R367TER_RCFBERERR2, 0x00}, + {R367TER_RCFBERERR1, 0x00}, + {R367TER_RCFBERERR0, 0x00}, + {R367TER_RCFSTATESM, 0x00}, + {R367TER_RCFSTATESL, 0x00}, + {R367TER_RCFSPYBER, 0x00}, + {R367TER_RCFSPYDISTM, 0x00}, + {R367TER_RCFSPYDISTL, 0x00}, + {R367TER_RCFSPYOBS7, 0x00}, + {R367TER_RCFSPYOBS6, 0x00}, + {R367TER_RCFSPYOBS5, 0x00}, + {R367TER_RCFSPYOBS4, 0x00}, + {R367TER_RCFSPYOBS3, 0x00}, + {R367TER_RCFSPYOBS2, 0x00}, + {R367TER_RCFSPYOBS1, 0x00}, + {R367TER_RCFSPYOBS0, 0x00}, + {R367TER_TSGENERAL, 0x00}, + {R367TER_RC1SPEED, 0x6f}, + {R367TER_TSGSTATUS, 0x18}, + {R367TER_FECM, 0x01}, + {R367TER_VTH12, 0xff}, + {R367TER_VTH23, 0xa1}, + {R367TER_VTH34, 0x64}, + {R367TER_VTH56, 0x40}, + {R367TER_VTH67, 0x00}, + {R367TER_VTH78, 0x2c}, + {R367TER_VITCURPUN, 0x12}, + {R367TER_VERROR, 0x01}, + {R367TER_PRVIT, 0x3f}, + {R367TER_VAVSRVIT, 0x00}, + {R367TER_VSTATUSVIT, 0xbd}, + {R367TER_VTHINUSE, 0xa1}, + {R367TER_KDIV12, 0x20}, + {R367TER_KDIV23, 0x40}, + {R367TER_KDIV34, 0x20}, + {R367TER_KDIV56, 0x30}, + {R367TER_KDIV67, 0x00}, + {R367TER_KDIV78, 0x30}, + {R367TER_SIGPOWER, 0x54}, + {R367TER_DEMAPVIT, 0x40}, + {R367TER_VITSCALE, 0x00}, + {R367TER_FFEC1PRG, 0x00}, + {R367TER_FVITCURPUN, 0x12}, + {R367TER_FVERROR, 0x01}, + {R367TER_FVSTATUSVIT, 0xbd}, + {R367TER_DEBUG_LT1, 0x00}, + {R367TER_DEBUG_LT2, 0x00}, + {R367TER_DEBUG_LT3, 0x00}, + {R367TER_TSTSFMET, 0x00}, + {R367TER_SELOUT, 0x00}, + {R367TER_TSYNC, 0x00}, + {R367TER_TSTERR, 0x00}, + {R367TER_TSFSYNC, 0x00}, + {R367TER_TSTSFERR, 0x00}, + {R367TER_TSTTSSF1, 0x01}, + {R367TER_TSTTSSF2, 0x1f}, + {R367TER_TSTTSSF3, 0x00}, + {R367TER_TSTTS1, 0x00}, + {R367TER_TSTTS2, 0x1f}, + {R367TER_TSTTS3, 0x01}, + {R367TER_TSTTS4, 0x00}, + {R367TER_TSTTSRC, 0x00}, + {R367TER_TSTTSRS, 0x00}, + {R367TER_TSSTATEM, 0xb0}, + {R367TER_TSSTATEL, 0x40}, + {R367TER_TSCFGH, 0xC0}, + {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */ + {R367TER_TSCFGL, 0x20}, + {R367TER_TSSYNC, 0x00}, + {R367TER_TSINSDELH, 0x00}, + {R367TER_TSINSDELM, 0x00}, + {R367TER_TSINSDELL, 0x00}, + {R367TER_TSDIVN, 0x03}, + {R367TER_TSDIVPM, 0x00}, + {R367TER_TSDIVPL, 0x00}, + {R367TER_TSDIVQM, 0x00}, + {R367TER_TSDIVQL, 0x00}, + {R367TER_TSDILSTKM, 0x00}, + {R367TER_TSDILSTKL, 0x00}, + {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */ + {R367TER_TSSTATUS, 0x81}, + {R367TER_TSSTATUS2, 0x6a}, + {R367TER_TSBITRATEM, 0x0f}, + {R367TER_TSBITRATEL, 0xc6}, + {R367TER_TSPACKLENM, 0x00}, + {R367TER_TSPACKLENL, 0xfc}, + {R367TER_TSBLOCLENM, 0x0a}, + {R367TER_TSBLOCLENL, 0x80}, + {R367TER_TSDLYH, 0x90}, + {R367TER_TSDLYM, 0x68}, + {R367TER_TSDLYL, 0x01}, + {R367TER_TSNPDAV, 0x00}, + {R367TER_TSBUFSTATH, 0x00}, + {R367TER_TSBUFSTATM, 0x00}, + {R367TER_TSBUFSTATL, 0x00}, + {R367TER_TSDEBUGM, 0xcf}, + {R367TER_TSDEBUGL, 0x1e}, + {R367TER_TSDLYSETH, 0x00}, + {R367TER_TSDLYSETM, 0x68}, + {R367TER_TSDLYSETL, 0x00}, + {R367TER_TSOBSCFG, 0x00}, + {R367TER_TSOBSM, 0x47}, + {R367TER_TSOBSL, 0x1f}, + {R367TER_ERRCTRL1, 0x95}, + {R367TER_ERRCNT1H, 0x80}, + {R367TER_ERRCNT1M, 0x00}, + {R367TER_ERRCNT1L, 0x00}, + {R367TER_ERRCTRL2, 0x95}, + {R367TER_ERRCNT2H, 0x00}, + {R367TER_ERRCNT2M, 0x00}, + {R367TER_ERRCNT2L, 0x00}, + {R367TER_FECSPY, 0x88}, + {R367TER_FSPYCFG, 0x2c}, + {R367TER_FSPYDATA, 0x3a}, + {R367TER_FSPYOUT, 0x06}, + {R367TER_FSTATUS, 0x61}, + {R367TER_FGOODPACK, 0xff}, + {R367TER_FPACKCNT, 0xff}, + {R367TER_FSPYMISC, 0x66}, + {R367TER_FBERCPT4, 0x00}, + {R367TER_FBERCPT3, 0x00}, + {R367TER_FBERCPT2, 0x36}, + {R367TER_FBERCPT1, 0x36}, + {R367TER_FBERCPT0, 0x14}, + {R367TER_FBERERR2, 0x00}, + {R367TER_FBERERR1, 0x03}, + {R367TER_FBERERR0, 0x28}, + {R367TER_FSTATESM, 0x00}, + {R367TER_FSTATESL, 0x02}, + {R367TER_FSPYBER, 0x00}, + {R367TER_FSPYDISTM, 0x01}, + {R367TER_FSPYDISTL, 0x9f}, + {R367TER_FSPYOBS7, 0xc9}, + {R367TER_FSPYOBS6, 0x99}, + {R367TER_FSPYOBS5, 0x08}, + {R367TER_FSPYOBS4, 0xec}, + {R367TER_FSPYOBS3, 0x01}, + {R367TER_FSPYOBS2, 0x0f}, + {R367TER_FSPYOBS1, 0xf5}, + {R367TER_FSPYOBS0, 0x08}, + {R367TER_SFDEMAP, 0x40}, + {R367TER_SFERROR, 0x00}, + {R367TER_SFAVSR, 0x30}, + {R367TER_SFECSTATUS, 0xcc}, + {R367TER_SFKDIV12, 0x20}, + {R367TER_SFKDIV23, 0x40}, + {R367TER_SFKDIV34, 0x20}, + {R367TER_SFKDIV56, 0x20}, + {R367TER_SFKDIV67, 0x00}, + {R367TER_SFKDIV78, 0x20}, + {R367TER_SFDILSTKM, 0x00}, + {R367TER_SFDILSTKL, 0x00}, + {R367TER_SFSTATUS, 0xb5}, + {R367TER_SFDLYH, 0x90}, + {R367TER_SFDLYM, 0x60}, + {R367TER_SFDLYL, 0x01}, + {R367TER_SFDLYSETH, 0xc0}, + {R367TER_SFDLYSETM, 0x60}, + {R367TER_SFDLYSETL, 0x00}, + {R367TER_SFOBSCFG, 0x00}, + {R367TER_SFOBSM, 0x47}, + {R367TER_SFOBSL, 0x05}, + {R367TER_SFECINFO, 0x40}, + {R367TER_SFERRCTRL, 0x74}, + {R367TER_SFERRCNTH, 0x80}, + {R367TER_SFERRCNTM, 0x00}, + {R367TER_SFERRCNTL, 0x00}, + {R367TER_SYMBRATEM, 0x2f}, + {R367TER_SYMBRATEL, 0x50}, + {R367TER_SYMBSTATUS, 0x7f}, + {R367TER_SYMBCFG, 0x00}, + {R367TER_SYMBFIFOM, 0xf4}, + {R367TER_SYMBFIFOL, 0x0d}, + {R367TER_SYMBOFFSM, 0xf0}, + {R367TER_SYMBOFFSL, 0x2d}, + {R367TER_DEBUG_LT4, 0x00}, + {R367TER_DEBUG_LT5, 0x00}, + {R367TER_DEBUG_LT6, 0x00}, + {R367TER_DEBUG_LT7, 0x00}, + {R367TER_DEBUG_LT8, 0x00}, + {R367TER_DEBUG_LT9, 0x00}, + {0x0000, 0x00}, +}; + +static const struct st_register def0367cab[] = { + {R367CAB_ID, 0x60}, + {R367CAB_I2CRPT, 0xa0}, + /*{R367CAB_I2CRPT, 0x22},*/ + {R367CAB_TOPCTRL, 0x10}, + {R367CAB_IOCFG0, 0x80}, + {R367CAB_DAC0R, 0x00}, + {R367CAB_IOCFG1, 0x00}, + {R367CAB_DAC1R, 0x00}, + {R367CAB_IOCFG2, 0x00}, + {R367CAB_SDFR, 0x00}, + {R367CAB_AUX_CLK, 0x00}, + {R367CAB_FREESYS1, 0x00}, + {R367CAB_FREESYS2, 0x00}, + {R367CAB_FREESYS3, 0x00}, + {R367CAB_GPIO_CFG, 0x55}, + {R367CAB_GPIO_CMD, 0x01}, + {R367CAB_TSTRES, 0x00}, + {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/ + {R367CAB_TSTBUS, 0x00}, + {R367CAB_RF_AGC1, 0xea}, + {R367CAB_RF_AGC2, 0x82}, + {R367CAB_ANADIGCTRL, 0x0b}, + {R367CAB_PLLMDIV, 0x01}, + {R367CAB_PLLNDIV, 0x08}, + {R367CAB_PLLSETUP, 0x18}, + {R367CAB_DUAL_AD12, 0x0C}, /* for xc5000 AGC voltage 1.6V */ + {R367CAB_TSTBIST, 0x00}, + {R367CAB_CTRL_1, 0x00}, + {R367CAB_CTRL_2, 0x03}, + {R367CAB_IT_STATUS1, 0x2b}, + {R367CAB_IT_STATUS2, 0x08}, + {R367CAB_IT_EN1, 0x00}, + {R367CAB_IT_EN2, 0x00}, + {R367CAB_CTRL_STATUS, 0x04}, + {R367CAB_TEST_CTL, 0x00}, + {R367CAB_AGC_CTL, 0x73}, + {R367CAB_AGC_IF_CFG, 0x50}, + {R367CAB_AGC_RF_CFG, 0x00}, + {R367CAB_AGC_PWM_CFG, 0x03}, + {R367CAB_AGC_PWR_REF_L, 0x5a}, + {R367CAB_AGC_PWR_REF_H, 0x00}, + {R367CAB_AGC_RF_TH_L, 0xff}, + {R367CAB_AGC_RF_TH_H, 0x07}, + {R367CAB_AGC_IF_LTH_L, 0x00}, + {R367CAB_AGC_IF_LTH_H, 0x08}, + {R367CAB_AGC_IF_HTH_L, 0xff}, + {R367CAB_AGC_IF_HTH_H, 0x07}, + {R367CAB_AGC_PWR_RD_L, 0xa0}, + {R367CAB_AGC_PWR_RD_M, 0xe9}, + {R367CAB_AGC_PWR_RD_H, 0x03}, + {R367CAB_AGC_PWM_IFCMD_L, 0xe4}, + {R367CAB_AGC_PWM_IFCMD_H, 0x00}, + {R367CAB_AGC_PWM_RFCMD_L, 0xff}, + {R367CAB_AGC_PWM_RFCMD_H, 0x07}, + {R367CAB_IQDEM_CFG, 0x01}, + {R367CAB_MIX_NCO_LL, 0x22}, + {R367CAB_MIX_NCO_HL, 0x96}, + {R367CAB_MIX_NCO_HH, 0x55}, + {R367CAB_SRC_NCO_LL, 0xff}, + {R367CAB_SRC_NCO_LH, 0x0c}, + {R367CAB_SRC_NCO_HL, 0xf5}, + {R367CAB_SRC_NCO_HH, 0x20}, + {R367CAB_IQDEM_GAIN_SRC_L, 0x06}, + {R367CAB_IQDEM_GAIN_SRC_H, 0x01}, + {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe}, + {R367CAB_IQDEM_DCRM_CFG_LH, 0xff}, + {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f}, + {R367CAB_IQDEM_DCRM_CFG_HH, 0x00}, + {R367CAB_IQDEM_ADJ_COEFF0, 0x34}, + {R367CAB_IQDEM_ADJ_COEFF1, 0xae}, + {R367CAB_IQDEM_ADJ_COEFF2, 0x46}, + {R367CAB_IQDEM_ADJ_COEFF3, 0x77}, + {R367CAB_IQDEM_ADJ_COEFF4, 0x96}, + {R367CAB_IQDEM_ADJ_COEFF5, 0x69}, + {R367CAB_IQDEM_ADJ_COEFF6, 0xc7}, + {R367CAB_IQDEM_ADJ_COEFF7, 0x01}, + {R367CAB_IQDEM_ADJ_EN, 0x04}, + {R367CAB_IQDEM_ADJ_AGC_REF, 0x94}, + {R367CAB_ALLPASSFILT1, 0xc9}, + {R367CAB_ALLPASSFILT2, 0x2d}, + {R367CAB_ALLPASSFILT3, 0xa3}, + {R367CAB_ALLPASSFILT4, 0xfb}, + {R367CAB_ALLPASSFILT5, 0xf6}, + {R367CAB_ALLPASSFILT6, 0x45}, + {R367CAB_ALLPASSFILT7, 0x6f}, + {R367CAB_ALLPASSFILT8, 0x7e}, + {R367CAB_ALLPASSFILT9, 0x05}, + {R367CAB_ALLPASSFILT10, 0x0a}, + {R367CAB_ALLPASSFILT11, 0x51}, + {R367CAB_TRL_AGC_CFG, 0x20}, + {R367CAB_TRL_LPF_CFG, 0x28}, + {R367CAB_TRL_LPF_ACQ_GAIN, 0x44}, + {R367CAB_TRL_LPF_TRK_GAIN, 0x22}, + {R367CAB_TRL_LPF_OUT_GAIN, 0x03}, + {R367CAB_TRL_LOCKDET_LTH, 0x04}, + {R367CAB_TRL_LOCKDET_HTH, 0x11}, + {R367CAB_TRL_LOCKDET_TRGVAL, 0x20}, + {R367CAB_IQ_QAM, 0x01}, + {R367CAB_FSM_STATE, 0xa0}, + {R367CAB_FSM_CTL, 0x08}, + {R367CAB_FSM_STS, 0x0c}, + {R367CAB_FSM_SNR0_HTH, 0x00}, + {R367CAB_FSM_SNR1_HTH, 0x00}, + {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */ + {R367CAB_FSM_SNR0_LTH, 0x00}, + {R367CAB_FSM_SNR1_LTH, 0x00}, + {R367CAB_FSM_EQA1_HTH, 0x00}, + {R367CAB_FSM_TEMPO, 0x32}, + {R367CAB_FSM_CONFIG, 0x03}, + {R367CAB_EQU_I_TESTTAP_L, 0x11}, + {R367CAB_EQU_I_TESTTAP_M, 0x00}, + {R367CAB_EQU_I_TESTTAP_H, 0x00}, + {R367CAB_EQU_TESTAP_CFG, 0x00}, + {R367CAB_EQU_Q_TESTTAP_L, 0xff}, + {R367CAB_EQU_Q_TESTTAP_M, 0x00}, + {R367CAB_EQU_Q_TESTTAP_H, 0x00}, + {R367CAB_EQU_TAP_CTRL, 0x00}, + {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11}, + {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05}, + {R367CAB_EQU_CTR_HIPOW_L, 0x00}, + {R367CAB_EQU_CTR_HIPOW_H, 0x00}, + {R367CAB_EQU_I_EQU_LO, 0xef}, + {R367CAB_EQU_I_EQU_HI, 0x00}, + {R367CAB_EQU_Q_EQU_LO, 0xee}, + {R367CAB_EQU_Q_EQU_HI, 0x00}, + {R367CAB_EQU_MAPPER, 0xc5}, + {R367CAB_EQU_SWEEP_RATE, 0x80}, + {R367CAB_EQU_SNR_LO, 0x64}, + {R367CAB_EQU_SNR_HI, 0x03}, + {R367CAB_EQU_GAMMA_LO, 0x00}, + {R367CAB_EQU_GAMMA_HI, 0x00}, + {R367CAB_EQU_ERR_GAIN, 0x36}, + {R367CAB_EQU_RADIUS, 0xaa}, + {R367CAB_EQU_FFE_MAINTAP, 0x00}, + {R367CAB_EQU_FFE_LEAKAGE, 0x63}, + {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf}, + {R367CAB_EQU_GAIN_WIDE, 0x88}, + {R367CAB_EQU_GAIN_NARROW, 0x41}, + {R367CAB_EQU_CTR_LPF_GAIN, 0xd1}, + {R367CAB_EQU_CRL_LPF_GAIN, 0xa7}, + {R367CAB_EQU_GLOBAL_GAIN, 0x06}, + {R367CAB_EQU_CRL_LD_SEN, 0x85}, + {R367CAB_EQU_CRL_LD_VAL, 0xe2}, + {R367CAB_EQU_CRL_TFR, 0x20}, + {R367CAB_EQU_CRL_BISTH_LO, 0x00}, + {R367CAB_EQU_CRL_BISTH_HI, 0x00}, + {R367CAB_EQU_SWEEP_RANGE_LO, 0x00}, + {R367CAB_EQU_SWEEP_RANGE_HI, 0x00}, + {R367CAB_EQU_CRL_LIMITER, 0x40}, + {R367CAB_EQU_MODULUS_MAP, 0x90}, + {R367CAB_EQU_PNT_GAIN, 0xa7}, + {R367CAB_FEC_AC_CTR_0, 0x16}, + {R367CAB_FEC_AC_CTR_1, 0x0b}, + {R367CAB_FEC_AC_CTR_2, 0x88}, + {R367CAB_FEC_AC_CTR_3, 0x02}, + {R367CAB_FEC_STATUS, 0x12}, + {R367CAB_RS_COUNTER_0, 0x7d}, + {R367CAB_RS_COUNTER_1, 0xd0}, + {R367CAB_RS_COUNTER_2, 0x19}, + {R367CAB_RS_COUNTER_3, 0x0b}, + {R367CAB_RS_COUNTER_4, 0xa3}, + {R367CAB_RS_COUNTER_5, 0x00}, + {R367CAB_BERT_0, 0x01}, + {R367CAB_BERT_1, 0x25}, + {R367CAB_BERT_2, 0x41}, + {R367CAB_BERT_3, 0x39}, + {R367CAB_OUTFORMAT_0, 0xc2}, + {R367CAB_OUTFORMAT_1, 0x22}, + {R367CAB_SMOOTHER_2, 0x28}, + {R367CAB_TSMF_CTRL_0, 0x01}, + {R367CAB_TSMF_CTRL_1, 0xc6}, + {R367CAB_TSMF_CTRL_3, 0x43}, + {R367CAB_TS_ON_ID_0, 0x00}, + {R367CAB_TS_ON_ID_1, 0x00}, + {R367CAB_TS_ON_ID_2, 0x00}, + {R367CAB_TS_ON_ID_3, 0x00}, + {R367CAB_RE_STATUS_0, 0x00}, + {R367CAB_RE_STATUS_1, 0x00}, + {R367CAB_RE_STATUS_2, 0x00}, + {R367CAB_RE_STATUS_3, 0x00}, + {R367CAB_TS_STATUS_0, 0x00}, + {R367CAB_TS_STATUS_1, 0x00}, + {R367CAB_TS_STATUS_2, 0xa0}, + {R367CAB_TS_STATUS_3, 0x00}, + {R367CAB_T_O_ID_0, 0x00}, + {R367CAB_T_O_ID_1, 0x00}, + {R367CAB_T_O_ID_2, 0x00}, + {R367CAB_T_O_ID_3, 0x00}, + {0x0000, 0x00}, +}; + +/************** + * + * Defaults / Tables for Digital Devices C/T Cine/Flex devices + * + **************/ + +static const struct st_register def0367dd_ofdm[] = { + {R367TER_AGC2MAX, 0xff}, + {R367TER_AGC2MIN, 0x00}, + {R367TER_AGC1MAX, 0xff}, + {R367TER_AGC1MIN, 0x00}, + {R367TER_AGCR, 0xbc}, + {R367TER_AGC2TH, 0x00}, + {R367TER_AGCCTRL1, 0x85}, + {R367TER_AGCCTRL2, 0x1f}, + {R367TER_AGC1VAL1, 0x00}, + {R367TER_AGC1VAL2, 0x00}, + {R367TER_AGC2VAL1, 0x6f}, + {R367TER_AGC2VAL2, 0x05}, + {R367TER_AGC2PGA, 0x00}, + {R367TER_OVF_RATE1, 0x00}, + {R367TER_OVF_RATE2, 0x00}, + {R367TER_GAIN_SRC1, 0x2b}, + {R367TER_GAIN_SRC2, 0x04}, + {R367TER_INC_DEROT1, 0x55}, + {R367TER_INC_DEROT2, 0x55}, + {R367TER_PPM_CPAMP_DIR, 0x2c}, + {R367TER_PPM_CPAMP_INV, 0x00}, + {R367TER_FREESTFE_1, 0x00}, + {R367TER_FREESTFE_2, 0x1c}, + {R367TER_DCOFFSET, 0x00}, + {R367TER_EN_PROCESS, 0x05}, + {R367TER_SDI_SMOOTHER, 0x80}, + {R367TER_FE_LOOP_OPEN, 0x1c}, + {R367TER_FREQOFF1, 0x00}, + {R367TER_FREQOFF2, 0x00}, + {R367TER_FREQOFF3, 0x00}, + {R367TER_TIMOFF1, 0x00}, + {R367TER_TIMOFF2, 0x00}, + {R367TER_EPQ, 0x02}, + {R367TER_EPQAUTO, 0x01}, + {R367TER_SYR_UPDATE, 0xf5}, + {R367TER_CHPFREE, 0x00}, + {R367TER_PPM_STATE_MAC, 0x23}, + {R367TER_INR_THRESHOLD, 0xff}, + {R367TER_EPQ_TPS_ID_CELL, 0xf9}, + {R367TER_EPQ_CFG, 0x00}, + {R367TER_EPQ_STATUS, 0x01}, + {R367TER_AUTORELOCK, 0x81}, + {R367TER_BER_THR_VMSB, 0x00}, + {R367TER_BER_THR_MSB, 0x00}, + {R367TER_BER_THR_LSB, 0x00}, + {R367TER_CCD, 0x83}, + {R367TER_SPECTR_CFG, 0x00}, + {R367TER_CHC_DUMMY, 0x18}, + {R367TER_INC_CTL, 0x88}, + {R367TER_INCTHRES_COR1, 0xb4}, + {R367TER_INCTHRES_COR2, 0x96}, + {R367TER_INCTHRES_DET1, 0x0e}, + {R367TER_INCTHRES_DET2, 0x11}, + {R367TER_IIR_CELLNB, 0x8d}, + {R367TER_IIRCX_COEFF1_MSB, 0x00}, + {R367TER_IIRCX_COEFF1_LSB, 0x00}, + {R367TER_IIRCX_COEFF2_MSB, 0x09}, + {R367TER_IIRCX_COEFF2_LSB, 0x18}, + {R367TER_IIRCX_COEFF3_MSB, 0x14}, + {R367TER_IIRCX_COEFF3_LSB, 0x9c}, + {R367TER_IIRCX_COEFF4_MSB, 0x00}, + {R367TER_IIRCX_COEFF4_LSB, 0x00}, + {R367TER_IIRCX_COEFF5_MSB, 0x36}, + {R367TER_IIRCX_COEFF5_LSB, 0x42}, + {R367TER_FEPATH_CFG, 0x00}, + {R367TER_PMC1_FUNC, 0x65}, + {R367TER_PMC1_FOR, 0x00}, + {R367TER_PMC2_FUNC, 0x00}, + {R367TER_STATUS_ERR_DA, 0xe0}, + {R367TER_DIG_AGC_R, 0xfe}, + {R367TER_COMAGC_TARMSB, 0x0b}, + {R367TER_COM_AGC_TAR_ENMODE, 0x41}, + {R367TER_COM_AGC_CFG, 0x3e}, + {R367TER_COM_AGC_GAIN1, 0x39}, + {R367TER_AUT_AGC_TARGETMSB, 0x0b}, + {R367TER_LOCK_DET_MSB, 0x01}, + {R367TER_AGCTAR_LOCK_LSBS, 0x40}, + {R367TER_AUT_GAIN_EN, 0xf4}, + {R367TER_AUT_CFG, 0xf0}, + {R367TER_LOCKN, 0x23}, + {R367TER_INT_X_3, 0x00}, + {R367TER_INT_X_2, 0x03}, + {R367TER_INT_X_1, 0x8d}, + {R367TER_INT_X_0, 0xa0}, + {R367TER_MIN_ERRX_MSB, 0x00}, + {R367TER_COR_CTL, 0x00}, + {R367TER_COR_STAT, 0xf6}, + {R367TER_COR_INTEN, 0x00}, + {R367TER_COR_INTSTAT, 0x3f}, + {R367TER_COR_MODEGUARD, 0x03}, + {R367TER_AGC_CTL, 0x08}, + {R367TER_AGC_MANUAL1, 0x00}, + {R367TER_AGC_MANUAL2, 0x00}, + {R367TER_AGC_TARG, 0x16}, + {R367TER_AGC_GAIN1, 0x53}, + {R367TER_AGC_GAIN2, 0x1d}, + {R367TER_RESERVED_1, 0x00}, + {R367TER_RESERVED_2, 0x00}, + {R367TER_RESERVED_3, 0x00}, + {R367TER_CAS_CTL, 0x44}, + {R367TER_CAS_FREQ, 0xb3}, + {R367TER_CAS_DAGCGAIN, 0x12}, + {R367TER_SYR_CTL, 0x04}, + {R367TER_SYR_STAT, 0x10}, + {R367TER_SYR_NCO1, 0x00}, + {R367TER_SYR_NCO2, 0x00}, + {R367TER_SYR_OFFSET1, 0x00}, + {R367TER_SYR_OFFSET2, 0x00}, + {R367TER_FFT_CTL, 0x00}, + {R367TER_SCR_CTL, 0x70}, + {R367TER_PPM_CTL1, 0xf8}, + {R367TER_TRL_CTL, 0xac}, + {R367TER_TRL_NOMRATE1, 0x1e}, + {R367TER_TRL_NOMRATE2, 0x58}, + {R367TER_TRL_TIME1, 0x1d}, + {R367TER_TRL_TIME2, 0xfc}, + {R367TER_CRL_CTL, 0x24}, + {R367TER_CRL_FREQ1, 0xad}, + {R367TER_CRL_FREQ2, 0x9d}, + {R367TER_CRL_FREQ3, 0xff}, + {R367TER_CHC_CTL, 0x01}, + {R367TER_CHC_SNR, 0xf0}, + {R367TER_BDI_CTL, 0x00}, + {R367TER_DMP_CTL, 0x00}, + {R367TER_TPS_RCVD1, 0x30}, + {R367TER_TPS_RCVD2, 0x02}, + {R367TER_TPS_RCVD3, 0x01}, + {R367TER_TPS_RCVD4, 0x00}, + {R367TER_TPS_ID_CELL1, 0x00}, + {R367TER_TPS_ID_CELL2, 0x00}, + {R367TER_TPS_RCVD5_SET1, 0x02}, + {R367TER_TPS_SET2, 0x02}, + {R367TER_TPS_SET3, 0x01}, + {R367TER_TPS_CTL, 0x00}, + {R367TER_CTL_FFTOSNUM, 0x34}, + {R367TER_TESTSELECT, 0x09}, + {R367TER_MSC_REV, 0x0a}, + {R367TER_PIR_CTL, 0x00}, + {R367TER_SNR_CARRIER1, 0xa1}, + {R367TER_SNR_CARRIER2, 0x9a}, + {R367TER_PPM_CPAMP, 0x2c}, + {R367TER_TSM_AP0, 0x00}, + {R367TER_TSM_AP1, 0x00}, + {R367TER_TSM_AP2, 0x00}, + {R367TER_TSM_AP3, 0x00}, + {R367TER_TSM_AP4, 0x00}, + {R367TER_TSM_AP5, 0x00}, + {R367TER_TSM_AP6, 0x00}, + {R367TER_TSM_AP7, 0x00}, + {R367TER_CONSTMODE, 0x01}, + {R367TER_CONSTCARR1, 0x00}, + {R367TER_CONSTCARR2, 0x00}, + {R367TER_ICONSTEL, 0x0a}, + {R367TER_QCONSTEL, 0x15}, + {R367TER_TSTBISTRES0, 0x00}, + {R367TER_TSTBISTRES1, 0x00}, + {R367TER_TSTBISTRES2, 0x28}, + {R367TER_TSTBISTRES3, 0x00}, + {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00}, + {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00}, + {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00}, + {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00}, + {R367TER_SYR_FLAG, 0x00}, + {R367TER_CRL_TARGET1, 0x00}, + {R367TER_CRL_TARGET2, 0x00}, + {R367TER_CRL_TARGET3, 0x00}, + {R367TER_CRL_TARGET4, 0x00}, + {R367TER_CRL_FLAG, 0x00}, + {R367TER_TRL_TARGET1, 0x00}, + {R367TER_TRL_TARGET2, 0x00}, + {R367TER_TRL_CHC, 0x00}, + {R367TER_CHC_SNR_TARG, 0x00}, + {R367TER_TOP_TRACK, 0x00}, + {R367TER_TRACKER_FREE1, 0x00}, + {R367TER_ERROR_CRL1, 0x00}, + {R367TER_ERROR_CRL2, 0x00}, + {R367TER_ERROR_CRL3, 0x00}, + {R367TER_ERROR_CRL4, 0x00}, + {R367TER_DEC_NCO1, 0x2c}, + {R367TER_DEC_NCO2, 0x0f}, + {R367TER_DEC_NCO3, 0x20}, + {R367TER_SNR, 0xf1}, + {R367TER_SYR_FFTADJ1, 0x00}, + {R367TER_SYR_FFTADJ2, 0x00}, + {R367TER_SYR_CHCADJ1, 0x00}, + {R367TER_SYR_CHCADJ2, 0x00}, + {R367TER_SYR_OFF, 0x00}, + {R367TER_PPM_OFFSET1, 0x00}, + {R367TER_PPM_OFFSET2, 0x03}, + {R367TER_TRACKER_FREE2, 0x00}, + {R367TER_DEBG_LT10, 0x00}, + {R367TER_DEBG_LT11, 0x00}, + {R367TER_DEBG_LT12, 0x00}, + {R367TER_DEBG_LT13, 0x00}, + {R367TER_DEBG_LT14, 0x00}, + {R367TER_DEBG_LT15, 0x00}, + {R367TER_DEBG_LT16, 0x00}, + {R367TER_DEBG_LT17, 0x00}, + {R367TER_DEBG_LT18, 0x00}, + {R367TER_DEBG_LT19, 0x00}, + {R367TER_DEBG_LT1A, 0x00}, + {R367TER_DEBG_LT1B, 0x00}, + {R367TER_DEBG_LT1C, 0x00}, + {R367TER_DEBG_LT1D, 0x00}, + {R367TER_DEBG_LT1E, 0x00}, + {R367TER_DEBG_LT1F, 0x00}, + {R367TER_RCCFGH, 0x00}, + {R367TER_RCCFGM, 0x00}, + {R367TER_RCCFGL, 0x00}, + {R367TER_RCINSDELH, 0x00}, + {R367TER_RCINSDELM, 0x00}, + {R367TER_RCINSDELL, 0x00}, + {R367TER_RCSTATUS, 0x00}, + {R367TER_RCSPEED, 0x6f}, + {R367TER_RCDEBUGM, 0xe7}, + {R367TER_RCDEBUGL, 0x9b}, + {R367TER_RCOBSCFG, 0x00}, + {R367TER_RCOBSM, 0x00}, + {R367TER_RCOBSL, 0x00}, + {R367TER_RCFECSPY, 0x00}, + {R367TER_RCFSPYCFG, 0x00}, + {R367TER_RCFSPYDATA, 0x00}, + {R367TER_RCFSPYOUT, 0x00}, + {R367TER_RCFSTATUS, 0x00}, + {R367TER_RCFGOODPACK, 0x00}, + {R367TER_RCFPACKCNT, 0x00}, + {R367TER_RCFSPYMISC, 0x00}, + {R367TER_RCFBERCPT4, 0x00}, + {R367TER_RCFBERCPT3, 0x00}, + {R367TER_RCFBERCPT2, 0x00}, + {R367TER_RCFBERCPT1, 0x00}, + {R367TER_RCFBERCPT0, 0x00}, + {R367TER_RCFBERERR2, 0x00}, + {R367TER_RCFBERERR1, 0x00}, + {R367TER_RCFBERERR0, 0x00}, + {R367TER_RCFSTATESM, 0x00}, + {R367TER_RCFSTATESL, 0x00}, + {R367TER_RCFSPYBER, 0x00}, + {R367TER_RCFSPYDISTM, 0x00}, + {R367TER_RCFSPYDISTL, 0x00}, + {R367TER_RCFSPYOBS7, 0x00}, + {R367TER_RCFSPYOBS6, 0x00}, + {R367TER_RCFSPYOBS5, 0x00}, + {R367TER_RCFSPYOBS4, 0x00}, + {R367TER_RCFSPYOBS3, 0x00}, + {R367TER_RCFSPYOBS2, 0x00}, + {R367TER_RCFSPYOBS1, 0x00}, + {R367TER_RCFSPYOBS0, 0x00}, + {R367TER_FECM, 0x01}, + {R367TER_VTH12, 0xff}, + {R367TER_VTH23, 0xa1}, + {R367TER_VTH34, 0x64}, + {R367TER_VTH56, 0x40}, + {R367TER_VTH67, 0x00}, + {R367TER_VTH78, 0x2c}, + {R367TER_VITCURPUN, 0x12}, + {R367TER_VERROR, 0x01}, + {R367TER_PRVIT, 0x3f}, + {R367TER_VAVSRVIT, 0x00}, + {R367TER_VSTATUSVIT, 0xbd}, + {R367TER_VTHINUSE, 0xa1}, + {R367TER_KDIV12, 0x20}, + {R367TER_KDIV23, 0x40}, + {R367TER_KDIV34, 0x20}, + {R367TER_KDIV56, 0x30}, + {R367TER_KDIV67, 0x00}, + {R367TER_KDIV78, 0x30}, + {R367TER_SIGPOWER, 0x54}, + {R367TER_DEMAPVIT, 0x40}, + {R367TER_VITSCALE, 0x00}, + {R367TER_FFEC1PRG, 0x00}, + {R367TER_FVITCURPUN, 0x12}, + {R367TER_FVERROR, 0x01}, + {R367TER_FVSTATUSVIT, 0xbd}, + {R367TER_DEBUG_LT1, 0x00}, + {R367TER_DEBUG_LT2, 0x00}, + {R367TER_DEBUG_LT3, 0x00}, + {R367TER_TSTSFMET, 0x00}, + {R367TER_SELOUT, 0x00}, + {R367TER_TSYNC, 0x00}, + {R367TER_TSTERR, 0x00}, + {R367TER_TSFSYNC, 0x00}, + {R367TER_TSTSFERR, 0x00}, + {R367TER_TSTTSSF1, 0x01}, + {R367TER_TSTTSSF2, 0x1f}, + {R367TER_TSTTSSF3, 0x00}, + {R367TER_TSTTS1, 0x00}, + {R367TER_TSTTS2, 0x1f}, + {R367TER_TSTTS3, 0x01}, + {R367TER_TSTTS4, 0x00}, + {R367TER_TSTTSRC, 0x00}, + {R367TER_TSTTSRS, 0x00}, + {R367TER_TSSTATEM, 0xb0}, + {R367TER_TSSTATEL, 0x40}, + {R367TER_TSCFGH, 0x80}, + {R367TER_TSCFGM, 0x00}, + {R367TER_TSCFGL, 0x20}, + {R367TER_TSSYNC, 0x00}, + {R367TER_TSINSDELH, 0x00}, + {R367TER_TSINSDELM, 0x00}, + {R367TER_TSINSDELL, 0x00}, + {R367TER_TSDIVN, 0x03}, + {R367TER_TSDIVPM, 0x00}, + {R367TER_TSDIVPL, 0x00}, + {R367TER_TSDIVQM, 0x00}, + {R367TER_TSDIVQL, 0x00}, + {R367TER_TSDILSTKM, 0x00}, + {R367TER_TSDILSTKL, 0x00}, + {R367TER_TSSPEED, 0x6f}, + {R367TER_TSSTATUS, 0x81}, + {R367TER_TSSTATUS2, 0x6a}, + {R367TER_TSBITRATEM, 0x0f}, + {R367TER_TSBITRATEL, 0xc6}, + {R367TER_TSPACKLENM, 0x00}, + {R367TER_TSPACKLENL, 0xfc}, + {R367TER_TSBLOCLENM, 0x0a}, + {R367TER_TSBLOCLENL, 0x80}, + {R367TER_TSDLYH, 0x90}, + {R367TER_TSDLYM, 0x68}, + {R367TER_TSDLYL, 0x01}, + {R367TER_TSNPDAV, 0x00}, + {R367TER_TSBUFSTATH, 0x00}, + {R367TER_TSBUFSTATM, 0x00}, + {R367TER_TSBUFSTATL, 0x00}, + {R367TER_TSDEBUGM, 0xcf}, + {R367TER_TSDEBUGL, 0x1e}, + {R367TER_TSDLYSETH, 0x00}, + {R367TER_TSDLYSETM, 0x68}, + {R367TER_TSDLYSETL, 0x00}, + {R367TER_TSOBSCFG, 0x00}, + {R367TER_TSOBSM, 0x47}, + {R367TER_TSOBSL, 0x1f}, + {R367TER_ERRCTRL1, 0x95}, + {R367TER_ERRCNT1H, 0x80}, + {R367TER_ERRCNT1M, 0x00}, + {R367TER_ERRCNT1L, 0x00}, + {R367TER_ERRCTRL2, 0x95}, + {R367TER_ERRCNT2H, 0x00}, + {R367TER_ERRCNT2M, 0x00}, + {R367TER_ERRCNT2L, 0x00}, + {R367TER_FECSPY, 0x88}, + {R367TER_FSPYCFG, 0x2c}, + {R367TER_FSPYDATA, 0x3a}, + {R367TER_FSPYOUT, 0x06}, + {R367TER_FSTATUS, 0x61}, + {R367TER_FGOODPACK, 0xff}, + {R367TER_FPACKCNT, 0xff}, + {R367TER_FSPYMISC, 0x66}, + {R367TER_FBERCPT4, 0x00}, + {R367TER_FBERCPT3, 0x00}, + {R367TER_FBERCPT2, 0x36}, + {R367TER_FBERCPT1, 0x36}, + {R367TER_FBERCPT0, 0x14}, + {R367TER_FBERERR2, 0x00}, + {R367TER_FBERERR1, 0x03}, + {R367TER_FBERERR0, 0x28}, + {R367TER_FSTATESM, 0x00}, + {R367TER_FSTATESL, 0x02}, + {R367TER_FSPYBER, 0x00}, + {R367TER_FSPYDISTM, 0x01}, + {R367TER_FSPYDISTL, 0x9f}, + {R367TER_FSPYOBS7, 0xc9}, + {R367TER_FSPYOBS6, 0x99}, + {R367TER_FSPYOBS5, 0x08}, + {R367TER_FSPYOBS4, 0xec}, + {R367TER_FSPYOBS3, 0x01}, + {R367TER_FSPYOBS2, 0x0f}, + {R367TER_FSPYOBS1, 0xf5}, + {R367TER_FSPYOBS0, 0x08}, + {R367TER_SFDEMAP, 0x40}, + {R367TER_SFERROR, 0x00}, + {R367TER_SFAVSR, 0x30}, + {R367TER_SFECSTATUS, 0xcc}, + {R367TER_SFKDIV12, 0x20}, + {R367TER_SFKDIV23, 0x40}, + {R367TER_SFKDIV34, 0x20}, + {R367TER_SFKDIV56, 0x20}, + {R367TER_SFKDIV67, 0x00}, + {R367TER_SFKDIV78, 0x20}, + {R367TER_SFDILSTKM, 0x00}, + {R367TER_SFDILSTKL, 0x00}, + {R367TER_SFSTATUS, 0xb5}, + {R367TER_SFDLYH, 0x90}, + {R367TER_SFDLYM, 0x60}, + {R367TER_SFDLYL, 0x01}, + {R367TER_SFDLYSETH, 0xc0}, + {R367TER_SFDLYSETM, 0x60}, + {R367TER_SFDLYSETL, 0x00}, + {R367TER_SFOBSCFG, 0x00}, + {R367TER_SFOBSM, 0x47}, + {R367TER_SFOBSL, 0x05}, + {R367TER_SFECINFO, 0x40}, + {R367TER_SFERRCTRL, 0x74}, + {R367TER_SFERRCNTH, 0x80}, + {R367TER_SFERRCNTM, 0x00}, + {R367TER_SFERRCNTL, 0x00}, + {R367TER_SYMBRATEM, 0x2f}, + {R367TER_SYMBRATEL, 0x50}, + {R367TER_SYMBSTATUS, 0x7f}, + {R367TER_SYMBCFG, 0x00}, + {R367TER_SYMBFIFOM, 0xf4}, + {R367TER_SYMBFIFOL, 0x0d}, + {R367TER_SYMBOFFSM, 0xf0}, + {R367TER_SYMBOFFSL, 0x2d}, + {0x0000, 0x00} /* EOT */ +}; + +static const struct st_register def0367dd_qam[] = { + {R367CAB_CTRL_1, 0x06}, /* Original 0x04 */ + {R367CAB_CTRL_2, 0x03}, + {R367CAB_IT_STATUS1, 0x2b}, + {R367CAB_IT_STATUS2, 0x08}, + {R367CAB_IT_EN1, 0x00}, + {R367CAB_IT_EN2, 0x00}, + {R367CAB_CTRL_STATUS, 0x04}, + {R367CAB_TEST_CTL, 0x00}, + {R367CAB_AGC_CTL, 0x73}, + {R367CAB_AGC_IF_CFG, 0x50}, + {R367CAB_AGC_RF_CFG, 0x02}, /* RF Freeze */ + {R367CAB_AGC_PWM_CFG, 0x03}, + {R367CAB_AGC_PWR_REF_L, 0x5a}, + {R367CAB_AGC_PWR_REF_H, 0x00}, + {R367CAB_AGC_RF_TH_L, 0xff}, + {R367CAB_AGC_RF_TH_H, 0x07}, + {R367CAB_AGC_IF_LTH_L, 0x00}, + {R367CAB_AGC_IF_LTH_H, 0x08}, + {R367CAB_AGC_IF_HTH_L, 0xff}, + {R367CAB_AGC_IF_HTH_H, 0x07}, + {R367CAB_AGC_PWR_RD_L, 0xa0}, + {R367CAB_AGC_PWR_RD_M, 0xe9}, + {R367CAB_AGC_PWR_RD_H, 0x03}, + {R367CAB_AGC_PWM_IFCMD_L, 0xe4}, + {R367CAB_AGC_PWM_IFCMD_H, 0x00}, + {R367CAB_AGC_PWM_RFCMD_L, 0xff}, + {R367CAB_AGC_PWM_RFCMD_H, 0x07}, + {R367CAB_IQDEM_CFG, 0x01}, + {R367CAB_MIX_NCO_LL, 0x22}, + {R367CAB_MIX_NCO_HL, 0x96}, + {R367CAB_MIX_NCO_HH, 0x55}, + {R367CAB_SRC_NCO_LL, 0xff}, + {R367CAB_SRC_NCO_LH, 0x0c}, + {R367CAB_SRC_NCO_HL, 0xf5}, + {R367CAB_SRC_NCO_HH, 0x20}, + {R367CAB_IQDEM_GAIN_SRC_L, 0x06}, + {R367CAB_IQDEM_GAIN_SRC_H, 0x01}, + {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe}, + {R367CAB_IQDEM_DCRM_CFG_LH, 0xff}, + {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f}, + {R367CAB_IQDEM_DCRM_CFG_HH, 0x00}, + {R367CAB_IQDEM_ADJ_COEFF0, 0x34}, + {R367CAB_IQDEM_ADJ_COEFF1, 0xae}, + {R367CAB_IQDEM_ADJ_COEFF2, 0x46}, + {R367CAB_IQDEM_ADJ_COEFF3, 0x77}, + {R367CAB_IQDEM_ADJ_COEFF4, 0x96}, + {R367CAB_IQDEM_ADJ_COEFF5, 0x69}, + {R367CAB_IQDEM_ADJ_COEFF6, 0xc7}, + {R367CAB_IQDEM_ADJ_COEFF7, 0x01}, + {R367CAB_IQDEM_ADJ_EN, 0x04}, + {R367CAB_IQDEM_ADJ_AGC_REF, 0x94}, + {R367CAB_ALLPASSFILT1, 0xc9}, + {R367CAB_ALLPASSFILT2, 0x2d}, + {R367CAB_ALLPASSFILT3, 0xa3}, + {R367CAB_ALLPASSFILT4, 0xfb}, + {R367CAB_ALLPASSFILT5, 0xf6}, + {R367CAB_ALLPASSFILT6, 0x45}, + {R367CAB_ALLPASSFILT7, 0x6f}, + {R367CAB_ALLPASSFILT8, 0x7e}, + {R367CAB_ALLPASSFILT9, 0x05}, + {R367CAB_ALLPASSFILT10, 0x0a}, + {R367CAB_ALLPASSFILT11, 0x51}, + {R367CAB_TRL_AGC_CFG, 0x20}, + {R367CAB_TRL_LPF_CFG, 0x28}, + {R367CAB_TRL_LPF_ACQ_GAIN, 0x44}, + {R367CAB_TRL_LPF_TRK_GAIN, 0x22}, + {R367CAB_TRL_LPF_OUT_GAIN, 0x03}, + {R367CAB_TRL_LOCKDET_LTH, 0x04}, + {R367CAB_TRL_LOCKDET_HTH, 0x11}, + {R367CAB_TRL_LOCKDET_TRGVAL, 0x20}, + {R367CAB_IQ_QAM, 0x01}, + {R367CAB_FSM_STATE, 0xa0}, + {R367CAB_FSM_CTL, 0x08}, + {R367CAB_FSM_STS, 0x0c}, + {R367CAB_FSM_SNR0_HTH, 0x00}, + {R367CAB_FSM_SNR1_HTH, 0x00}, + {R367CAB_FSM_SNR2_HTH, 0x00}, + {R367CAB_FSM_SNR0_LTH, 0x00}, + {R367CAB_FSM_SNR1_LTH, 0x00}, + {R367CAB_FSM_EQA1_HTH, 0x00}, + {R367CAB_FSM_TEMPO, 0x32}, + {R367CAB_FSM_CONFIG, 0x03}, + {R367CAB_EQU_I_TESTTAP_L, 0x11}, + {R367CAB_EQU_I_TESTTAP_M, 0x00}, + {R367CAB_EQU_I_TESTTAP_H, 0x00}, + {R367CAB_EQU_TESTAP_CFG, 0x00}, + {R367CAB_EQU_Q_TESTTAP_L, 0xff}, + {R367CAB_EQU_Q_TESTTAP_M, 0x00}, + {R367CAB_EQU_Q_TESTTAP_H, 0x00}, + {R367CAB_EQU_TAP_CTRL, 0x00}, + {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11}, + {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05}, + {R367CAB_EQU_CTR_HIPOW_L, 0x00}, + {R367CAB_EQU_CTR_HIPOW_H, 0x00}, + {R367CAB_EQU_I_EQU_LO, 0xef}, + {R367CAB_EQU_I_EQU_HI, 0x00}, + {R367CAB_EQU_Q_EQU_LO, 0xee}, + {R367CAB_EQU_Q_EQU_HI, 0x00}, + {R367CAB_EQU_MAPPER, 0xc5}, + {R367CAB_EQU_SWEEP_RATE, 0x80}, + {R367CAB_EQU_SNR_LO, 0x64}, + {R367CAB_EQU_SNR_HI, 0x03}, + {R367CAB_EQU_GAMMA_LO, 0x00}, + {R367CAB_EQU_GAMMA_HI, 0x00}, + {R367CAB_EQU_ERR_GAIN, 0x36}, + {R367CAB_EQU_RADIUS, 0xaa}, + {R367CAB_EQU_FFE_MAINTAP, 0x00}, + {R367CAB_EQU_FFE_LEAKAGE, 0x63}, + {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf}, + {R367CAB_EQU_GAIN_WIDE, 0x88}, + {R367CAB_EQU_GAIN_NARROW, 0x41}, + {R367CAB_EQU_CTR_LPF_GAIN, 0xd1}, + {R367CAB_EQU_CRL_LPF_GAIN, 0xa7}, + {R367CAB_EQU_GLOBAL_GAIN, 0x06}, + {R367CAB_EQU_CRL_LD_SEN, 0x85}, + {R367CAB_EQU_CRL_LD_VAL, 0xe2}, + {R367CAB_EQU_CRL_TFR, 0x20}, + {R367CAB_EQU_CRL_BISTH_LO, 0x00}, + {R367CAB_EQU_CRL_BISTH_HI, 0x00}, + {R367CAB_EQU_SWEEP_RANGE_LO, 0x00}, + {R367CAB_EQU_SWEEP_RANGE_HI, 0x00}, + {R367CAB_EQU_CRL_LIMITER, 0x40}, + {R367CAB_EQU_MODULUS_MAP, 0x90}, + {R367CAB_EQU_PNT_GAIN, 0xa7}, + {R367CAB_FEC_AC_CTR_0, 0x16}, + {R367CAB_FEC_AC_CTR_1, 0x0b}, + {R367CAB_FEC_AC_CTR_2, 0x88}, + {R367CAB_FEC_AC_CTR_3, 0x02}, + {R367CAB_FEC_STATUS, 0x12}, + {R367CAB_RS_COUNTER_0, 0x7d}, + {R367CAB_RS_COUNTER_1, 0xd0}, + {R367CAB_RS_COUNTER_2, 0x19}, + {R367CAB_RS_COUNTER_3, 0x0b}, + {R367CAB_RS_COUNTER_4, 0xa3}, + {R367CAB_RS_COUNTER_5, 0x00}, + {R367CAB_BERT_0, 0x01}, + {R367CAB_BERT_1, 0x25}, + {R367CAB_BERT_2, 0x41}, + {R367CAB_BERT_3, 0x39}, + {R367CAB_OUTFORMAT_0, 0xc2}, + {R367CAB_OUTFORMAT_1, 0x22}, + {R367CAB_SMOOTHER_2, 0x28}, + {R367CAB_TSMF_CTRL_0, 0x01}, + {R367CAB_TSMF_CTRL_1, 0xc6}, + {R367CAB_TSMF_CTRL_3, 0x43}, + {R367CAB_TS_ON_ID_0, 0x00}, + {R367CAB_TS_ON_ID_1, 0x00}, + {R367CAB_TS_ON_ID_2, 0x00}, + {R367CAB_TS_ON_ID_3, 0x00}, + {R367CAB_RE_STATUS_0, 0x00}, + {R367CAB_RE_STATUS_1, 0x00}, + {R367CAB_RE_STATUS_2, 0x00}, + {R367CAB_RE_STATUS_3, 0x00}, + {R367CAB_TS_STATUS_0, 0x00}, + {R367CAB_TS_STATUS_1, 0x00}, + {R367CAB_TS_STATUS_2, 0xa0}, + {R367CAB_TS_STATUS_3, 0x00}, + {R367CAB_T_O_ID_0, 0x00}, + {R367CAB_T_O_ID_1, 0x00}, + {R367CAB_T_O_ID_2, 0x00}, + {R367CAB_T_O_ID_3, 0x00}, + {0x0000, 0x00} /* EOT */ +}; + +static const struct st_register def0367dd_base[] = { + {R367TER_IOCFG0, 0x80}, + {R367TER_DAC0R, 0x00}, + {R367TER_IOCFG1, 0x00}, + {R367TER_DAC1R, 0x00}, + {R367TER_IOCFG2, 0x00}, + {R367TER_SDFR, 0x00}, + {R367TER_AUX_CLK, 0x00}, + {R367TER_FREESYS1, 0x00}, + {R367TER_FREESYS2, 0x00}, + {R367TER_FREESYS3, 0x00}, + {R367TER_GPIO_CFG, 0x55}, + {R367TER_GPIO_CMD, 0x01}, + {R367TER_TSTRES, 0x00}, + {R367TER_ANACTRL, 0x00}, + {R367TER_TSTBUS, 0x00}, + {R367TER_RF_AGC2, 0x20}, + {R367TER_ANADIGCTRL, 0x0b}, + {R367TER_PLLMDIV, 0x01}, + {R367TER_PLLNDIV, 0x08}, + {R367TER_PLLSETUP, 0x18}, + {R367TER_DUAL_AD12, 0x04}, + {R367TER_TSTBIST, 0x00}, + {0x0000, 0x00} /* EOT */ +}; + +/* + * Tables combined + */ + +static const struct +st_register *stv0367_deftabs[STV0367_DEFTAB_MAX][STV0367_TAB_MAX] = { + /* generic default/init tabs */ + { def0367ter, def0367cab, NULL }, + /* default tabs for digital devices cards/flex modules */ + { def0367dd_ofdm, def0367dd_qam, def0367dd_base }, +}; + +#endif diff --git a/drivers/media/dvb-frontends/stv0367_priv.h b/drivers/media/dvb-frontends/stv0367_priv.h new file mode 100644 index 0000000000..617f605947 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0367_priv.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0367_priv.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru> + */ +/* Common driver error constants */ + +#ifndef STV0367_PRIV_H +#define STV0367_PRIV_H + +#ifndef TRUE + #define TRUE (1 == 1) +#endif +#ifndef FALSE + #define FALSE (!TRUE) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +/* MACRO definitions */ +#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) +#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) +#define INRANGE(X, Y, Z) \ + ((((X) <= (Y)) && ((Y) <= (Z))) || \ + (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) + +#ifndef MAKEWORD +#define MAKEWORD(X, Y) (((X) << 8) + (Y)) +#endif + +#define LSB(X) (((X) & 0xff)) +#define MSB(Y) (((Y) >> 8) & 0xff) +#define MMSB(Y)(((Y) >> 16) & 0xff) + +enum stv0367_ter_signal_type { + FE_TER_NOAGC = 0, + FE_TER_AGCOK = 5, + FE_TER_NOTPS = 6, + FE_TER_TPSOK = 7, + FE_TER_NOSYMBOL = 8, + FE_TER_BAD_CPQ = 9, + FE_TER_PRFOUNDOK = 10, + FE_TER_NOPRFOUND = 11, + FE_TER_LOCKOK = 12, + FE_TER_NOLOCK = 13, + FE_TER_SYMBOLOK = 15, + FE_TER_CPAMPOK = 16, + FE_TER_NOCPAMP = 17, + FE_TER_SWNOK = 18 +}; + +enum stv0367_ts_mode { + STV0367_OUTPUTMODE_DEFAULT, + STV0367_SERIAL_PUNCT_CLOCK, + STV0367_SERIAL_CONT_CLOCK, + STV0367_PARALLEL_PUNCT_CLOCK, + STV0367_DVBCI_CLOCK +}; + +enum stv0367_clk_pol { + STV0367_CLOCKPOLARITY_DEFAULT, + STV0367_RISINGEDGE_CLOCK, + STV0367_FALLINGEDGE_CLOCK +}; + +enum stv0367_ter_bw { + FE_TER_CHAN_BW_6M = 6, + FE_TER_CHAN_BW_7M = 7, + FE_TER_CHAN_BW_8M = 8 +}; + +#if 0 +enum FE_TER_Rate_TPS { + FE_TER_TPS_1_2 = 0, + FE_TER_TPS_2_3 = 1, + FE_TER_TPS_3_4 = 2, + FE_TER_TPS_5_6 = 3, + FE_TER_TPS_7_8 = 4 +}; +#endif + +enum stv0367_ter_mode { + FE_TER_MODE_2K, + FE_TER_MODE_8K, + FE_TER_MODE_4K +}; +#if 0 +enum FE_TER_Hierarchy_Alpha { + FE_TER_HIER_ALPHA_NONE, /* Regular modulation */ + FE_TER_HIER_ALPHA_1, /* Hierarchical modulation a = 1*/ + FE_TER_HIER_ALPHA_2, /* Hierarchical modulation a = 2*/ + FE_TER_HIER_ALPHA_4 /* Hierarchical modulation a = 4*/ +}; +#endif +enum stv0367_ter_hierarchy { + FE_TER_HIER_NONE, /*Hierarchy None*/ + FE_TER_HIER_LOW_PRIO, /*Hierarchy : Low Priority*/ + FE_TER_HIER_HIGH_PRIO, /*Hierarchy : High Priority*/ + FE_TER_HIER_PRIO_ANY /*Hierarchy :Any*/ +}; + +#if 0 +enum fe_stv0367_ter_spec { + FE_TER_INVERSION_NONE = 0, + FE_TER_INVERSION = 1, + FE_TER_INVERSION_AUTO = 2, + FE_TER_INVERSION_UNK = 4 +}; +#endif + +enum stv0367_ter_if_iq_mode { + FE_TER_NORMAL_IF_TUNER = 0, + FE_TER_LONGPATH_IF_TUNER = 1, + FE_TER_IQ_TUNER = 2 + +}; + +#if 0 +enum FE_TER_FECRate { + FE_TER_FEC_NONE = 0x00, /* no FEC rate specified */ + FE_TER_FEC_ALL = 0xFF, /* Logical OR of all FECs */ + FE_TER_FEC_1_2 = 1, + FE_TER_FEC_2_3 = (1 << 1), + FE_TER_FEC_3_4 = (1 << 2), + FE_TER_FEC_4_5 = (1 << 3), + FE_TER_FEC_5_6 = (1 << 4), + FE_TER_FEC_6_7 = (1 << 5), + FE_TER_FEC_7_8 = (1 << 6), + FE_TER_FEC_8_9 = (1 << 7) +}; + +enum FE_TER_Rate { + FE_TER_FE_1_2 = 0, + FE_TER_FE_2_3 = 1, + FE_TER_FE_3_4 = 2, + FE_TER_FE_5_6 = 3, + FE_TER_FE_6_7 = 4, + FE_TER_FE_7_8 = 5 +}; +#endif + +enum stv0367_ter_force { + FE_TER_FORCENONE = 0, + FE_TER_FORCE_M_G = 1 +}; + +enum stv0367cab_mod { + FE_CAB_MOD_QAM4, + FE_CAB_MOD_QAM16, + FE_CAB_MOD_QAM32, + FE_CAB_MOD_QAM64, + FE_CAB_MOD_QAM128, + FE_CAB_MOD_QAM256, + FE_CAB_MOD_QAM512, + FE_CAB_MOD_QAM1024 +}; +#if 0 +enum { + FE_CAB_FEC_A = 1, /* J83 Annex A */ + FE_CAB_FEC_B = (1 << 1),/* J83 Annex B */ + FE_CAB_FEC_C = (1 << 2) /* J83 Annex C */ +} FE_CAB_FECType_t; +#endif +struct stv0367_cab_signal_info { + int locked; + u32 frequency; /* kHz */ + u32 symbol_rate; /* Mbds */ + enum stv0367cab_mod modulation; + enum fe_spectral_inversion spect_inv; + s32 Power_dBmx10; /* Power of the RF signal (dBm x 10) */ + u32 CN_dBx10; /* Carrier to noise ratio (dB x 10) */ + u32 BER; /* Bit error rate (x 10000000) */ +}; + +enum stv0367_cab_signal_type { + FE_CAB_NOTUNER, + FE_CAB_NOAGC, + FE_CAB_NOSIGNAL, + FE_CAB_NOTIMING, + FE_CAB_TIMINGOK, + FE_CAB_NOCARRIER, + FE_CAB_CARRIEROK, + FE_CAB_NOBLIND, + FE_CAB_BLINDOK, + FE_CAB_NODEMOD, + FE_CAB_DEMODOK, + FE_CAB_DATAOK +}; + +#endif diff --git a/drivers/media/dvb-frontends/stv0367_regs.h b/drivers/media/dvb-frontends/stv0367_regs.h new file mode 100644 index 0000000000..821054e8bb --- /dev/null +++ b/drivers/media/dvb-frontends/stv0367_regs.h @@ -0,0 +1,3596 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0367_regs.h + * + * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0367_REGS_H +#define STV0367_REGS_H + +/* ID */ +#define R367TER_ID 0xf000 +#define F367TER_IDENTIFICATIONREG 0xf00000ff + +/* I2CRPT */ +#define R367TER_I2CRPT 0xf001 +#define F367TER_I2CT_ON 0xf0010080 +#define F367TER_ENARPT_LEVEL 0xf0010070 +#define F367TER_SCLT_DELAY 0xf0010008 +#define F367TER_SCLT_NOD 0xf0010004 +#define F367TER_STOP_ENABLE 0xf0010002 +#define F367TER_SDAT_NOD 0xf0010001 + +/* TOPCTRL */ +#define R367TER_TOPCTRL 0xf002 +#define F367TER_STDBY 0xf0020080 +#define F367TER_STDBY_FEC 0xf0020040 +#define F367TER_STDBY_CORE 0xf0020020 +#define F367TER_QAM_COFDM 0xf0020010 +#define F367TER_TS_DIS 0xf0020008 +#define F367TER_DIR_CLK_216 0xf0020004 +#define F367TER_TUNER_BB 0xf0020002 +#define F367TER_DVBT_H 0xf0020001 + +/* IOCFG0 */ +#define R367TER_IOCFG0 0xf003 +#define F367TER_OP0_SD 0xf0030080 +#define F367TER_OP0_VAL 0xf0030040 +#define F367TER_OP0_OD 0xf0030020 +#define F367TER_OP0_INV 0xf0030010 +#define F367TER_OP0_DACVALUE_HI 0xf003000f + +/* DAc0R */ +#define R367TER_DAC0R 0xf004 +#define F367TER_OP0_DACVALUE_LO 0xf00400ff + +/* IOCFG1 */ +#define R367TER_IOCFG1 0xf005 +#define F367TER_IP0 0xf0050040 +#define F367TER_OP1_OD 0xf0050020 +#define F367TER_OP1_INV 0xf0050010 +#define F367TER_OP1_DACVALUE_HI 0xf005000f + +/* DAC1R */ +#define R367TER_DAC1R 0xf006 +#define F367TER_OP1_DACVALUE_LO 0xf00600ff + +/* IOCFG2 */ +#define R367TER_IOCFG2 0xf007 +#define F367TER_OP2_LOCK_CONF 0xf00700e0 +#define F367TER_OP2_OD 0xf0070010 +#define F367TER_OP2_VAL 0xf0070008 +#define F367TER_OP1_LOCK_CONF 0xf0070007 + +/* SDFR */ +#define R367TER_SDFR 0xf008 +#define F367TER_OP0_FREQ 0xf00800f0 +#define F367TER_OP1_FREQ 0xf008000f + +/* STATUS */ +#define R367TER_STATUS 0xf009 +#define F367TER_TPS_LOCK 0xf0090080 +#define F367TER_SYR_LOCK 0xf0090040 +#define F367TER_AGC_LOCK 0xf0090020 +#define F367TER_PRF 0xf0090010 +#define F367TER_LK 0xf0090008 +#define F367TER_PR 0xf0090007 + +/* AUX_CLK */ +#define R367TER_AUX_CLK 0xf00a +#define F367TER_AUXFEC_CTL 0xf00a00c0 +#define F367TER_DIS_CKX4 0xf00a0020 +#define F367TER_CKSEL 0xf00a0018 +#define F367TER_CKDIV_PROG 0xf00a0006 +#define F367TER_AUXCLK_ENA 0xf00a0001 + +/* FREESYS1 */ +#define R367TER_FREESYS1 0xf00b +#define F367TER_FREE_SYS1 0xf00b00ff + +/* FREESYS2 */ +#define R367TER_FREESYS2 0xf00c +#define F367TER_FREE_SYS2 0xf00c00ff + +/* FREESYS3 */ +#define R367TER_FREESYS3 0xf00d +#define F367TER_FREE_SYS3 0xf00d00ff + +/* GPIO_CFG */ +#define R367TER_GPIO_CFG 0xf00e +#define F367TER_GPIO7_NOD 0xf00e0080 +#define F367TER_GPIO7_CFG 0xf00e0040 +#define F367TER_GPIO6_NOD 0xf00e0020 +#define F367TER_GPIO6_CFG 0xf00e0010 +#define F367TER_GPIO5_NOD 0xf00e0008 +#define F367TER_GPIO5_CFG 0xf00e0004 +#define F367TER_GPIO4_NOD 0xf00e0002 +#define F367TER_GPIO4_CFG 0xf00e0001 + +/* GPIO_CMD */ +#define R367TER_GPIO_CMD 0xf00f +#define F367TER_GPIO7_VAL 0xf00f0008 +#define F367TER_GPIO6_VAL 0xf00f0004 +#define F367TER_GPIO5_VAL 0xf00f0002 +#define F367TER_GPIO4_VAL 0xf00f0001 + +/* AGC2MAX */ +#define R367TER_AGC2MAX 0xf010 +#define F367TER_AGC2_MAX 0xf01000ff + +/* AGC2MIN */ +#define R367TER_AGC2MIN 0xf011 +#define F367TER_AGC2_MIN 0xf01100ff + +/* AGC1MAX */ +#define R367TER_AGC1MAX 0xf012 +#define F367TER_AGC1_MAX 0xf01200ff + +/* AGC1MIN */ +#define R367TER_AGC1MIN 0xf013 +#define F367TER_AGC1_MIN 0xf01300ff + +/* AGCR */ +#define R367TER_AGCR 0xf014 +#define F367TER_RATIO_A 0xf01400e0 +#define F367TER_RATIO_B 0xf0140018 +#define F367TER_RATIO_C 0xf0140007 + +/* AGC2TH */ +#define R367TER_AGC2TH 0xf015 +#define F367TER_AGC2_THRES 0xf01500ff + +/* AGC12c */ +#define R367TER_AGC12C 0xf016 +#define F367TER_AGC1_IV 0xf0160080 +#define F367TER_AGC1_OD 0xf0160040 +#define F367TER_AGC1_LOAD 0xf0160020 +#define F367TER_AGC2_IV 0xf0160010 +#define F367TER_AGC2_OD 0xf0160008 +#define F367TER_AGC2_LOAD 0xf0160004 +#define F367TER_AGC12_MODE 0xf0160003 + +/* AGCCTRL1 */ +#define R367TER_AGCCTRL1 0xf017 +#define F367TER_DAGC_ON 0xf0170080 +#define F367TER_INVERT_AGC12 0xf0170040 +#define F367TER_AGC1_MODE 0xf0170008 +#define F367TER_AGC2_MODE 0xf0170007 + +/* AGCCTRL2 */ +#define R367TER_AGCCTRL2 0xf018 +#define F367TER_FRZ2_CTRL 0xf0180060 +#define F367TER_FRZ1_CTRL 0xf0180018 +#define F367TER_TIME_CST 0xf0180007 + +/* AGC1VAL1 */ +#define R367TER_AGC1VAL1 0xf019 +#define F367TER_AGC1_VAL_LO 0xf01900ff + +/* AGC1VAL2 */ +#define R367TER_AGC1VAL2 0xf01a +#define F367TER_AGC1_VAL_HI 0xf01a000f + +/* AGC2VAL1 */ +#define R367TER_AGC2VAL1 0xf01b +#define F367TER_AGC2_VAL_LO 0xf01b00ff + +/* AGC2VAL2 */ +#define R367TER_AGC2VAL2 0xf01c +#define F367TER_AGC2_VAL_HI 0xf01c000f + +/* AGC2PGA */ +#define R367TER_AGC2PGA 0xf01d +#define F367TER_AGC2_PGA 0xf01d00ff + +/* OVF_RATE1 */ +#define R367TER_OVF_RATE1 0xf01e +#define F367TER_OVF_RATE_HI 0xf01e000f + +/* OVF_RATE2 */ +#define R367TER_OVF_RATE2 0xf01f +#define F367TER_OVF_RATE_LO 0xf01f00ff + +/* GAIN_SRC1 */ +#define R367TER_GAIN_SRC1 0xf020 +#define F367TER_INV_SPECTR 0xf0200080 +#define F367TER_IQ_INVERT 0xf0200040 +#define F367TER_INR_BYPASS 0xf0200020 +#define F367TER_STATUS_INV_SPECRUM 0xf0200010 +#define F367TER_GAIN_SRC_HI 0xf020000f + +/* GAIN_SRC2 */ +#define R367TER_GAIN_SRC2 0xf021 +#define F367TER_GAIN_SRC_LO 0xf02100ff + +/* INC_DEROT1 */ +#define R367TER_INC_DEROT1 0xf022 +#define F367TER_INC_DEROT_HI 0xf02200ff + +/* INC_DEROT2 */ +#define R367TER_INC_DEROT2 0xf023 +#define F367TER_INC_DEROT_LO 0xf02300ff + +/* PPM_CPAMP_DIR */ +#define R367TER_PPM_CPAMP_DIR 0xf024 +#define F367TER_PPM_CPAMP_DIRECT 0xf02400ff + +/* PPM_CPAMP_INV */ +#define R367TER_PPM_CPAMP_INV 0xf025 +#define F367TER_PPM_CPAMP_INVER 0xf02500ff + +/* FREESTFE_1 */ +#define R367TER_FREESTFE_1 0xf026 +#define F367TER_SYMBOL_NUMBER_INC 0xf02600c0 +#define F367TER_SEL_LSB 0xf0260004 +#define F367TER_AVERAGE_ON 0xf0260002 +#define F367TER_DC_ADJ 0xf0260001 + +/* FREESTFE_2 */ +#define R367TER_FREESTFE_2 0xf027 +#define F367TER_SEL_SRCOUT 0xf02700c0 +#define F367TER_SEL_SYRTHR 0xf027001f + +/* DCOFFSET */ +#define R367TER_DCOFFSET 0xf028 +#define F367TER_SELECT_I_Q 0xf0280080 +#define F367TER_DC_OFFSET 0xf028007f + +/* EN_PROCESS */ +#define R367TER_EN_PROCESS 0xf029 +#define F367TER_FREE 0xf02900f0 +#define F367TER_ENAB_MANUAL 0xf0290001 + +/* SDI_SMOOTHER */ +#define R367TER_SDI_SMOOTHER 0xf02a +#define F367TER_DIS_SMOOTH 0xf02a0080 +#define F367TER_SDI_INC_SMOOTHER 0xf02a007f + +/* FE_LOOP_OPEN */ +#define R367TER_FE_LOOP_OPEN 0xf02b +#define F367TER_TRL_LOOP_OP 0xf02b0002 +#define F367TER_CRL_LOOP_OP 0xf02b0001 + +/* FREQOFF1 */ +#define R367TER_FREQOFF1 0xf02c +#define F367TER_FREQ_OFFSET_LOOP_OPEN_VHI 0xf02c00ff + +/* FREQOFF2 */ +#define R367TER_FREQOFF2 0xf02d +#define F367TER_FREQ_OFFSET_LOOP_OPEN_HI 0xf02d00ff + +/* FREQOFF3 */ +#define R367TER_FREQOFF3 0xf02e +#define F367TER_FREQ_OFFSET_LOOP_OPEN_LO 0xf02e00ff + +/* TIMOFF1 */ +#define R367TER_TIMOFF1 0xf02f +#define F367TER_TIM_OFFSET_LOOP_OPEN_HI 0xf02f00ff + +/* TIMOFF2 */ +#define R367TER_TIMOFF2 0xf030 +#define F367TER_TIM_OFFSET_LOOP_OPEN_LO 0xf03000ff + +/* EPQ */ +#define R367TER_EPQ 0xf031 +#define F367TER_EPQ1 0xf03100ff + +/* EPQAUTO */ +#define R367TER_EPQAUTO 0xf032 +#define F367TER_EPQ2 0xf03200ff + +/* SYR_UPDATE */ +#define R367TER_SYR_UPDATE 0xf033 +#define F367TER_SYR_PROTV 0xf0330080 +#define F367TER_SYR_PROTV_GAIN 0xf0330060 +#define F367TER_SYR_FILTER 0xf0330010 +#define F367TER_SYR_TRACK_THRES 0xf033000c + +/* CHPFREE */ +#define R367TER_CHPFREE 0xf034 +#define F367TER_CHP_FREE 0xf03400ff + +/* PPM_STATE_MAC */ +#define R367TER_PPM_STATE_MAC 0xf035 +#define F367TER_PPM_STATE_MACHINE_DECODER 0xf035003f + +/* INR_THRESHOLD */ +#define R367TER_INR_THRESHOLD 0xf036 +#define F367TER_INR_THRESH 0xf03600ff + +/* EPQ_TPS_ID_CELL */ +#define R367TER_EPQ_TPS_ID_CELL 0xf037 +#define F367TER_ENABLE_LGTH_TO_CF 0xf0370080 +#define F367TER_DIS_TPS_RSVD 0xf0370040 +#define F367TER_DIS_BCH 0xf0370020 +#define F367TER_DIS_ID_CEL 0xf0370010 +#define F367TER_TPS_ADJUST_SYM 0xf037000f + +/* EPQ_CFG */ +#define R367TER_EPQ_CFG 0xf038 +#define F367TER_EPQ_RANGE 0xf0380002 +#define F367TER_EPQ_SOFT 0xf0380001 + +/* EPQ_STATUS */ +#define R367TER_EPQ_STATUS 0xf039 +#define F367TER_SLOPE_INC 0xf03900fc +#define F367TER_TPS_FIELD 0xf0390003 + +/* AUTORELOCK */ +#define R367TER_AUTORELOCK 0xf03a +#define F367TER_BYPASS_BER_TEMPO 0xf03a0080 +#define F367TER_BER_TEMPO 0xf03a0070 +#define F367TER_BYPASS_COFDM_TEMPO 0xf03a0008 +#define F367TER_COFDM_TEMPO 0xf03a0007 + +/* BER_THR_VMSB */ +#define R367TER_BER_THR_VMSB 0xf03b +#define F367TER_BER_THRESHOLD_HI 0xf03b00ff + +/* BER_THR_MSB */ +#define R367TER_BER_THR_MSB 0xf03c +#define F367TER_BER_THRESHOLD_MID 0xf03c00ff + +/* BER_THR_LSB */ +#define R367TER_BER_THR_LSB 0xf03d +#define F367TER_BER_THRESHOLD_LO 0xf03d00ff + +/* CCD */ +#define R367TER_CCD 0xf03e +#define F367TER_CCD_DETECTED 0xf03e0080 +#define F367TER_CCD_RESET 0xf03e0040 +#define F367TER_CCD_THRESHOLD 0xf03e000f + +/* SPECTR_CFG */ +#define R367TER_SPECTR_CFG 0xf03f +#define F367TER_SPECT_CFG 0xf03f0003 + +/* CONSTMU_MSB */ +#define R367TER_CONSTMU_MSB 0xf040 +#define F367TER_CONSTMU_FREEZE 0xf0400080 +#define F367TER_CONSTNU_FORCE_EN 0xf0400040 +#define F367TER_CONST_MU_MSB 0xf040003f + +/* CONSTMU_LSB */ +#define R367TER_CONSTMU_LSB 0xf041 +#define F367TER_CONST_MU_LSB 0xf04100ff + +/* CONSTMU_MAX_MSB */ +#define R367TER_CONSTMU_MAX_MSB 0xf042 +#define F367TER_CONST_MU_MAX_MSB 0xf042003f + +/* CONSTMU_MAX_LSB */ +#define R367TER_CONSTMU_MAX_LSB 0xf043 +#define F367TER_CONST_MU_MAX_LSB 0xf04300ff + +/* ALPHANOISE */ +#define R367TER_ALPHANOISE 0xf044 +#define F367TER_USE_ALLFILTER 0xf0440080 +#define F367TER_INTER_ON 0xf0440040 +#define F367TER_ALPHA_NOISE 0xf044001f + +/* MAXGP_MSB */ +#define R367TER_MAXGP_MSB 0xf045 +#define F367TER_MUFILTER_LENGTH 0xf04500f0 +#define F367TER_MAX_GP_MSB 0xf045000f + +/* MAXGP_LSB */ +#define R367TER_MAXGP_LSB 0xf046 +#define F367TER_MAX_GP_LSB 0xf04600ff + +/* ALPHAMSB */ +#define R367TER_ALPHAMSB 0xf047 +#define F367TER_CHC_DATARATE 0xf04700c0 +#define F367TER_ALPHA_MSB 0xf047003f + +/* ALPHALSB */ +#define R367TER_ALPHALSB 0xf048 +#define F367TER_ALPHA_LSB 0xf04800ff + +/* PILOT_ACCU */ +#define R367TER_PILOT_ACCU 0xf049 +#define F367TER_USE_SCAT4ADDAPT 0xf0490080 +#define F367TER_PILOT_ACC 0xf049001f + +/* PILOTMU_ACCU */ +#define R367TER_PILOTMU_ACCU 0xf04a +#define F367TER_DISCARD_BAD_SP 0xf04a0080 +#define F367TER_DISCARD_BAD_CP 0xf04a0040 +#define F367TER_PILOT_MU_ACCU 0xf04a001f + +/* FILT_CHANNEL_EST */ +#define R367TER_FILT_CHANNEL_EST 0xf04b +#define F367TER_USE_FILT_PILOT 0xf04b0080 +#define F367TER_FILT_CHANNEL 0xf04b007f + +/* ALPHA_NOPISE_FREQ */ +#define R367TER_ALPHA_NOPISE_FREQ 0xf04c +#define F367TER_NOISE_FREQ_FILT 0xf04c0040 +#define F367TER_ALPHA_NOISE_FREQ 0xf04c003f + +/* RATIO_PILOT */ +#define R367TER_RATIO_PILOT 0xf04d +#define F367TER_RATIO_MEAN_SP 0xf04d00f0 +#define F367TER_RATIO_MEAN_CP 0xf04d000f + +/* CHC_CTL */ +#define R367TER_CHC_CTL 0xf04e +#define F367TER_TRACK_EN 0xf04e0080 +#define F367TER_NOISE_NORM_EN 0xf04e0040 +#define F367TER_FORCE_CHC_RESET 0xf04e0020 +#define F367TER_SHORT_TIME 0xf04e0010 +#define F367TER_FORCE_STATE_EN 0xf04e0008 +#define F367TER_FORCE_STATE 0xf04e0007 + +/* EPQ_ADJUST */ +#define R367TER_EPQ_ADJUST 0xf04f +#define F367TER_ADJUST_SCAT_IND 0xf04f00c0 +#define F367TER_ONE_SYMBOL 0xf04f0010 +#define F367TER_EPQ_DECAY 0xf04f000e +#define F367TER_HOLD_SLOPE 0xf04f0001 + +/* EPQ_THRES */ +#define R367TER_EPQ_THRES 0xf050 +#define F367TER_EPQ_THR 0xf05000ff + +/* OMEGA_CTL */ +#define R367TER_OMEGA_CTL 0xf051 +#define F367TER_OMEGA_RST 0xf0510080 +#define F367TER_FREEZE_OMEGA 0xf0510040 +#define F367TER_OMEGA_SEL 0xf051003f + +/* GP_CTL */ +#define R367TER_GP_CTL 0xf052 +#define F367TER_CHC_STATE 0xf05200e0 +#define F367TER_FREEZE_GP 0xf0520010 +#define F367TER_GP_SEL 0xf052000f + +/* MUMSB */ +#define R367TER_MUMSB 0xf053 +#define F367TER_MU_MSB 0xf053007f + +/* MULSB */ +#define R367TER_MULSB 0xf054 +#define F367TER_MU_LSB 0xf05400ff + +/* GPMSB */ +#define R367TER_GPMSB 0xf055 +#define F367TER_CSI_THRESHOLD 0xf05500e0 +#define F367TER_GP_MSB 0xf055000f + +/* GPLSB */ +#define R367TER_GPLSB 0xf056 +#define F367TER_GP_LSB 0xf05600ff + +/* OMEGAMSB */ +#define R367TER_OMEGAMSB 0xf057 +#define F367TER_OMEGA_MSB 0xf057007f + +/* OMEGALSB */ +#define R367TER_OMEGALSB 0xf058 +#define F367TER_OMEGA_LSB 0xf05800ff + +/* SCAT_NB */ +#define R367TER_SCAT_NB 0xf059 +#define F367TER_CHC_TEST 0xf05900f8 +#define F367TER_SCAT_NUMB 0xf0590003 + +/* CHC_DUMMY */ +#define R367TER_CHC_DUMMY 0xf05a +#define F367TER_CHC_DUM 0xf05a00ff + +/* INC_CTL */ +#define R367TER_INC_CTL 0xf05b +#define F367TER_INC_BYPASS 0xf05b0080 +#define F367TER_INC_NDEPTH 0xf05b000c +#define F367TER_INC_MADEPTH 0xf05b0003 + +/* INCTHRES_COR1 */ +#define R367TER_INCTHRES_COR1 0xf05c +#define F367TER_INC_THRES_COR1 0xf05c00ff + +/* INCTHRES_COR2 */ +#define R367TER_INCTHRES_COR2 0xf05d +#define F367TER_INC_THRES_COR2 0xf05d00ff + +/* INCTHRES_DET1 */ +#define R367TER_INCTHRES_DET1 0xf05e +#define F367TER_INC_THRES_DET1 0xf05e003f + +/* INCTHRES_DET2 */ +#define R367TER_INCTHRES_DET2 0xf05f +#define F367TER_INC_THRES_DET2 0xf05f003f + +/* IIR_CELLNB */ +#define R367TER_IIR_CELLNB 0xf060 +#define F367TER_NRST_IIR 0xf0600080 +#define F367TER_IIR_CELL_NB 0xf0600007 + +/* IIRCX_COEFF1_MSB */ +#define R367TER_IIRCX_COEFF1_MSB 0xf061 +#define F367TER_IIR_CX_COEFF1_MSB 0xf06100ff + +/* IIRCX_COEFF1_LSB */ +#define R367TER_IIRCX_COEFF1_LSB 0xf062 +#define F367TER_IIR_CX_COEFF1_LSB 0xf06200ff + +/* IIRCX_COEFF2_MSB */ +#define R367TER_IIRCX_COEFF2_MSB 0xf063 +#define F367TER_IIR_CX_COEFF2_MSB 0xf06300ff + +/* IIRCX_COEFF2_LSB */ +#define R367TER_IIRCX_COEFF2_LSB 0xf064 +#define F367TER_IIR_CX_COEFF2_LSB 0xf06400ff + +/* IIRCX_COEFF3_MSB */ +#define R367TER_IIRCX_COEFF3_MSB 0xf065 +#define F367TER_IIR_CX_COEFF3_MSB 0xf06500ff + +/* IIRCX_COEFF3_LSB */ +#define R367TER_IIRCX_COEFF3_LSB 0xf066 +#define F367TER_IIR_CX_COEFF3_LSB 0xf06600ff + +/* IIRCX_COEFF4_MSB */ +#define R367TER_IIRCX_COEFF4_MSB 0xf067 +#define F367TER_IIR_CX_COEFF4_MSB 0xf06700ff + +/* IIRCX_COEFF4_LSB */ +#define R367TER_IIRCX_COEFF4_LSB 0xf068 +#define F367TER_IIR_CX_COEFF4_LSB 0xf06800ff + +/* IIRCX_COEFF5_MSB */ +#define R367TER_IIRCX_COEFF5_MSB 0xf069 +#define F367TER_IIR_CX_COEFF5_MSB 0xf06900ff + +/* IIRCX_COEFF5_LSB */ +#define R367TER_IIRCX_COEFF5_LSB 0xf06a +#define F367TER_IIR_CX_COEFF5_LSB 0xf06a00ff + +/* FEPATH_CFG */ +#define R367TER_FEPATH_CFG 0xf06b +#define F367TER_DEMUX_SWAP 0xf06b0004 +#define F367TER_DIGAGC_SWAP 0xf06b0002 +#define F367TER_LONGPATH_IF 0xf06b0001 + +/* PMC1_FUNC */ +#define R367TER_PMC1_FUNC 0xf06c +#define F367TER_SOFT_RSTN 0xf06c0080 +#define F367TER_PMC1_AVERAGE_TIME 0xf06c0078 +#define F367TER_PMC1_WAIT_TIME 0xf06c0006 +#define F367TER_PMC1_2N_SEL 0xf06c0001 + +/* PMC1_FOR */ +#define R367TER_PMC1_FOR 0xf06d +#define F367TER_PMC1_FORCE 0xf06d0080 +#define F367TER_PMC1_FORCE_VALUE 0xf06d007c + +/* PMC2_FUNC */ +#define R367TER_PMC2_FUNC 0xf06e +#define F367TER_PMC2_SOFT_STN 0xf06e0080 +#define F367TER_PMC2_ACCU_TIME 0xf06e0070 +#define F367TER_PMC2_CMDP_MN 0xf06e0008 +#define F367TER_PMC2_SWAP 0xf06e0004 + +/* STATUS_ERR_DA */ +#define R367TER_STATUS_ERR_DA 0xf06f +#define F367TER_COM_USEGAINTRK 0xf06f0080 +#define F367TER_COM_AGCLOCK 0xf06f0040 +#define F367TER_AUT_AGCLOCK 0xf06f0020 +#define F367TER_MIN_ERR_X_LSB 0xf06f000f + +/* DIG_AGC_R */ +#define R367TER_DIG_AGC_R 0xf070 +#define F367TER_COM_SOFT_RSTN 0xf0700080 +#define F367TER_COM_AGC_ON 0xf0700040 +#define F367TER_COM_EARLY 0xf0700020 +#define F367TER_AUT_SOFT_RESETN 0xf0700010 +#define F367TER_AUT_AGC_ON 0xf0700008 +#define F367TER_AUT_EARLY 0xf0700004 +#define F367TER_AUT_ROT_EN 0xf0700002 +#define F367TER_LOCK_SOFT_RESETN 0xf0700001 + +/* COMAGC_TARMSB */ +#define R367TER_COMAGC_TARMSB 0xf071 +#define F367TER_COM_AGC_TARGET_MSB 0xf07100ff + +/* COM_AGC_TAR_ENMODE */ +#define R367TER_COM_AGC_TAR_ENMODE 0xf072 +#define F367TER_COM_AGC_TARGET_LSB 0xf07200f0 +#define F367TER_COM_ENMODE 0xf072000f + +/* COM_AGC_CFG */ +#define R367TER_COM_AGC_CFG 0xf073 +#define F367TER_COM_N 0xf07300f8 +#define F367TER_COM_STABMODE 0xf0730006 +#define F367TER_ERR_SEL 0xf0730001 + +/* COM_AGC_GAIN1 */ +#define R367TER_COM_AGC_GAIN1 0xf074 +#define F367TER_COM_GAIN1aCK 0xf07400f0 +#define F367TER_COM_GAIN1TRK 0xf074000f + +/* AUT_AGC_TARGETMSB */ +#define R367TER_AUT_AGC_TARGETMSB 0xf075 +#define F367TER_AUT_AGC_TARGET_MSB 0xf07500ff + +/* LOCK_DET_MSB */ +#define R367TER_LOCK_DET_MSB 0xf076 +#define F367TER_LOCK_DETECT_MSB 0xf07600ff + +/* AGCTAR_LOCK_LSBS */ +#define R367TER_AGCTAR_LOCK_LSBS 0xf077 +#define F367TER_AUT_AGC_TARGET_LSB 0xf07700f0 +#define F367TER_LOCK_DETECT_LSB 0xf077000f + +/* AUT_GAIN_EN */ +#define R367TER_AUT_GAIN_EN 0xf078 +#define F367TER_AUT_ENMODE 0xf07800f0 +#define F367TER_AUT_GAIN2 0xf078000f + +/* AUT_CFG */ +#define R367TER_AUT_CFG 0xf079 +#define F367TER_AUT_N 0xf07900f8 +#define F367TER_INT_CHOICE 0xf0790006 +#define F367TER_INT_LOAD 0xf0790001 + +/* LOCKN */ +#define R367TER_LOCKN 0xf07a +#define F367TER_LOCK_N 0xf07a00f8 +#define F367TER_SEL_IQNTAR 0xf07a0004 +#define F367TER_LOCK_DETECT_CHOICE 0xf07a0003 + +/* INT_X_3 */ +#define R367TER_INT_X_3 0xf07b +#define F367TER_INT_X3 0xf07b00ff + +/* INT_X_2 */ +#define R367TER_INT_X_2 0xf07c +#define F367TER_INT_X2 0xf07c00ff + +/* INT_X_1 */ +#define R367TER_INT_X_1 0xf07d +#define F367TER_INT_X1 0xf07d00ff + +/* INT_X_0 */ +#define R367TER_INT_X_0 0xf07e +#define F367TER_INT_X0 0xf07e00ff + +/* MIN_ERRX_MSB */ +#define R367TER_MIN_ERRX_MSB 0xf07f +#define F367TER_MIN_ERR_X_MSB 0xf07f00ff + +/* COR_CTL */ +#define R367TER_COR_CTL 0xf080 +#define F367TER_CORE_ACTIVE 0xf0800020 +#define F367TER_HOLD 0xf0800010 +#define F367TER_CORE_STATE_CTL 0xf080000f + +/* COR_STAT */ +#define R367TER_COR_STAT 0xf081 +#define F367TER_SCATT_LOCKED 0xf0810080 +#define F367TER_TPS_LOCKED 0xf0810040 +#define F367TER_SYR_LOCKED_COR 0xf0810020 +#define F367TER_AGC_LOCKED_STAT 0xf0810010 +#define F367TER_CORE_STATE_STAT 0xf081000f + +/* COR_INTEN */ +#define R367TER_COR_INTEN 0xf082 +#define F367TER_INTEN 0xf0820080 +#define F367TER_INTEN_SYR 0xf0820020 +#define F367TER_INTEN_FFT 0xf0820010 +#define F367TER_INTEN_AGC 0xf0820008 +#define F367TER_INTEN_TPS1 0xf0820004 +#define F367TER_INTEN_TPS2 0xf0820002 +#define F367TER_INTEN_TPS3 0xf0820001 + +/* COR_INTSTAT */ +#define R367TER_COR_INTSTAT 0xf083 +#define F367TER_INTSTAT_SYR 0xf0830020 +#define F367TER_INTSTAT_FFT 0xf0830010 +#define F367TER_INTSAT_AGC 0xf0830008 +#define F367TER_INTSTAT_TPS1 0xf0830004 +#define F367TER_INTSTAT_TPS2 0xf0830002 +#define F367TER_INTSTAT_TPS3 0xf0830001 + +/* COR_MODEGUARD */ +#define R367TER_COR_MODEGUARD 0xf084 +#define F367TER_FORCE 0xf0840010 +#define F367TER_MODE 0xf084000c +#define F367TER_GUARD 0xf0840003 + +/* AGC_CTL */ +#define R367TER_AGC_CTL 0xf085 +#define F367TER_AGC_TIMING_FACTOR 0xf08500e0 +#define F367TER_AGC_LAST 0xf0850010 +#define F367TER_AGC_GAIN 0xf085000c +#define F367TER_AGC_NEG 0xf0850002 +#define F367TER_AGC_SET 0xf0850001 + +/* AGC_MANUAL1 */ +#define R367TER_AGC_MANUAL1 0xf086 +#define F367TER_AGC_VAL_LO 0xf08600ff + +/* AGC_MANUAL2 */ +#define R367TER_AGC_MANUAL2 0xf087 +#define F367TER_AGC_VAL_HI 0xf087000f + +/* AGC_TARG */ +#define R367TER_AGC_TARG 0xf088 +#define F367TER_AGC_TARGET 0xf08800ff + +/* AGC_GAIN1 */ +#define R367TER_AGC_GAIN1 0xf089 +#define F367TER_AGC_GAIN_LO 0xf08900ff + +/* AGC_GAIN2 */ +#define R367TER_AGC_GAIN2 0xf08a +#define F367TER_AGC_LOCKED_GAIN2 0xf08a0010 +#define F367TER_AGC_GAIN_HI 0xf08a000f + +/* RESERVED_1 */ +#define R367TER_RESERVED_1 0xf08b +#define F367TER_RESERVED1 0xf08b00ff + +/* RESERVED_2 */ +#define R367TER_RESERVED_2 0xf08c +#define F367TER_RESERVED2 0xf08c00ff + +/* RESERVED_3 */ +#define R367TER_RESERVED_3 0xf08d +#define F367TER_RESERVED3 0xf08d00ff + +/* CAS_CTL */ +#define R367TER_CAS_CTL 0xf08e +#define F367TER_CCS_ENABLE 0xf08e0080 +#define F367TER_ACS_DISABLE 0xf08e0040 +#define F367TER_DAGC_DIS 0xf08e0020 +#define F367TER_DAGC_GAIN 0xf08e0018 +#define F367TER_CCSMU 0xf08e0007 + +/* CAS_FREQ */ +#define R367TER_CAS_FREQ 0xf08f +#define F367TER_CCS_FREQ 0xf08f00ff + +/* CAS_DAGCGAIN */ +#define R367TER_CAS_DAGCGAIN 0xf090 +#define F367TER_CAS_DAGC_GAIN 0xf09000ff + +/* SYR_CTL */ +#define R367TER_SYR_CTL 0xf091 +#define F367TER_SICTH_ENABLE 0xf0910080 +#define F367TER_LONG_ECHO 0xf0910078 +#define F367TER_AUTO_LE_EN 0xf0910004 +#define F367TER_SYR_BYPASS 0xf0910002 +#define F367TER_SYR_TR_DIS 0xf0910001 + +/* SYR_STAT */ +#define R367TER_SYR_STAT 0xf092 +#define F367TER_SYR_LOCKED_STAT 0xf0920010 +#define F367TER_SYR_MODE 0xf092000c +#define F367TER_SYR_GUARD 0xf0920003 + +/* SYR_NCO1 */ +#define R367TER_SYR_NCO1 0xf093 +#define F367TER_SYR_NCO_LO 0xf09300ff + +/* SYR_NCO2 */ +#define R367TER_SYR_NCO2 0xf094 +#define F367TER_SYR_NCO_HI 0xf094003f + +/* SYR_OFFSET1 */ +#define R367TER_SYR_OFFSET1 0xf095 +#define F367TER_SYR_OFFSET_LO 0xf09500ff + +/* SYR_OFFSET2 */ +#define R367TER_SYR_OFFSET2 0xf096 +#define F367TER_SYR_OFFSET_HI 0xf096003f + +/* FFT_CTL */ +#define R367TER_FFT_CTL 0xf097 +#define F367TER_SHIFT_FFT_TRIG 0xf0970018 +#define F367TER_FFT_TRIGGER 0xf0970004 +#define F367TER_FFT_MANUAL 0xf0970002 +#define F367TER_IFFT_MODE 0xf0970001 + +/* SCR_CTL */ +#define R367TER_SCR_CTL 0xf098 +#define F367TER_SYRADJDECAY 0xf0980070 +#define F367TER_SCR_CPEDIS 0xf0980002 +#define F367TER_SCR_DIS 0xf0980001 + +/* PPM_CTL1 */ +#define R367TER_PPM_CTL1 0xf099 +#define F367TER_PPM_MAXFREQ 0xf0990030 +#define F367TER_PPM_MAXTIM 0xf0990008 +#define F367TER_PPM_INVSEL 0xf0990004 +#define F367TER_PPM_SCATDIS 0xf0990002 +#define F367TER_PPM_BYP 0xf0990001 + +/* TRL_CTL */ +#define R367TER_TRL_CTL 0xf09a +#define F367TER_TRL_NOMRATE_LSB 0xf09a0080 +#define F367TER_TRL_GAIN_FACTOR 0xf09a0078 +#define F367TER_TRL_LOOPGAIN 0xf09a0007 + +/* TRL_NOMRATE1 */ +#define R367TER_TRL_NOMRATE1 0xf09b +#define F367TER_TRL_NOMRATE_LO 0xf09b00ff + +/* TRL_NOMRATE2 */ +#define R367TER_TRL_NOMRATE2 0xf09c +#define F367TER_TRL_NOMRATE_HI 0xf09c00ff + +/* TRL_TIME1 */ +#define R367TER_TRL_TIME1 0xf09d +#define F367TER_TRL_TOFFSET_LO 0xf09d00ff + +/* TRL_TIME2 */ +#define R367TER_TRL_TIME2 0xf09e +#define F367TER_TRL_TOFFSET_HI 0xf09e00ff + +/* CRL_CTL */ +#define R367TER_CRL_CTL 0xf09f +#define F367TER_CRL_DIS 0xf09f0080 +#define F367TER_CRL_GAIN_FACTOR 0xf09f0078 +#define F367TER_CRL_LOOPGAIN 0xf09f0007 + +/* CRL_FREQ1 */ +#define R367TER_CRL_FREQ1 0xf0a0 +#define F367TER_CRL_FOFFSET_LO 0xf0a000ff + +/* CRL_FREQ2 */ +#define R367TER_CRL_FREQ2 0xf0a1 +#define F367TER_CRL_FOFFSET_HI 0xf0a100ff + +/* CRL_FREQ3 */ +#define R367TER_CRL_FREQ3 0xf0a2 +#define F367TER_CRL_FOFFSET_VHI 0xf0a200ff + +/* TPS_SFRAME_CTL */ +#define R367TER_TPS_SFRAME_CTL 0xf0a3 +#define F367TER_TPS_SFRAME_SYNC 0xf0a30001 + +/* CHC_SNR */ +#define R367TER_CHC_SNR 0xf0a4 +#define F367TER_CHCSNR 0xf0a400ff + +/* BDI_CTL */ +#define R367TER_BDI_CTL 0xf0a5 +#define F367TER_BDI_LPSEL 0xf0a50002 +#define F367TER_BDI_SERIAL 0xf0a50001 + +/* DMP_CTL */ +#define R367TER_DMP_CTL 0xf0a6 +#define F367TER_DMP_SCALING_FACTOR 0xf0a6001e +#define F367TER_DMP_SDDIS 0xf0a60001 + +/* TPS_RCVD1 */ +#define R367TER_TPS_RCVD1 0xf0a7 +#define F367TER_TPS_CHANGE 0xf0a70040 +#define F367TER_BCH_OK 0xf0a70020 +#define F367TER_TPS_SYNC 0xf0a70010 +#define F367TER_TPS_FRAME 0xf0a70003 + +/* TPS_RCVD2 */ +#define R367TER_TPS_RCVD2 0xf0a8 +#define F367TER_TPS_HIERMODE 0xf0a80070 +#define F367TER_TPS_CONST 0xf0a80003 + +/* TPS_RCVD3 */ +#define R367TER_TPS_RCVD3 0xf0a9 +#define F367TER_TPS_LPCODE 0xf0a90070 +#define F367TER_TPS_HPCODE 0xf0a90007 + +/* TPS_RCVD4 */ +#define R367TER_TPS_RCVD4 0xf0aa +#define F367TER_TPS_GUARD 0xf0aa0030 +#define F367TER_TPS_MODE 0xf0aa0003 + +/* TPS_ID_CELL1 */ +#define R367TER_TPS_ID_CELL1 0xf0ab +#define F367TER_TPS_ID_CELL_LO 0xf0ab00ff + +/* TPS_ID_CELL2 */ +#define R367TER_TPS_ID_CELL2 0xf0ac +#define F367TER_TPS_ID_CELL_HI 0xf0ac00ff + +/* TPS_RCVD5_SET1 */ +#define R367TER_TPS_RCVD5_SET1 0xf0ad +#define F367TER_TPS_NA 0xf0ad00fC +#define F367TER_TPS_SETFRAME 0xf0ad0003 + +/* TPS_SET2 */ +#define R367TER_TPS_SET2 0xf0ae +#define F367TER_TPS_SETHIERMODE 0xf0ae0070 +#define F367TER_TPS_SETCONST 0xf0ae0003 + +/* TPS_SET3 */ +#define R367TER_TPS_SET3 0xf0af +#define F367TER_TPS_SETLPCODE 0xf0af0070 +#define F367TER_TPS_SETHPCODE 0xf0af0007 + +/* TPS_CTL */ +#define R367TER_TPS_CTL 0xf0b0 +#define F367TER_TPS_IMM 0xf0b00004 +#define F367TER_TPS_BCHDIS 0xf0b00002 +#define F367TER_TPS_UPDDIS 0xf0b00001 + +/* CTL_FFTOSNUM */ +#define R367TER_CTL_FFTOSNUM 0xf0b1 +#define F367TER_SYMBOL_NUMBER 0xf0b1007f + +/* TESTSELECT */ +#define R367TER_TESTSELECT 0xf0b2 +#define F367TER_TEST_SELECT 0xf0b2001f + +/* MSC_REV */ +#define R367TER_MSC_REV 0xf0b3 +#define F367TER_REV_NUMBER 0xf0b300ff + +/* PIR_CTL */ +#define R367TER_PIR_CTL 0xf0b4 +#define F367TER_FREEZE 0xf0b40001 + +/* SNR_CARRIER1 */ +#define R367TER_SNR_CARRIER1 0xf0b5 +#define F367TER_SNR_CARRIER_LO 0xf0b500ff + +/* SNR_CARRIER2 */ +#define R367TER_SNR_CARRIER2 0xf0b6 +#define F367TER_MEAN 0xf0b600c0 +#define F367TER_SNR_CARRIER_HI 0xf0b6001f + +/* PPM_CPAMP */ +#define R367TER_PPM_CPAMP 0xf0b7 +#define F367TER_PPM_CPC 0xf0b700ff + +/* TSM_AP0 */ +#define R367TER_TSM_AP0 0xf0b8 +#define F367TER_ADDRESS_BYTE_0 0xf0b800ff + +/* TSM_AP1 */ +#define R367TER_TSM_AP1 0xf0b9 +#define F367TER_ADDRESS_BYTE_1 0xf0b900ff + +/* TSM_AP2 */ +#define R367TER_TSM_AP2 0xf0bA +#define F367TER_DATA_BYTE_0 0xf0ba00ff + +/* TSM_AP3 */ +#define R367TER_TSM_AP3 0xf0bB +#define F367TER_DATA_BYTE_1 0xf0bb00ff + +/* TSM_AP4 */ +#define R367TER_TSM_AP4 0xf0bC +#define F367TER_DATA_BYTE_2 0xf0bc00ff + +/* TSM_AP5 */ +#define R367TER_TSM_AP5 0xf0bD +#define F367TER_DATA_BYTE_3 0xf0bd00ff + +/* TSM_AP6 */ +#define R367TER_TSM_AP6 0xf0bE +#define F367TER_TSM_AP_6 0xf0be00ff + +/* TSM_AP7 */ +#define R367TER_TSM_AP7 0xf0bF +#define F367TER_MEM_SELECT_BYTE 0xf0bf00ff + +/* TSTRES */ +#define R367TER_TSTRES 0xf0c0 +#define F367TER_FRES_DISPLAY 0xf0c00080 +#define F367TER_FRES_FIFO_AD 0xf0c00020 +#define F367TER_FRESRS 0xf0c00010 +#define F367TER_FRESACS 0xf0c00008 +#define F367TER_FRESFEC 0xf0c00004 +#define F367TER_FRES_PRIF 0xf0c00002 +#define F367TER_FRESCORE 0xf0c00001 + +/* ANACTRL */ +#define R367TER_ANACTRL 0xf0c1 +#define F367TER_BYPASS_XTAL 0xf0c10040 +#define F367TER_BYPASS_PLLXN 0xf0c1000c +#define F367TER_DIS_PAD_OSC 0xf0c10002 +#define F367TER_STDBY_PLLXN 0xf0c10001 + +/* TSTBUS */ +#define R367TER_TSTBUS 0xf0c2 +#define F367TER_TS_BYTE_CLK_INV 0xf0c20080 +#define F367TER_CFG_IP 0xf0c20070 +#define F367TER_CFG_TST 0xf0c2000f + +/* TSTRATE */ +#define R367TER_TSTRATE 0xf0c6 +#define F367TER_FORCEPHA 0xf0c60080 +#define F367TER_FNEWPHA 0xf0c60010 +#define F367TER_FROT90 0xf0c60008 +#define F367TER_FR 0xf0c60007 + +/* CONSTMODE */ +#define R367TER_CONSTMODE 0xf0cb +#define F367TER_TST_PRIF 0xf0cb00e0 +#define F367TER_CAR_TYPE 0xf0cb0018 +#define F367TER_CONST_MODE 0xf0cb0003 + +/* CONSTCARR1 */ +#define R367TER_CONSTCARR1 0xf0cc +#define F367TER_CONST_CARR_LO 0xf0cc00ff + +/* CONSTCARR2 */ +#define R367TER_CONSTCARR2 0xf0cd +#define F367TER_CONST_CARR_HI 0xf0cd001f + +/* ICONSTEL */ +#define R367TER_ICONSTEL 0xf0ce +#define F367TER_PICONSTEL 0xf0ce00ff + +/* QCONSTEL */ +#define R367TER_QCONSTEL 0xf0cf +#define F367TER_PQCONSTEL 0xf0cf00ff + +/* TSTBISTRES0 */ +#define R367TER_TSTBISTRES0 0xf0d0 +#define F367TER_BEND_PPM 0xf0d00080 +#define F367TER_BBAD_PPM 0xf0d00040 +#define F367TER_BEND_FFTW 0xf0d00020 +#define F367TER_BBAD_FFTW 0xf0d00010 +#define F367TER_BEND_FFT_BUF 0xf0d00008 +#define F367TER_BBAD_FFT_BUF 0xf0d00004 +#define F367TER_BEND_SYR 0xf0d00002 +#define F367TER_BBAD_SYR 0xf0d00001 + +/* TSTBISTRES1 */ +#define R367TER_TSTBISTRES1 0xf0d1 +#define F367TER_BEND_CHC_CP 0xf0d10080 +#define F367TER_BBAD_CHC_CP 0xf0d10040 +#define F367TER_BEND_CHCI 0xf0d10020 +#define F367TER_BBAD_CHCI 0xf0d10010 +#define F367TER_BEND_BDI 0xf0d10008 +#define F367TER_BBAD_BDI 0xf0d10004 +#define F367TER_BEND_SDI 0xf0d10002 +#define F367TER_BBAD_SDI 0xf0d10001 + +/* TSTBISTRES2 */ +#define R367TER_TSTBISTRES2 0xf0d2 +#define F367TER_BEND_CHC_INC 0xf0d20080 +#define F367TER_BBAD_CHC_INC 0xf0d20040 +#define F367TER_BEND_CHC_SPP 0xf0d20020 +#define F367TER_BBAD_CHC_SPP 0xf0d20010 +#define F367TER_BEND_CHC_CPP 0xf0d20008 +#define F367TER_BBAD_CHC_CPP 0xf0d20004 +#define F367TER_BEND_CHC_SP 0xf0d20002 +#define F367TER_BBAD_CHC_SP 0xf0d20001 + +/* TSTBISTRES3 */ +#define R367TER_TSTBISTRES3 0xf0d3 +#define F367TER_BEND_QAM 0xf0d30080 +#define F367TER_BBAD_QAM 0xf0d30040 +#define F367TER_BEND_SFEC_VIT 0xf0d30020 +#define F367TER_BBAD_SFEC_VIT 0xf0d30010 +#define F367TER_BEND_SFEC_DLINE 0xf0d30008 +#define F367TER_BBAD_SFEC_DLINE 0xf0d30004 +#define F367TER_BEND_SFEC_HW 0xf0d30002 +#define F367TER_BBAD_SFEC_HW 0xf0d30001 + +/* RF_AGC1 */ +#define R367TER_RF_AGC1 0xf0d4 +#define F367TER_RF_AGC1_LEVEL_HI 0xf0d400ff + +/* RF_AGC2 */ +#define R367TER_RF_AGC2 0xf0d5 +#define F367TER_REF_ADGP 0xf0d50080 +#define F367TER_STDBY_ADCGP 0xf0d50020 +#define F367TER_CHANNEL_SEL 0xf0d5001c +#define F367TER_RF_AGC1_LEVEL_LO 0xf0d50003 + +/* ANADIGCTRL */ +#define R367TER_ANADIGCTRL 0xf0d7 +#define F367TER_SEL_CLKDEM 0xf0d70020 +#define F367TER_EN_BUFFER_Q 0xf0d70010 +#define F367TER_EN_BUFFER_I 0xf0d70008 +#define F367TER_ADC_RIS_EGDE 0xf0d70004 +#define F367TER_SGN_ADC 0xf0d70002 +#define F367TER_SEL_AD12_SYNC 0xf0d70001 + +/* PLLMDIV */ +#define R367TER_PLLMDIV 0xf0d8 +#define F367TER_PLL_MDIV 0xf0d800ff + +/* PLLNDIV */ +#define R367TER_PLLNDIV 0xf0d9 +#define F367TER_PLL_NDIV 0xf0d900ff + +/* PLLSETUP */ +#define R367TER_PLLSETUP 0xf0dA +#define F367TER_PLL_PDIV 0xf0da0070 +#define F367TER_PLL_KDIV 0xf0da000f + +/* DUAL_AD12 */ +#define R367TER_DUAL_AD12 0xf0dB +#define F367TER_FS20M 0xf0db0020 +#define F367TER_FS50M 0xf0db0010 +#define F367TER_INMODe0 0xf0db0008 +#define F367TER_POFFQ 0xf0db0004 +#define F367TER_POFFI 0xf0db0002 +#define F367TER_INMODE1 0xf0db0001 + +/* TSTBIST */ +#define R367TER_TSTBIST 0xf0dC +#define F367TER_TST_BYP_CLK 0xf0dc0080 +#define F367TER_TST_GCLKENA_STD 0xf0dc0040 +#define F367TER_TST_GCLKENA 0xf0dc0020 +#define F367TER_TST_MEMBIST 0xf0dc001f + +/* PAD_COMP_CTRL */ +#define R367TER_PAD_COMP_CTRL 0xf0dD +#define F367TER_COMPTQ 0xf0dd0010 +#define F367TER_COMPEN 0xf0dd0008 +#define F367TER_FREEZE2 0xf0dd0004 +#define F367TER_SLEEP_INHBT 0xf0dd0002 +#define F367TER_CHIP_SLEEP 0xf0dd0001 + +/* PAD_COMP_WR */ +#define R367TER_PAD_COMP_WR 0xf0de +#define F367TER_WR_ASRC 0xf0de007f + +/* PAD_COMP_RD */ +#define R367TER_PAD_COMP_RD 0xf0df +#define F367TER_COMPOK 0xf0df0080 +#define F367TER_RD_ASRC 0xf0df007f + +/* SYR_TARGET_FFTADJT_MSB */ +#define R367TER_SYR_TARGET_FFTADJT_MSB 0xf100 +#define F367TER_SYR_START 0xf1000080 +#define F367TER_SYR_TARGET_FFTADJ_HI 0xf100000f + +/* SYR_TARGET_FFTADJT_LSB */ +#define R367TER_SYR_TARGET_FFTADJT_LSB 0xf101 +#define F367TER_SYR_TARGET_FFTADJ_LO 0xf10100ff + +/* SYR_TARGET_CHCADJT_MSB */ +#define R367TER_SYR_TARGET_CHCADJT_MSB 0xf102 +#define F367TER_SYR_TARGET_CHCADJ_HI 0xf102000f + +/* SYR_TARGET_CHCADJT_LSB */ +#define R367TER_SYR_TARGET_CHCADJT_LSB 0xf103 +#define F367TER_SYR_TARGET_CHCADJ_LO 0xf10300ff + +/* SYR_FLAG */ +#define R367TER_SYR_FLAG 0xf104 +#define F367TER_TRIG_FLG1 0xf1040080 +#define F367TER_TRIG_FLG0 0xf1040040 +#define F367TER_FFT_FLG1 0xf1040008 +#define F367TER_FFT_FLG0 0xf1040004 +#define F367TER_CHC_FLG1 0xf1040002 +#define F367TER_CHC_FLG0 0xf1040001 + +/* CRL_TARGET1 */ +#define R367TER_CRL_TARGET1 0xf105 +#define F367TER_CRL_START 0xf1050080 +#define F367TER_CRL_TARGET_VHI 0xf105000f + +/* CRL_TARGET2 */ +#define R367TER_CRL_TARGET2 0xf106 +#define F367TER_CRL_TARGET_HI 0xf10600ff + +/* CRL_TARGET3 */ +#define R367TER_CRL_TARGET3 0xf107 +#define F367TER_CRL_TARGET_LO 0xf10700ff + +/* CRL_TARGET4 */ +#define R367TER_CRL_TARGET4 0xf108 +#define F367TER_CRL_TARGET_VLO 0xf10800ff + +/* CRL_FLAG */ +#define R367TER_CRL_FLAG 0xf109 +#define F367TER_CRL_FLAG1 0xf1090002 +#define F367TER_CRL_FLAG0 0xf1090001 + +/* TRL_TARGET1 */ +#define R367TER_TRL_TARGET1 0xf10a +#define F367TER_TRL_TARGET_HI 0xf10a00ff + +/* TRL_TARGET2 */ +#define R367TER_TRL_TARGET2 0xf10b +#define F367TER_TRL_TARGET_LO 0xf10b00ff + +/* TRL_CHC */ +#define R367TER_TRL_CHC 0xf10c +#define F367TER_TRL_START 0xf10c0080 +#define F367TER_CHC_START 0xf10c0040 +#define F367TER_TRL_FLAG1 0xf10c0002 +#define F367TER_TRL_FLAG0 0xf10c0001 + +/* CHC_SNR_TARG */ +#define R367TER_CHC_SNR_TARG 0xf10d +#define F367TER_CHC_SNR_TARGET 0xf10d00ff + +/* TOP_TRACK */ +#define R367TER_TOP_TRACK 0xf10e +#define F367TER_TOP_START 0xf10e0080 +#define F367TER_FIRST_FLAG 0xf10e0070 +#define F367TER_TOP_FLAG1 0xf10e0008 +#define F367TER_TOP_FLAG0 0xf10e0004 +#define F367TER_CHC_FLAG1 0xf10e0002 +#define F367TER_CHC_FLAG0 0xf10e0001 + +/* TRACKER_FREE1 */ +#define R367TER_TRACKER_FREE1 0xf10f +#define F367TER_TRACKER_FREE_1 0xf10f00ff + +/* ERROR_CRL1 */ +#define R367TER_ERROR_CRL1 0xf110 +#define F367TER_ERROR_CRL_VHI 0xf11000ff + +/* ERROR_CRL2 */ +#define R367TER_ERROR_CRL2 0xf111 +#define F367TER_ERROR_CRL_HI 0xf11100ff + +/* ERROR_CRL3 */ +#define R367TER_ERROR_CRL3 0xf112 +#define F367TER_ERROR_CRL_LOI 0xf11200ff + +/* ERROR_CRL4 */ +#define R367TER_ERROR_CRL4 0xf113 +#define F367TER_ERROR_CRL_VLO 0xf11300ff + +/* DEC_NCO1 */ +#define R367TER_DEC_NCO1 0xf114 +#define F367TER_DEC_NCO_VHI 0xf11400ff + +/* DEC_NCO2 */ +#define R367TER_DEC_NCO2 0xf115 +#define F367TER_DEC_NCO_HI 0xf11500ff + +/* DEC_NCO3 */ +#define R367TER_DEC_NCO3 0xf116 +#define F367TER_DEC_NCO_LO 0xf11600ff + +/* SNR */ +#define R367TER_SNR 0xf117 +#define F367TER_SNRATIO 0xf11700ff + +/* SYR_FFTADJ1 */ +#define R367TER_SYR_FFTADJ1 0xf118 +#define F367TER_SYR_FFTADJ_HI 0xf11800ff + +/* SYR_FFTADJ2 */ +#define R367TER_SYR_FFTADJ2 0xf119 +#define F367TER_SYR_FFTADJ_LO 0xf11900ff + +/* SYR_CHCADJ1 */ +#define R367TER_SYR_CHCADJ1 0xf11a +#define F367TER_SYR_CHCADJ_HI 0xf11a00ff + +/* SYR_CHCADJ2 */ +#define R367TER_SYR_CHCADJ2 0xf11b +#define F367TER_SYR_CHCADJ_LO 0xf11b00ff + +/* SYR_OFF */ +#define R367TER_SYR_OFF 0xf11c +#define F367TER_SYR_OFFSET 0xf11c00ff + +/* PPM_OFFSET1 */ +#define R367TER_PPM_OFFSET1 0xf11d +#define F367TER_PPM_OFFSET_HI 0xf11d00ff + +/* PPM_OFFSET2 */ +#define R367TER_PPM_OFFSET2 0xf11e +#define F367TER_PPM_OFFSET_LO 0xf11e00ff + +/* TRACKER_FREE2 */ +#define R367TER_TRACKER_FREE2 0xf11f +#define F367TER_TRACKER_FREE_2 0xf11f00ff + +/* DEBG_LT10 */ +#define R367TER_DEBG_LT10 0xf120 +#define F367TER_DEBUG_LT10 0xf12000ff + +/* DEBG_LT11 */ +#define R367TER_DEBG_LT11 0xf121 +#define F367TER_DEBUG_LT11 0xf12100ff + +/* DEBG_LT12 */ +#define R367TER_DEBG_LT12 0xf122 +#define F367TER_DEBUG_LT12 0xf12200ff + +/* DEBG_LT13 */ +#define R367TER_DEBG_LT13 0xf123 +#define F367TER_DEBUG_LT13 0xf12300ff + +/* DEBG_LT14 */ +#define R367TER_DEBG_LT14 0xf124 +#define F367TER_DEBUG_LT14 0xf12400ff + +/* DEBG_LT15 */ +#define R367TER_DEBG_LT15 0xf125 +#define F367TER_DEBUG_LT15 0xf12500ff + +/* DEBG_LT16 */ +#define R367TER_DEBG_LT16 0xf126 +#define F367TER_DEBUG_LT16 0xf12600ff + +/* DEBG_LT17 */ +#define R367TER_DEBG_LT17 0xf127 +#define F367TER_DEBUG_LT17 0xf12700ff + +/* DEBG_LT18 */ +#define R367TER_DEBG_LT18 0xf128 +#define F367TER_DEBUG_LT18 0xf12800ff + +/* DEBG_LT19 */ +#define R367TER_DEBG_LT19 0xf129 +#define F367TER_DEBUG_LT19 0xf12900ff + +/* DEBG_LT1a */ +#define R367TER_DEBG_LT1A 0xf12a +#define F367TER_DEBUG_LT1A 0xf12a00ff + +/* DEBG_LT1b */ +#define R367TER_DEBG_LT1B 0xf12b +#define F367TER_DEBUG_LT1B 0xf12b00ff + +/* DEBG_LT1c */ +#define R367TER_DEBG_LT1C 0xf12c +#define F367TER_DEBUG_LT1C 0xf12c00ff + +/* DEBG_LT1D */ +#define R367TER_DEBG_LT1D 0xf12d +#define F367TER_DEBUG_LT1D 0xf12d00ff + +/* DEBG_LT1E */ +#define R367TER_DEBG_LT1E 0xf12e +#define F367TER_DEBUG_LT1E 0xf12e00ff + +/* DEBG_LT1F */ +#define R367TER_DEBG_LT1F 0xf12f +#define F367TER_DEBUG_LT1F 0xf12f00ff + +/* RCCFGH */ +#define R367TER_RCCFGH 0xf200 +#define F367TER_TSRCFIFO_DVBCI 0xf2000080 +#define F367TER_TSRCFIFO_SERIAL 0xf2000040 +#define F367TER_TSRCFIFO_DISABLE 0xf2000020 +#define F367TER_TSFIFO_2TORC 0xf2000010 +#define F367TER_TSRCFIFO_HSGNLOUT 0xf2000008 +#define F367TER_TSRCFIFO_ERRMODE 0xf2000006 +#define F367TER_RCCFGH_0 0xf2000001 + +/* RCCFGM */ +#define R367TER_RCCFGM 0xf201 +#define F367TER_TSRCFIFO_MANSPEED 0xf20100c0 +#define F367TER_TSRCFIFO_PERMDATA 0xf2010020 +#define F367TER_TSRCFIFO_NONEWSGNL 0xf2010010 +#define F367TER_RCBYTE_OVERSAMPLING 0xf201000e +#define F367TER_TSRCFIFO_INVDATA 0xf2010001 + +/* RCCFGL */ +#define R367TER_RCCFGL 0xf202 +#define F367TER_TSRCFIFO_BCLKDEL1cK 0xf20200c0 +#define F367TER_RCCFGL_5 0xf2020020 +#define F367TER_TSRCFIFO_DUTY50 0xf2020010 +#define F367TER_TSRCFIFO_NSGNL2dATA 0xf2020008 +#define F367TER_TSRCFIFO_DISSERMUX 0xf2020004 +#define F367TER_RCCFGL_1 0xf2020002 +#define F367TER_TSRCFIFO_STOPCKDIS 0xf2020001 + +/* RCINSDELH */ +#define R367TER_RCINSDELH 0xf203 +#define F367TER_TSRCDEL_SYNCBYTE 0xf2030080 +#define F367TER_TSRCDEL_XXHEADER 0xf2030040 +#define F367TER_TSRCDEL_BBHEADER 0xf2030020 +#define F367TER_TSRCDEL_DATAFIELD 0xf2030010 +#define F367TER_TSRCINSDEL_ISCR 0xf2030008 +#define F367TER_TSRCINSDEL_NPD 0xf2030004 +#define F367TER_TSRCINSDEL_RSPARITY 0xf2030002 +#define F367TER_TSRCINSDEL_CRC8 0xf2030001 + +/* RCINSDELM */ +#define R367TER_RCINSDELM 0xf204 +#define F367TER_TSRCINS_BBPADDING 0xf2040080 +#define F367TER_TSRCINS_BCHFEC 0xf2040040 +#define F367TER_TSRCINS_LDPCFEC 0xf2040020 +#define F367TER_TSRCINS_EMODCOD 0xf2040010 +#define F367TER_TSRCINS_TOKEN 0xf2040008 +#define F367TER_TSRCINS_XXXERR 0xf2040004 +#define F367TER_TSRCINS_MATYPE 0xf2040002 +#define F367TER_TSRCINS_UPL 0xf2040001 + +/* RCINSDELL */ +#define R367TER_RCINSDELL 0xf205 +#define F367TER_TSRCINS_DFL 0xf2050080 +#define F367TER_TSRCINS_SYNCD 0xf2050040 +#define F367TER_TSRCINS_BLOCLEN 0xf2050020 +#define F367TER_TSRCINS_SIGPCOUNT 0xf2050010 +#define F367TER_TSRCINS_FIFO 0xf2050008 +#define F367TER_TSRCINS_REALPACK 0xf2050004 +#define F367TER_TSRCINS_TSCONFIG 0xf2050002 +#define F367TER_TSRCINS_LATENCY 0xf2050001 + +/* RCSTATUS */ +#define R367TER_RCSTATUS 0xf206 +#define F367TER_TSRCFIFO_LINEOK 0xf2060080 +#define F367TER_TSRCFIFO_ERROR 0xf2060040 +#define F367TER_TSRCFIFO_DATA7 0xf2060020 +#define F367TER_RCSTATUS_4 0xf2060010 +#define F367TER_TSRCFIFO_DEMODSEL 0xf2060008 +#define F367TER_TSRC1FIFOSPEED_STORE 0xf2060004 +#define F367TER_RCSTATUS_1 0xf2060002 +#define F367TER_TSRCSERIAL_IMPOSSIBLE 0xf2060001 + +/* RCSPEED */ +#define R367TER_RCSPEED 0xf207 +#define F367TER_TSRCFIFO_OUTSPEED 0xf20700ff + +/* RCDEBUGM */ +#define R367TER_RCDEBUGM 0xf208 +#define F367TER_SD_UNSYNC 0xf2080080 +#define F367TER_ULFLOCK_DETECTM 0xf2080040 +#define F367TER_SUL_SELECTOS 0xf2080020 +#define F367TER_DILUL_NOSCRBLE 0xf2080010 +#define F367TER_NUL_SCRB 0xf2080008 +#define F367TER_UL_SCRB 0xf2080004 +#define F367TER_SCRAULBAD 0xf2080002 +#define F367TER_SCRAUL_UNSYNC 0xf2080001 + +/* RCDEBUGL */ +#define R367TER_RCDEBUGL 0xf209 +#define F367TER_RS_ERR 0xf2090080 +#define F367TER_LLFLOCK_DETECTM 0xf2090040 +#define F367TER_NOT_SUL_SELECTOS 0xf2090020 +#define F367TER_DILLL_NOSCRBLE 0xf2090010 +#define F367TER_NLL_SCRB 0xf2090008 +#define F367TER_LL_SCRB 0xf2090004 +#define F367TER_SCRALLBAD 0xf2090002 +#define F367TER_SCRALL_UNSYNC 0xf2090001 + +/* RCOBSCFG */ +#define R367TER_RCOBSCFG 0xf20a +#define F367TER_TSRCFIFO_OBSCFG 0xf20a00ff + +/* RCOBSM */ +#define R367TER_RCOBSM 0xf20b +#define F367TER_TSRCFIFO_OBSDATA_HI 0xf20b00ff + +/* RCOBSL */ +#define R367TER_RCOBSL 0xf20c +#define F367TER_TSRCFIFO_OBSDATA_LO 0xf20c00ff + +/* RCFECSPY */ +#define R367TER_RCFECSPY 0xf210 +#define F367TER_SPYRC_ENABLE 0xf2100080 +#define F367TER_RCNO_SYNCBYTE 0xf2100040 +#define F367TER_RCSERIAL_MODE 0xf2100020 +#define F367TER_RCUNUSUAL_PACKET 0xf2100010 +#define F367TER_BERRCMETER_DATAMODE 0xf210000c +#define F367TER_BERRCMETER_LMODE 0xf2100002 +#define F367TER_BERRCMETER_RESET 0xf2100001 + +/* RCFSPYCFG */ +#define R367TER_RCFSPYCFG 0xf211 +#define F367TER_FECSPYRC_INPUT 0xf21100c0 +#define F367TER_RCRST_ON_ERROR 0xf2110020 +#define F367TER_RCONE_SHOT 0xf2110010 +#define F367TER_RCI2C_MODE 0xf211000c +#define F367TER_SPYRC_HSTERESIS 0xf2110003 + +/* RCFSPYDATA */ +#define R367TER_RCFSPYDATA 0xf212 +#define F367TER_SPYRC_STUFFING 0xf2120080 +#define F367TER_RCNOERR_PKTJITTER 0xf2120040 +#define F367TER_SPYRC_CNULLPKT 0xf2120020 +#define F367TER_SPYRC_OUTDATA_MODE 0xf212001f + +/* RCFSPYOUT */ +#define R367TER_RCFSPYOUT 0xf213 +#define F367TER_FSPYRC_DIRECT 0xf2130080 +#define F367TER_RCFSPYOUT_6 0xf2130040 +#define F367TER_SPYRC_OUTDATA_BUS 0xf2130038 +#define F367TER_RCSTUFF_MODE 0xf2130007 + +/* RCFSTATUS */ +#define R367TER_RCFSTATUS 0xf214 +#define F367TER_SPYRC_ENDSIM 0xf2140080 +#define F367TER_RCVALID_SIM 0xf2140040 +#define F367TER_RCFOUND_SIGNAL 0xf2140020 +#define F367TER_RCDSS_SYNCBYTE 0xf2140010 +#define F367TER_RCRESULT_STATE 0xf214000f + +/* RCFGOODPACK */ +#define R367TER_RCFGOODPACK 0xf215 +#define F367TER_RCGOOD_PACKET 0xf21500ff + +/* RCFPACKCNT */ +#define R367TER_RCFPACKCNT 0xf216 +#define F367TER_RCPACKET_COUNTER 0xf21600ff + +/* RCFSPYMISC */ +#define R367TER_RCFSPYMISC 0xf217 +#define F367TER_RCLABEL_COUNTER 0xf21700ff + +/* RCFBERCPT4 */ +#define R367TER_RCFBERCPT4 0xf218 +#define F367TER_FBERRCMETER_CPT_MMMMSB 0xf21800ff + +/* RCFBERCPT3 */ +#define R367TER_RCFBERCPT3 0xf219 +#define F367TER_FBERRCMETER_CPT_MMMSB 0xf21900ff + +/* RCFBERCPT2 */ +#define R367TER_RCFBERCPT2 0xf21a +#define F367TER_FBERRCMETER_CPT_MMSB 0xf21a00ff + +/* RCFBERCPT1 */ +#define R367TER_RCFBERCPT1 0xf21b +#define F367TER_FBERRCMETER_CPT_MSB 0xf21b00ff + +/* RCFBERCPT0 */ +#define R367TER_RCFBERCPT0 0xf21c +#define F367TER_FBERRCMETER_CPT_LSB 0xf21c00ff + +/* RCFBERERR2 */ +#define R367TER_RCFBERERR2 0xf21d +#define F367TER_FBERRCMETER_ERR_HI 0xf21d00ff + +/* RCFBERERR1 */ +#define R367TER_RCFBERERR1 0xf21e +#define F367TER_FBERRCMETER_ERR 0xf21e00ff + +/* RCFBERERR0 */ +#define R367TER_RCFBERERR0 0xf21f +#define F367TER_FBERRCMETER_ERR_LO 0xf21f00ff + +/* RCFSTATESM */ +#define R367TER_RCFSTATESM 0xf220 +#define F367TER_RCRSTATE_F 0xf2200080 +#define F367TER_RCRSTATE_E 0xf2200040 +#define F367TER_RCRSTATE_D 0xf2200020 +#define F367TER_RCRSTATE_C 0xf2200010 +#define F367TER_RCRSTATE_B 0xf2200008 +#define F367TER_RCRSTATE_A 0xf2200004 +#define F367TER_RCRSTATE_9 0xf2200002 +#define F367TER_RCRSTATE_8 0xf2200001 + +/* RCFSTATESL */ +#define R367TER_RCFSTATESL 0xf221 +#define F367TER_RCRSTATE_7 0xf2210080 +#define F367TER_RCRSTATE_6 0xf2210040 +#define F367TER_RCRSTATE_5 0xf2210020 +#define F367TER_RCRSTATE_4 0xf2210010 +#define F367TER_RCRSTATE_3 0xf2210008 +#define F367TER_RCRSTATE_2 0xf2210004 +#define F367TER_RCRSTATE_1 0xf2210002 +#define F367TER_RCRSTATE_0 0xf2210001 + +/* RCFSPYBER */ +#define R367TER_RCFSPYBER 0xf222 +#define F367TER_RCFSPYBER_7 0xf2220080 +#define F367TER_SPYRCOBS_XORREAD 0xf2220040 +#define F367TER_FSPYRCBER_OBSMODE 0xf2220020 +#define F367TER_FSPYRCBER_SYNCBYT 0xf2220010 +#define F367TER_FSPYRCBER_UNSYNC 0xf2220008 +#define F367TER_FSPYRCBER_CTIME 0xf2220007 + +/* RCFSPYDISTM */ +#define R367TER_RCFSPYDISTM 0xf223 +#define F367TER_RCPKTTIME_DISTANCE_HI 0xf22300ff + +/* RCFSPYDISTL */ +#define R367TER_RCFSPYDISTL 0xf224 +#define F367TER_RCPKTTIME_DISTANCE_LO 0xf22400ff + +/* RCFSPYOBS7 */ +#define R367TER_RCFSPYOBS7 0xf228 +#define F367TER_RCSPYOBS_SPYFAIL 0xf2280080 +#define F367TER_RCSPYOBS_SPYFAIL1 0xf2280040 +#define F367TER_RCSPYOBS_ERROR 0xf2280020 +#define F367TER_RCSPYOBS_STROUT 0xf2280010 +#define F367TER_RCSPYOBS_RESULTSTATE1 0xf228000f + +/* RCFSPYOBS6 */ +#define R367TER_RCFSPYOBS6 0xf229 +#define F367TER_RCSPYOBS_RESULTSTATe0 0xf22900f0 +#define F367TER_RCSPYOBS_RESULTSTATEM1 0xf229000f + +/* RCFSPYOBS5 */ +#define R367TER_RCFSPYOBS5 0xf22a +#define F367TER_RCSPYOBS_BYTEOFPACKET1 0xf22a00ff + +/* RCFSPYOBS4 */ +#define R367TER_RCFSPYOBS4 0xf22b +#define F367TER_RCSPYOBS_BYTEVALUE1 0xf22b00ff + +/* RCFSPYOBS3 */ +#define R367TER_RCFSPYOBS3 0xf22c +#define F367TER_RCSPYOBS_DATA1 0xf22c00ff + +/* RCFSPYOBS2 */ +#define R367TER_RCFSPYOBS2 0xf22d +#define F367TER_RCSPYOBS_DATa0 0xf22d00ff + +/* RCFSPYOBS1 */ +#define R367TER_RCFSPYOBS1 0xf22e +#define F367TER_RCSPYOBS_DATAM1 0xf22e00ff + +/* RCFSPYOBS0 */ +#define R367TER_RCFSPYOBS0 0xf22f +#define F367TER_RCSPYOBS_DATAM2 0xf22f00ff + +/* TSGENERAL */ +#define R367TER_TSGENERAL 0xf230 +#define F367TER_TSGENERAL_7 0xf2300080 +#define F367TER_TSGENERAL_6 0xf2300040 +#define F367TER_TSFIFO_BCLK1aLL 0xf2300020 +#define F367TER_TSGENERAL_4 0xf2300010 +#define F367TER_MUXSTREAM_OUTMODE 0xf2300008 +#define F367TER_TSFIFO_PERMPARAL 0xf2300006 +#define F367TER_RST_REEDSOLO 0xf2300001 + +/* RC1SPEED */ +#define R367TER_RC1SPEED 0xf231 +#define F367TER_TSRCFIFO1_OUTSPEED 0xf23100ff + +/* TSGSTATUS */ +#define R367TER_TSGSTATUS 0xf232 +#define F367TER_TSGSTATUS_7 0xf2320080 +#define F367TER_TSGSTATUS_6 0xf2320040 +#define F367TER_RSMEM_FULL 0xf2320020 +#define F367TER_RS_MULTCALC 0xf2320010 +#define F367TER_RSIN_OVERTIME 0xf2320008 +#define F367TER_TSFIFO3_DEMODSEL 0xf2320004 +#define F367TER_TSFIFO2_DEMODSEL 0xf2320002 +#define F367TER_TSFIFO1_DEMODSEL 0xf2320001 + + +/* FECM */ +#define R367TER_FECM 0xf233 +#define F367TER_DSS_DVB 0xf2330080 +#define F367TER_DEMOD_BYPASS 0xf2330040 +#define F367TER_CMP_SLOWMODE 0xf2330020 +#define F367TER_DSS_SRCH 0xf2330010 +#define F367TER_FECM_3 0xf2330008 +#define F367TER_DIFF_MODEVIT 0xf2330004 +#define F367TER_SYNCVIT 0xf2330002 +#define F367TER_I2CSYM 0xf2330001 + +/* VTH12 */ +#define R367TER_VTH12 0xf234 +#define F367TER_VTH_12 0xf23400ff + +/* VTH23 */ +#define R367TER_VTH23 0xf235 +#define F367TER_VTH_23 0xf23500ff + +/* VTH34 */ +#define R367TER_VTH34 0xf236 +#define F367TER_VTH_34 0xf23600ff + +/* VTH56 */ +#define R367TER_VTH56 0xf237 +#define F367TER_VTH_56 0xf23700ff + +/* VTH67 */ +#define R367TER_VTH67 0xf238 +#define F367TER_VTH_67 0xf23800ff + +/* VTH78 */ +#define R367TER_VTH78 0xf239 +#define F367TER_VTH_78 0xf23900ff + +/* VITCURPUN */ +#define R367TER_VITCURPUN 0xf23a +#define F367TER_VIT_MAPPING 0xf23a00e0 +#define F367TER_VIT_CURPUN 0xf23a001f + +/* VERROR */ +#define R367TER_VERROR 0xf23b +#define F367TER_REGERR_VIT 0xf23b00ff + +/* PRVIT */ +#define R367TER_PRVIT 0xf23c +#define F367TER_PRVIT_7 0xf23c0080 +#define F367TER_DIS_VTHLOCK 0xf23c0040 +#define F367TER_E7_8VIT 0xf23c0020 +#define F367TER_E6_7VIT 0xf23c0010 +#define F367TER_E5_6VIT 0xf23c0008 +#define F367TER_E3_4VIT 0xf23c0004 +#define F367TER_E2_3VIT 0xf23c0002 +#define F367TER_E1_2VIT 0xf23c0001 + +/* VAVSRVIT */ +#define R367TER_VAVSRVIT 0xf23d +#define F367TER_AMVIT 0xf23d0080 +#define F367TER_FROZENVIT 0xf23d0040 +#define F367TER_SNVIT 0xf23d0030 +#define F367TER_TOVVIT 0xf23d000c +#define F367TER_HYPVIT 0xf23d0003 + +/* VSTATUSVIT */ +#define R367TER_VSTATUSVIT 0xf23e +#define F367TER_VITERBI_ON 0xf23e0080 +#define F367TER_END_LOOPVIT 0xf23e0040 +#define F367TER_VITERBI_DEPRF 0xf23e0020 +#define F367TER_PRFVIT 0xf23e0010 +#define F367TER_LOCKEDVIT 0xf23e0008 +#define F367TER_VITERBI_DELOCK 0xf23e0004 +#define F367TER_VIT_DEMODSEL 0xf23e0002 +#define F367TER_VITERBI_COMPOUT 0xf23e0001 + +/* VTHINUSE */ +#define R367TER_VTHINUSE 0xf23f +#define F367TER_VIT_INUSE 0xf23f00ff + +/* KDIV12 */ +#define R367TER_KDIV12 0xf240 +#define F367TER_KDIV12_MANUAL 0xf2400080 +#define F367TER_K_DIVIDER_12 0xf240007f + +/* KDIV23 */ +#define R367TER_KDIV23 0xf241 +#define F367TER_KDIV23_MANUAL 0xf2410080 +#define F367TER_K_DIVIDER_23 0xf241007f + +/* KDIV34 */ +#define R367TER_KDIV34 0xf242 +#define F367TER_KDIV34_MANUAL 0xf2420080 +#define F367TER_K_DIVIDER_34 0xf242007f + +/* KDIV56 */ +#define R367TER_KDIV56 0xf243 +#define F367TER_KDIV56_MANUAL 0xf2430080 +#define F367TER_K_DIVIDER_56 0xf243007f + +/* KDIV67 */ +#define R367TER_KDIV67 0xf244 +#define F367TER_KDIV67_MANUAL 0xf2440080 +#define F367TER_K_DIVIDER_67 0xf244007f + +/* KDIV78 */ +#define R367TER_KDIV78 0xf245 +#define F367TER_KDIV78_MANUAL 0xf2450080 +#define F367TER_K_DIVIDER_78 0xf245007f + +/* SIGPOWER */ +#define R367TER_SIGPOWER 0xf246 +#define F367TER_SIGPOWER_MANUAL 0xf2460080 +#define F367TER_SIG_POWER 0xf246007f + +/* DEMAPVIT */ +#define R367TER_DEMAPVIT 0xf247 +#define F367TER_DEMAPVIT_7 0xf2470080 +#define F367TER_K_DIVIDER_VIT 0xf247007f + +/* VITSCALE */ +#define R367TER_VITSCALE 0xf248 +#define F367TER_NVTH_NOSRANGE 0xf2480080 +#define F367TER_VERROR_MAXMODE 0xf2480040 +#define F367TER_KDIV_MODE 0xf2480030 +#define F367TER_NSLOWSN_LOCKED 0xf2480008 +#define F367TER_DELOCK_PRFLOSS 0xf2480004 +#define F367TER_DIS_RSFLOCK 0xf2480002 +#define F367TER_VITSCALE_0 0xf2480001 + +/* FFEC1PRG */ +#define R367TER_FFEC1PRG 0xf249 +#define F367TER_FDSS_DVB 0xf2490080 +#define F367TER_FDSS_SRCH 0xf2490040 +#define F367TER_FFECPROG_5 0xf2490020 +#define F367TER_FFECPROG_4 0xf2490010 +#define F367TER_FFECPROG_3 0xf2490008 +#define F367TER_FFECPROG_2 0xf2490004 +#define F367TER_FTS1_DISABLE 0xf2490002 +#define F367TER_FTS2_DISABLE 0xf2490001 + +/* FVITCURPUN */ +#define R367TER_FVITCURPUN 0xf24a +#define F367TER_FVIT_MAPPING 0xf24a00e0 +#define F367TER_FVIT_CURPUN 0xf24a001f + +/* FVERROR */ +#define R367TER_FVERROR 0xf24b +#define F367TER_FREGERR_VIT 0xf24b00ff + +/* FVSTATUSVIT */ +#define R367TER_FVSTATUSVIT 0xf24c +#define F367TER_FVITERBI_ON 0xf24c0080 +#define F367TER_F1END_LOOPVIT 0xf24c0040 +#define F367TER_FVITERBI_DEPRF 0xf24c0020 +#define F367TER_FPRFVIT 0xf24c0010 +#define F367TER_FLOCKEDVIT 0xf24c0008 +#define F367TER_FVITERBI_DELOCK 0xf24c0004 +#define F367TER_FVIT_DEMODSEL 0xf24c0002 +#define F367TER_FVITERBI_COMPOUT 0xf24c0001 + +/* DEBUG_LT1 */ +#define R367TER_DEBUG_LT1 0xf24d +#define F367TER_DBG_LT1 0xf24d00ff + +/* DEBUG_LT2 */ +#define R367TER_DEBUG_LT2 0xf24e +#define F367TER_DBG_LT2 0xf24e00ff + +/* DEBUG_LT3 */ +#define R367TER_DEBUG_LT3 0xf24f +#define F367TER_DBG_LT3 0xf24f00ff + +/* TSTSFMET */ +#define R367TER_TSTSFMET 0xf250 +#define F367TER_TSTSFEC_METRIQUES 0xf25000ff + +/* SELOUT */ +#define R367TER_SELOUT 0xf252 +#define F367TER_EN_SYNC 0xf2520080 +#define F367TER_EN_TBUSDEMAP 0xf2520040 +#define F367TER_SELOUT_5 0xf2520020 +#define F367TER_SELOUT_4 0xf2520010 +#define F367TER_TSTSYNCHRO_MODE 0xf2520002 + +/* TSYNC */ +#define R367TER_TSYNC 0xf253 +#define F367TER_CURPUN_INCMODE 0xf2530080 +#define F367TER_CERR_TSTMODE 0xf2530040 +#define F367TER_SHIFTSOF_MODE 0xf2530030 +#define F367TER_SLOWPHA_MODE 0xf2530008 +#define F367TER_PXX_BYPALL 0xf2530004 +#define F367TER_FROTA45_FIRST 0xf2530002 +#define F367TER_TST_BCHERROR 0xf2530001 + +/* TSTERR */ +#define R367TER_TSTERR 0xf254 +#define F367TER_TST_LONGPKT 0xf2540080 +#define F367TER_TST_ISSYION 0xf2540040 +#define F367TER_TST_NPDON 0xf2540020 +#define F367TER_TSTERR_4 0xf2540010 +#define F367TER_TRACEBACK_MODE 0xf2540008 +#define F367TER_TST_RSPARITY 0xf2540004 +#define F367TER_METRIQUE_MODE 0xf2540003 + +/* TSFSYNC */ +#define R367TER_TSFSYNC 0xf255 +#define F367TER_EN_SFECSYNC 0xf2550080 +#define F367TER_EN_SFECDEMAP 0xf2550040 +#define F367TER_SFCERR_TSTMODE 0xf2550020 +#define F367TER_SFECPXX_BYPALL 0xf2550010 +#define F367TER_SFECTSTSYNCHRO_MODE 0xf255000f + +/* TSTSFERR */ +#define R367TER_TSTSFERR 0xf256 +#define F367TER_TSTSTERR_7 0xf2560080 +#define F367TER_TSTSTERR_6 0xf2560040 +#define F367TER_TSTSTERR_5 0xf2560020 +#define F367TER_TSTSTERR_4 0xf2560010 +#define F367TER_SFECTRACEBACK_MODE 0xf2560008 +#define F367TER_SFEC_NCONVPROG 0xf2560004 +#define F367TER_SFECMETRIQUE_MODE 0xf2560003 + +/* TSTTSSF1 */ +#define R367TER_TSTTSSF1 0xf258 +#define F367TER_TSTERSSF 0xf2580080 +#define F367TER_TSTTSSFEN 0xf2580040 +#define F367TER_SFEC_OUTMODE 0xf2580030 +#define F367TER_XLSF_NOFTHRESHOLD 0xf2580008 +#define F367TER_TSTTSSF_STACKSEL 0xf2580007 + +/* TSTTSSF2 */ +#define R367TER_TSTTSSF2 0xf259 +#define F367TER_DILSF_DBBHEADER 0xf2590080 +#define F367TER_TSTTSSF_DISBUG 0xf2590040 +#define F367TER_TSTTSSF_NOBADSTART 0xf2590020 +#define F367TER_TSTTSSF_SELECT 0xf259001f + +/* TSTTSSF3 */ +#define R367TER_TSTTSSF3 0xf25a +#define F367TER_TSTTSSF3_7 0xf25a0080 +#define F367TER_TSTTSSF3_6 0xf25a0040 +#define F367TER_TSTTSSF3_5 0xf25a0020 +#define F367TER_TSTTSSF3_4 0xf25a0010 +#define F367TER_TSTTSSF3_3 0xf25a0008 +#define F367TER_TSTTSSF3_2 0xf25a0004 +#define F367TER_TSTTSSF3_1 0xf25a0002 +#define F367TER_DISSF_CLKENABLE 0xf25a0001 + +/* TSTTS1 */ +#define R367TER_TSTTS1 0xf25c +#define F367TER_TSTERS 0xf25c0080 +#define F367TER_TSFIFO_DSSSYNCB 0xf25c0040 +#define F367TER_TSTTS_FSPYBEFRS 0xf25c0020 +#define F367TER_NFORCE_SYNCBYTE 0xf25c0010 +#define F367TER_XL_NOFTHRESHOLD 0xf25c0008 +#define F367TER_TSTTS_FRFORCEPKT 0xf25c0004 +#define F367TER_DESCR_NOTAUTO 0xf25c0002 +#define F367TER_TSTTSEN 0xf25c0001 + +/* TSTTS2 */ +#define R367TER_TSTTS2 0xf25d +#define F367TER_DIL_DBBHEADER 0xf25d0080 +#define F367TER_TSTTS_NOBADXXX 0xf25d0040 +#define F367TER_TSFIFO_DELSPEEDUP 0xf25d0020 +#define F367TER_TSTTS_SELECT 0xf25d001f + +/* TSTTS3 */ +#define R367TER_TSTTS3 0xf25e +#define F367TER_TSTTS_NOPKTGAIN 0xf25e0080 +#define F367TER_TSTTS_NOPKTENE 0xf25e0040 +#define F367TER_TSTTS_ISOLATION 0xf25e0020 +#define F367TER_TSTTS_DISBUG 0xf25e0010 +#define F367TER_TSTTS_NOBADSTART 0xf25e0008 +#define F367TER_TSTTS_STACKSEL 0xf25e0007 + +/* TSTTS4 */ +#define R367TER_TSTTS4 0xf25f +#define F367TER_TSTTS4_7 0xf25f0080 +#define F367TER_TSTTS4_6 0xf25f0040 +#define F367TER_TSTTS4_5 0xf25f0020 +#define F367TER_TSTTS_DISDSTATE 0xf25f0010 +#define F367TER_TSTTS_FASTNOSYNC 0xf25f0008 +#define F367TER_EXT_FECSPYIN 0xf25f0004 +#define F367TER_TSTTS_NODPZERO 0xf25f0002 +#define F367TER_TSTTS_NODIV3 0xf25f0001 + +/* TSTTSRC */ +#define R367TER_TSTTSRC 0xf26c +#define F367TER_TSTTSRC_7 0xf26c0080 +#define F367TER_TSRCFIFO_DSSSYNCB 0xf26c0040 +#define F367TER_TSRCFIFO_DPUNACTIVE 0xf26c0020 +#define F367TER_TSRCFIFO_DELSPEEDUP 0xf26c0010 +#define F367TER_TSTTSRC_NODIV3 0xf26c0008 +#define F367TER_TSTTSRC_FRFORCEPKT 0xf26c0004 +#define F367TER_SAT25_SDDORIGINE 0xf26c0002 +#define F367TER_TSTTSRC_INACTIVE 0xf26c0001 + +/* TSTTSRS */ +#define R367TER_TSTTSRS 0xf26d +#define F367TER_TSTTSRS_7 0xf26d0080 +#define F367TER_TSTTSRS_6 0xf26d0040 +#define F367TER_TSTTSRS_5 0xf26d0020 +#define F367TER_TSTTSRS_4 0xf26d0010 +#define F367TER_TSTTSRS_3 0xf26d0008 +#define F367TER_TSTTSRS_2 0xf26d0004 +#define F367TER_TSTRS_DISRS2 0xf26d0002 +#define F367TER_TSTRS_DISRS1 0xf26d0001 + +/* TSSTATEM */ +#define R367TER_TSSTATEM 0xf270 +#define F367TER_TSDIL_ON 0xf2700080 +#define F367TER_TSSKIPRS_ON 0xf2700040 +#define F367TER_TSRS_ON 0xf2700020 +#define F367TER_TSDESCRAMB_ON 0xf2700010 +#define F367TER_TSFRAME_MODE 0xf2700008 +#define F367TER_TS_DISABLE 0xf2700004 +#define F367TER_TSACM_MODE 0xf2700002 +#define F367TER_TSOUT_NOSYNC 0xf2700001 + +/* TSSTATEL */ +#define R367TER_TSSTATEL 0xf271 +#define F367TER_TSNOSYNCBYTE 0xf2710080 +#define F367TER_TSPARITY_ON 0xf2710040 +#define F367TER_TSSYNCOUTRS_ON 0xf2710020 +#define F367TER_TSDVBS2_MODE 0xf2710010 +#define F367TER_TSISSYI_ON 0xf2710008 +#define F367TER_TSNPD_ON 0xf2710004 +#define F367TER_TSCRC8_ON 0xf2710002 +#define F367TER_TSDSS_PACKET 0xf2710001 + +/* TSCFGH */ +#define R367TER_TSCFGH 0xf272 +#define F367TER_TSFIFO_DVBCI 0xf2720080 +#define F367TER_TSFIFO_SERIAL 0xf2720040 +#define F367TER_TSFIFO_TEIUPDATE 0xf2720020 +#define F367TER_TSFIFO_DUTY50 0xf2720010 +#define F367TER_TSFIFO_HSGNLOUT 0xf2720008 +#define F367TER_TSFIFO_ERRMODE 0xf2720006 +#define F367TER_RST_HWARE 0xf2720001 + +/* TSCFGM */ +#define R367TER_TSCFGM 0xf273 +#define F367TER_TSFIFO_MANSPEED 0xf27300c0 +#define F367TER_TSFIFO_PERMDATA 0xf2730020 +#define F367TER_TSFIFO_NONEWSGNL 0xf2730010 +#define F367TER_TSFIFO_BITSPEED 0xf2730008 +#define F367TER_NPD_SPECDVBS2 0xf2730004 +#define F367TER_TSFIFO_STOPCKDIS 0xf2730002 +#define F367TER_TSFIFO_INVDATA 0xf2730001 + +/* TSCFGL */ +#define R367TER_TSCFGL 0xf274 +#define F367TER_TSFIFO_BCLKDEL1cK 0xf27400c0 +#define F367TER_BCHERROR_MODE 0xf2740030 +#define F367TER_TSFIFO_NSGNL2dATA 0xf2740008 +#define F367TER_TSFIFO_EMBINDVB 0xf2740004 +#define F367TER_TSFIFO_DPUNACT 0xf2740002 +#define F367TER_TSFIFO_NPDOFF 0xf2740001 + +/* TSSYNC */ +#define R367TER_TSSYNC 0xf275 +#define F367TER_TSFIFO_PERMUTE 0xf2750080 +#define F367TER_TSFIFO_FISCR3B 0xf2750060 +#define F367TER_TSFIFO_SYNCMODE 0xf2750018 +#define F367TER_TSFIFO_SYNCSEL 0xf2750007 + +/* TSINSDELH */ +#define R367TER_TSINSDELH 0xf276 +#define F367TER_TSDEL_SYNCBYTE 0xf2760080 +#define F367TER_TSDEL_XXHEADER 0xf2760040 +#define F367TER_TSDEL_BBHEADER 0xf2760020 +#define F367TER_TSDEL_DATAFIELD 0xf2760010 +#define F367TER_TSINSDEL_ISCR 0xf2760008 +#define F367TER_TSINSDEL_NPD 0xf2760004 +#define F367TER_TSINSDEL_RSPARITY 0xf2760002 +#define F367TER_TSINSDEL_CRC8 0xf2760001 + +/* TSINSDELM */ +#define R367TER_TSINSDELM 0xf277 +#define F367TER_TSINS_BBPADDING 0xf2770080 +#define F367TER_TSINS_BCHFEC 0xf2770040 +#define F367TER_TSINS_LDPCFEC 0xf2770020 +#define F367TER_TSINS_EMODCOD 0xf2770010 +#define F367TER_TSINS_TOKEN 0xf2770008 +#define F367TER_TSINS_XXXERR 0xf2770004 +#define F367TER_TSINS_MATYPE 0xf2770002 +#define F367TER_TSINS_UPL 0xf2770001 + +/* TSINSDELL */ +#define R367TER_TSINSDELL 0xf278 +#define F367TER_TSINS_DFL 0xf2780080 +#define F367TER_TSINS_SYNCD 0xf2780040 +#define F367TER_TSINS_BLOCLEN 0xf2780020 +#define F367TER_TSINS_SIGPCOUNT 0xf2780010 +#define F367TER_TSINS_FIFO 0xf2780008 +#define F367TER_TSINS_REALPACK 0xf2780004 +#define F367TER_TSINS_TSCONFIG 0xf2780002 +#define F367TER_TSINS_LATENCY 0xf2780001 + +/* TSDIVN */ +#define R367TER_TSDIVN 0xf279 +#define F367TER_TSFIFO_LOWSPEED 0xf2790080 +#define F367TER_BYTE_OVERSAMPLING 0xf2790070 +#define F367TER_TSMANUAL_PACKETNBR 0xf279000f + +/* TSDIVPM */ +#define R367TER_TSDIVPM 0xf27a +#define F367TER_TSMANUAL_P_HI 0xf27a00ff + +/* TSDIVPL */ +#define R367TER_TSDIVPL 0xf27b +#define F367TER_TSMANUAL_P_LO 0xf27b00ff + +/* TSDIVQM */ +#define R367TER_TSDIVQM 0xf27c +#define F367TER_TSMANUAL_Q_HI 0xf27c00ff + +/* TSDIVQL */ +#define R367TER_TSDIVQL 0xf27d +#define F367TER_TSMANUAL_Q_LO 0xf27d00ff + +/* TSDILSTKM */ +#define R367TER_TSDILSTKM 0xf27e +#define F367TER_TSFIFO_DILSTK_HI 0xf27e00ff + +/* TSDILSTKL */ +#define R367TER_TSDILSTKL 0xf27f +#define F367TER_TSFIFO_DILSTK_LO 0xf27f00ff + +/* TSSPEED */ +#define R367TER_TSSPEED 0xf280 +#define F367TER_TSFIFO_OUTSPEED 0xf28000ff + +/* TSSTATUS */ +#define R367TER_TSSTATUS 0xf281 +#define F367TER_TSFIFO_LINEOK 0xf2810080 +#define F367TER_TSFIFO_ERROR 0xf2810040 +#define F367TER_TSFIFO_DATA7 0xf2810020 +#define F367TER_TSFIFO_NOSYNC 0xf2810010 +#define F367TER_ISCR_INITIALIZED 0xf2810008 +#define F367TER_ISCR_UPDATED 0xf2810004 +#define F367TER_SOFFIFO_UNREGUL 0xf2810002 +#define F367TER_DIL_READY 0xf2810001 + +/* TSSTATUS2 */ +#define R367TER_TSSTATUS2 0xf282 +#define F367TER_TSFIFO_DEMODSEL 0xf2820080 +#define F367TER_TSFIFOSPEED_STORE 0xf2820040 +#define F367TER_DILXX_RESET 0xf2820020 +#define F367TER_TSSERIAL_IMPOSSIBLE 0xf2820010 +#define F367TER_TSFIFO_UNDERSPEED 0xf2820008 +#define F367TER_BITSPEED_EVENT 0xf2820004 +#define F367TER_UL_SCRAMBDETECT 0xf2820002 +#define F367TER_ULDTV67_FALSELOCK 0xf2820001 + +/* TSBITRATEM */ +#define R367TER_TSBITRATEM 0xf283 +#define F367TER_TSFIFO_BITRATE_HI 0xf28300ff + +/* TSBITRATEL */ +#define R367TER_TSBITRATEL 0xf284 +#define F367TER_TSFIFO_BITRATE_LO 0xf28400ff + +/* TSPACKLENM */ +#define R367TER_TSPACKLENM 0xf285 +#define F367TER_TSFIFO_PACKCPT 0xf28500e0 +#define F367TER_DIL_RPLEN_HI 0xf285001f + +/* TSPACKLENL */ +#define R367TER_TSPACKLENL 0xf286 +#define F367TER_DIL_RPLEN_LO 0xf28600ff + +/* TSBLOCLENM */ +#define R367TER_TSBLOCLENM 0xf287 +#define F367TER_TSFIFO_PFLEN_HI 0xf28700ff + +/* TSBLOCLENL */ +#define R367TER_TSBLOCLENL 0xf288 +#define F367TER_TSFIFO_PFLEN_LO 0xf28800ff + +/* TSDLYH */ +#define R367TER_TSDLYH 0xf289 +#define F367TER_SOFFIFO_TSTIMEVALID 0xf2890080 +#define F367TER_SOFFIFO_SPEEDUP 0xf2890040 +#define F367TER_SOFFIFO_STOP 0xf2890020 +#define F367TER_SOFFIFO_REGULATED 0xf2890010 +#define F367TER_SOFFIFO_REALSBOFF_HI 0xf289000f + +/* TSDLYM */ +#define R367TER_TSDLYM 0xf28a +#define F367TER_SOFFIFO_REALSBOFF_MED 0xf28a00ff + +/* TSDLYL */ +#define R367TER_TSDLYL 0xf28b +#define F367TER_SOFFIFO_REALSBOFF_LO 0xf28b00ff + +/* TSNPDAV */ +#define R367TER_TSNPDAV 0xf28c +#define F367TER_TSNPD_AVERAGE 0xf28c00ff + +/* TSBUFSTATH */ +#define R367TER_TSBUFSTATH 0xf28d +#define F367TER_TSISCR_3BYTES 0xf28d0080 +#define F367TER_TSISCR_NEWDATA 0xf28d0040 +#define F367TER_TSISCR_BUFSTAT_HI 0xf28d003f + +/* TSBUFSTATM */ +#define R367TER_TSBUFSTATM 0xf28e +#define F367TER_TSISCR_BUFSTAT_MED 0xf28e00ff + +/* TSBUFSTATL */ +#define R367TER_TSBUFSTATL 0xf28f +#define F367TER_TSISCR_BUFSTAT_LO 0xf28f00ff + +/* TSDEBUGM */ +#define R367TER_TSDEBUGM 0xf290 +#define F367TER_TSFIFO_ILLPACKET 0xf2900080 +#define F367TER_DIL_NOSYNC 0xf2900040 +#define F367TER_DIL_ISCR 0xf2900020 +#define F367TER_DILOUT_BSYNCB 0xf2900010 +#define F367TER_TSFIFO_EMPTYPKT 0xf2900008 +#define F367TER_TSFIFO_EMPTYRD 0xf2900004 +#define F367TER_SOFFIFO_STOPM 0xf2900002 +#define F367TER_SOFFIFO_SPEEDUPM 0xf2900001 + +/* TSDEBUGL */ +#define R367TER_TSDEBUGL 0xf291 +#define F367TER_TSFIFO_PACKLENFAIL 0xf2910080 +#define F367TER_TSFIFO_SYNCBFAIL 0xf2910040 +#define F367TER_TSFIFO_VITLIBRE 0xf2910020 +#define F367TER_TSFIFO_BOOSTSPEEDM 0xf2910010 +#define F367TER_TSFIFO_UNDERSPEEDM 0xf2910008 +#define F367TER_TSFIFO_ERROR_EVNT 0xf2910004 +#define F367TER_TSFIFO_FULL 0xf2910002 +#define F367TER_TSFIFO_OVERFLOWM 0xf2910001 + +/* TSDLYSETH */ +#define R367TER_TSDLYSETH 0xf292 +#define F367TER_SOFFIFO_OFFSET 0xf29200e0 +#define F367TER_SOFFIFO_SYMBOFFSET_HI 0xf292001f + +/* TSDLYSETM */ +#define R367TER_TSDLYSETM 0xf293 +#define F367TER_SOFFIFO_SYMBOFFSET_MED 0xf29300ff + +/* TSDLYSETL */ +#define R367TER_TSDLYSETL 0xf294 +#define F367TER_SOFFIFO_SYMBOFFSET_LO 0xf29400ff + +/* TSOBSCFG */ +#define R367TER_TSOBSCFG 0xf295 +#define F367TER_TSFIFO_OBSCFG 0xf29500ff + +/* TSOBSM */ +#define R367TER_TSOBSM 0xf296 +#define F367TER_TSFIFO_OBSDATA_HI 0xf29600ff + +/* TSOBSL */ +#define R367TER_TSOBSL 0xf297 +#define F367TER_TSFIFO_OBSDATA_LO 0xf29700ff + +/* ERRCTRL1 */ +#define R367TER_ERRCTRL1 0xf298 +#define F367TER_ERR_SRC1 0xf29800f0 +#define F367TER_ERRCTRL1_3 0xf2980008 +#define F367TER_NUM_EVT1 0xf2980007 + +/* ERRCNT1H */ +#define R367TER_ERRCNT1H 0xf299 +#define F367TER_ERRCNT1_OLDVALUE 0xf2990080 +#define F367TER_ERR_CNT1 0xf299007f + +/* ERRCNT1M */ +#define R367TER_ERRCNT1M 0xf29a +#define F367TER_ERR_CNT1_HI 0xf29a00ff + +/* ERRCNT1L */ +#define R367TER_ERRCNT1L 0xf29b +#define F367TER_ERR_CNT1_LO 0xf29b00ff + +/* ERRCTRL2 */ +#define R367TER_ERRCTRL2 0xf29c +#define F367TER_ERR_SRC2 0xf29c00f0 +#define F367TER_ERRCTRL2_3 0xf29c0008 +#define F367TER_NUM_EVT2 0xf29c0007 + +/* ERRCNT2H */ +#define R367TER_ERRCNT2H 0xf29d +#define F367TER_ERRCNT2_OLDVALUE 0xf29d0080 +#define F367TER_ERR_CNT2_HI 0xf29d007f + +/* ERRCNT2M */ +#define R367TER_ERRCNT2M 0xf29e +#define F367TER_ERR_CNT2_MED 0xf29e00ff + +/* ERRCNT2L */ +#define R367TER_ERRCNT2L 0xf29f +#define F367TER_ERR_CNT2_LO 0xf29f00ff + +/* FECSPY */ +#define R367TER_FECSPY 0xf2a0 +#define F367TER_SPY_ENABLE 0xf2a00080 +#define F367TER_NO_SYNCBYTE 0xf2a00040 +#define F367TER_SERIAL_MODE 0xf2a00020 +#define F367TER_UNUSUAL_PACKET 0xf2a00010 +#define F367TER_BERMETER_DATAMODE 0xf2a0000c +#define F367TER_BERMETER_LMODE 0xf2a00002 +#define F367TER_BERMETER_RESET 0xf2a00001 + +/* FSPYCFG */ +#define R367TER_FSPYCFG 0xf2a1 +#define F367TER_FECSPY_INPUT 0xf2a100c0 +#define F367TER_RST_ON_ERROR 0xf2a10020 +#define F367TER_ONE_SHOT 0xf2a10010 +#define F367TER_I2C_MOD 0xf2a1000c +#define F367TER_SPY_HYSTERESIS 0xf2a10003 + +/* FSPYDATA */ +#define R367TER_FSPYDATA 0xf2a2 +#define F367TER_SPY_STUFFING 0xf2a20080 +#define F367TER_NOERROR_PKTJITTER 0xf2a20040 +#define F367TER_SPY_CNULLPKT 0xf2a20020 +#define F367TER_SPY_OUTDATA_MODE 0xf2a2001f + +/* FSPYOUT */ +#define R367TER_FSPYOUT 0xf2a3 +#define F367TER_FSPY_DIRECT 0xf2a30080 +#define F367TER_FSPYOUT_6 0xf2a30040 +#define F367TER_SPY_OUTDATA_BUS 0xf2a30038 +#define F367TER_STUFF_MODE 0xf2a30007 + +/* FSTATUS */ +#define R367TER_FSTATUS 0xf2a4 +#define F367TER_SPY_ENDSIM 0xf2a40080 +#define F367TER_VALID_SIM 0xf2a40040 +#define F367TER_FOUND_SIGNAL 0xf2a40020 +#define F367TER_DSS_SYNCBYTE 0xf2a40010 +#define F367TER_RESULT_STATE 0xf2a4000f + +/* FGOODPACK */ +#define R367TER_FGOODPACK 0xf2a5 +#define F367TER_FGOOD_PACKET 0xf2a500ff + +/* FPACKCNT */ +#define R367TER_FPACKCNT 0xf2a6 +#define F367TER_FPACKET_COUNTER 0xf2a600ff + +/* FSPYMISC */ +#define R367TER_FSPYMISC 0xf2a7 +#define F367TER_FLABEL_COUNTER 0xf2a700ff + +/* FBERCPT4 */ +#define R367TER_FBERCPT4 0xf2a8 +#define F367TER_FBERMETER_CPT5 0xf2a800ff + +/* FBERCPT3 */ +#define R367TER_FBERCPT3 0xf2a9 +#define F367TER_FBERMETER_CPT4 0xf2a900ff + +/* FBERCPT2 */ +#define R367TER_FBERCPT2 0xf2aa +#define F367TER_FBERMETER_CPT3 0xf2aa00ff + +/* FBERCPT1 */ +#define R367TER_FBERCPT1 0xf2ab +#define F367TER_FBERMETER_CPT2 0xf2ab00ff + +/* FBERCPT0 */ +#define R367TER_FBERCPT0 0xf2ac +#define F367TER_FBERMETER_CPT1 0xf2ac00ff + +/* FBERERR2 */ +#define R367TER_FBERERR2 0xf2ad +#define F367TER_FBERMETER_ERR_HI 0xf2ad00ff + +/* FBERERR1 */ +#define R367TER_FBERERR1 0xf2ae +#define F367TER_FBERMETER_ERR_MED 0xf2ae00ff + +/* FBERERR0 */ +#define R367TER_FBERERR0 0xf2af +#define F367TER_FBERMETER_ERR_LO 0xf2af00ff + +/* FSTATESM */ +#define R367TER_FSTATESM 0xf2b0 +#define F367TER_RSTATE_F 0xf2b00080 +#define F367TER_RSTATE_E 0xf2b00040 +#define F367TER_RSTATE_D 0xf2b00020 +#define F367TER_RSTATE_C 0xf2b00010 +#define F367TER_RSTATE_B 0xf2b00008 +#define F367TER_RSTATE_A 0xf2b00004 +#define F367TER_RSTATE_9 0xf2b00002 +#define F367TER_RSTATE_8 0xf2b00001 + +/* FSTATESL */ +#define R367TER_FSTATESL 0xf2b1 +#define F367TER_RSTATE_7 0xf2b10080 +#define F367TER_RSTATE_6 0xf2b10040 +#define F367TER_RSTATE_5 0xf2b10020 +#define F367TER_RSTATE_4 0xf2b10010 +#define F367TER_RSTATE_3 0xf2b10008 +#define F367TER_RSTATE_2 0xf2b10004 +#define F367TER_RSTATE_1 0xf2b10002 +#define F367TER_RSTATE_0 0xf2b10001 + +/* FSPYBER */ +#define R367TER_FSPYBER 0xf2b2 +#define F367TER_FSPYBER_7 0xf2b20080 +#define F367TER_FSPYOBS_XORREAD 0xf2b20040 +#define F367TER_FSPYBER_OBSMODE 0xf2b20020 +#define F367TER_FSPYBER_SYNCBYTE 0xf2b20010 +#define F367TER_FSPYBER_UNSYNC 0xf2b20008 +#define F367TER_FSPYBER_CTIME 0xf2b20007 + +/* FSPYDISTM */ +#define R367TER_FSPYDISTM 0xf2b3 +#define F367TER_PKTTIME_DISTANCE_HI 0xf2b300ff + +/* FSPYDISTL */ +#define R367TER_FSPYDISTL 0xf2b4 +#define F367TER_PKTTIME_DISTANCE_LO 0xf2b400ff + +/* FSPYOBS7 */ +#define R367TER_FSPYOBS7 0xf2b8 +#define F367TER_FSPYOBS_SPYFAIL 0xf2b80080 +#define F367TER_FSPYOBS_SPYFAIL1 0xf2b80040 +#define F367TER_FSPYOBS_ERROR 0xf2b80020 +#define F367TER_FSPYOBS_STROUT 0xf2b80010 +#define F367TER_FSPYOBS_RESULTSTATE1 0xf2b8000f + +/* FSPYOBS6 */ +#define R367TER_FSPYOBS6 0xf2b9 +#define F367TER_FSPYOBS_RESULTSTATe0 0xf2b900f0 +#define F367TER_FSPYOBS_RESULTSTATEM1 0xf2b9000f + +/* FSPYOBS5 */ +#define R367TER_FSPYOBS5 0xf2ba +#define F367TER_FSPYOBS_BYTEOFPACKET1 0xf2ba00ff + +/* FSPYOBS4 */ +#define R367TER_FSPYOBS4 0xf2bb +#define F367TER_FSPYOBS_BYTEVALUE1 0xf2bb00ff + +/* FSPYOBS3 */ +#define R367TER_FSPYOBS3 0xf2bc +#define F367TER_FSPYOBS_DATA1 0xf2bc00ff + +/* FSPYOBS2 */ +#define R367TER_FSPYOBS2 0xf2bd +#define F367TER_FSPYOBS_DATa0 0xf2bd00ff + +/* FSPYOBS1 */ +#define R367TER_FSPYOBS1 0xf2be +#define F367TER_FSPYOBS_DATAM1 0xf2be00ff + +/* FSPYOBS0 */ +#define R367TER_FSPYOBS0 0xf2bf +#define F367TER_FSPYOBS_DATAM2 0xf2bf00ff + +/* SFDEMAP */ +#define R367TER_SFDEMAP 0xf2c0 +#define F367TER_SFDEMAP_7 0xf2c00080 +#define F367TER_SFEC_K_DIVIDER_VIT 0xf2c0007f + +/* SFERROR */ +#define R367TER_SFERROR 0xf2c1 +#define F367TER_SFEC_REGERR_VIT 0xf2c100ff + +/* SFAVSR */ +#define R367TER_SFAVSR 0xf2c2 +#define F367TER_SFEC_SUMERRORS 0xf2c20080 +#define F367TER_SERROR_MAXMODE 0xf2c20040 +#define F367TER_SN_SFEC 0xf2c20030 +#define F367TER_KDIV_MODE_SFEC 0xf2c2000c +#define F367TER_SFAVSR_1 0xf2c20002 +#define F367TER_SFAVSR_0 0xf2c20001 + +/* SFECSTATUS */ +#define R367TER_SFECSTATUS 0xf2c3 +#define F367TER_SFEC_ON 0xf2c30080 +#define F367TER_SFSTATUS_6 0xf2c30040 +#define F367TER_SFSTATUS_5 0xf2c30020 +#define F367TER_SFSTATUS_4 0xf2c30010 +#define F367TER_LOCKEDSFEC 0xf2c30008 +#define F367TER_SFEC_DELOCK 0xf2c30004 +#define F367TER_SFEC_DEMODSEL1 0xf2c30002 +#define F367TER_SFEC_OVFON 0xf2c30001 + +/* SFKDIV12 */ +#define R367TER_SFKDIV12 0xf2c4 +#define F367TER_SFECKDIV12_MAN 0xf2c40080 +#define F367TER_SFEC_K_DIVIDER_12 0xf2c4007f + +/* SFKDIV23 */ +#define R367TER_SFKDIV23 0xf2c5 +#define F367TER_SFECKDIV23_MAN 0xf2c50080 +#define F367TER_SFEC_K_DIVIDER_23 0xf2c5007f + +/* SFKDIV34 */ +#define R367TER_SFKDIV34 0xf2c6 +#define F367TER_SFECKDIV34_MAN 0xf2c60080 +#define F367TER_SFEC_K_DIVIDER_34 0xf2c6007f + +/* SFKDIV56 */ +#define R367TER_SFKDIV56 0xf2c7 +#define F367TER_SFECKDIV56_MAN 0xf2c70080 +#define F367TER_SFEC_K_DIVIDER_56 0xf2c7007f + +/* SFKDIV67 */ +#define R367TER_SFKDIV67 0xf2c8 +#define F367TER_SFECKDIV67_MAN 0xf2c80080 +#define F367TER_SFEC_K_DIVIDER_67 0xf2c8007f + +/* SFKDIV78 */ +#define R367TER_SFKDIV78 0xf2c9 +#define F367TER_SFECKDIV78_MAN 0xf2c90080 +#define F367TER_SFEC_K_DIVIDER_78 0xf2c9007f + +/* SFDILSTKM */ +#define R367TER_SFDILSTKM 0xf2ca +#define F367TER_SFEC_PACKCPT 0xf2ca00e0 +#define F367TER_SFEC_DILSTK_HI 0xf2ca001f + +/* SFDILSTKL */ +#define R367TER_SFDILSTKL 0xf2cb +#define F367TER_SFEC_DILSTK_LO 0xf2cb00ff + +/* SFSTATUS */ +#define R367TER_SFSTATUS 0xf2cc +#define F367TER_SFEC_LINEOK 0xf2cc0080 +#define F367TER_SFEC_ERROR 0xf2cc0040 +#define F367TER_SFEC_DATA7 0xf2cc0020 +#define F367TER_SFEC_OVERFLOW 0xf2cc0010 +#define F367TER_SFEC_DEMODSEL2 0xf2cc0008 +#define F367TER_SFEC_NOSYNC 0xf2cc0004 +#define F367TER_SFEC_UNREGULA 0xf2cc0002 +#define F367TER_SFEC_READY 0xf2cc0001 + +/* SFDLYH */ +#define R367TER_SFDLYH 0xf2cd +#define F367TER_SFEC_TSTIMEVALID 0xf2cd0080 +#define F367TER_SFEC_SPEEDUP 0xf2cd0040 +#define F367TER_SFEC_STOP 0xf2cd0020 +#define F367TER_SFEC_REGULATED 0xf2cd0010 +#define F367TER_SFEC_REALSYMBOFFSET 0xf2cd000f + +/* SFDLYM */ +#define R367TER_SFDLYM 0xf2ce +#define F367TER_SFEC_REALSYMBOFFSET_HI 0xf2ce00ff + +/* SFDLYL */ +#define R367TER_SFDLYL 0xf2cf +#define F367TER_SFEC_REALSYMBOFFSET_LO 0xf2cf00ff + +/* SFDLYSETH */ +#define R367TER_SFDLYSETH 0xf2d0 +#define F367TER_SFEC_OFFSET 0xf2d000e0 +#define F367TER_SFECDLYSETH_4 0xf2d00010 +#define F367TER_RST_SFEC 0xf2d00008 +#define F367TER_SFECDLYSETH_2 0xf2d00004 +#define F367TER_SFEC_DISABLE 0xf2d00002 +#define F367TER_SFEC_UNREGUL 0xf2d00001 + +/* SFDLYSETM */ +#define R367TER_SFDLYSETM 0xf2d1 +#define F367TER_SFECDLYSETM_7 0xf2d10080 +#define F367TER_SFEC_SYMBOFFSET_HI 0xf2d1007f + +/* SFDLYSETL */ +#define R367TER_SFDLYSETL 0xf2d2 +#define F367TER_SFEC_SYMBOFFSET_LO 0xf2d200ff + +/* SFOBSCFG */ +#define R367TER_SFOBSCFG 0xf2d3 +#define F367TER_SFEC_OBSCFG 0xf2d300ff + +/* SFOBSM */ +#define R367TER_SFOBSM 0xf2d4 +#define F367TER_SFEC_OBSDATA_HI 0xf2d400ff + +/* SFOBSL */ +#define R367TER_SFOBSL 0xf2d5 +#define F367TER_SFEC_OBSDATA_LO 0xf2d500ff + +/* SFECINFO */ +#define R367TER_SFECINFO 0xf2d6 +#define F367TER_SFECINFO_7 0xf2d60080 +#define F367TER_SFEC_SYNCDLSB 0xf2d60070 +#define F367TER_SFCE_S1cPHASE 0xf2d6000f + +/* SFERRCTRL */ +#define R367TER_SFERRCTRL 0xf2d8 +#define F367TER_SFEC_ERR_SOURCE 0xf2d800f0 +#define F367TER_SFERRCTRL_3 0xf2d80008 +#define F367TER_SFEC_NUM_EVENT 0xf2d80007 + +/* SFERRCNTH */ +#define R367TER_SFERRCNTH 0xf2d9 +#define F367TER_SFERRC_OLDVALUE 0xf2d90080 +#define F367TER_SFEC_ERR_CNT 0xf2d9007f + +/* SFERRCNTM */ +#define R367TER_SFERRCNTM 0xf2da +#define F367TER_SFEC_ERR_CNT_HI 0xf2da00ff + +/* SFERRCNTL */ +#define R367TER_SFERRCNTL 0xf2db +#define F367TER_SFEC_ERR_CNT_LO 0xf2db00ff + +/* SYMBRATEM */ +#define R367TER_SYMBRATEM 0xf2e0 +#define F367TER_DEFGEN_SYMBRATE_HI 0xf2e000ff + +/* SYMBRATEL */ +#define R367TER_SYMBRATEL 0xf2e1 +#define F367TER_DEFGEN_SYMBRATE_LO 0xf2e100ff + +/* SYMBSTATUS */ +#define R367TER_SYMBSTATUS 0xf2e2 +#define F367TER_SYMBDLINE2_OFF 0xf2e20080 +#define F367TER_SDDL_REINIT1 0xf2e20040 +#define F367TER_SDD_REINIT1 0xf2e20020 +#define F367TER_TOKENID_ERROR 0xf2e20010 +#define F367TER_SYMBRATE_OVERFLOW 0xf2e20008 +#define F367TER_SYMBRATE_UNDERFLOW 0xf2e20004 +#define F367TER_TOKENID_RSTEVENT 0xf2e20002 +#define F367TER_TOKENID_RESET1 0xf2e20001 + +/* SYMBCFG */ +#define R367TER_SYMBCFG 0xf2e3 +#define F367TER_SYMBCFG_7 0xf2e30080 +#define F367TER_SYMBCFG_6 0xf2e30040 +#define F367TER_SYMBCFG_5 0xf2e30020 +#define F367TER_SYMBCFG_4 0xf2e30010 +#define F367TER_SYMRATE_FSPEED 0xf2e3000c +#define F367TER_SYMRATE_SSPEED 0xf2e30003 + +/* SYMBFIFOM */ +#define R367TER_SYMBFIFOM 0xf2e4 +#define F367TER_SYMBFIFOM_7 0xf2e40080 +#define F367TER_SYMBFIFOM_6 0xf2e40040 +#define F367TER_DEFGEN_SYMFIFO_HI 0xf2e4003f + +/* SYMBFIFOL */ +#define R367TER_SYMBFIFOL 0xf2e5 +#define F367TER_DEFGEN_SYMFIFO_LO 0xf2e500ff + +/* SYMBOFFSM */ +#define R367TER_SYMBOFFSM 0xf2e6 +#define F367TER_TOKENID_RESET2 0xf2e60080 +#define F367TER_SDDL_REINIT2 0xf2e60040 +#define F367TER_SDD_REINIT2 0xf2e60020 +#define F367TER_SYMBOFFSM_4 0xf2e60010 +#define F367TER_SYMBOFFSM_3 0xf2e60008 +#define F367TER_DEFGEN_SYMBOFFSET_HI 0xf2e60007 + +/* SYMBOFFSL */ +#define R367TER_SYMBOFFSL 0xf2e7 +#define F367TER_DEFGEN_SYMBOFFSET_LO 0xf2e700ff + +/* DEBUG_LT4 */ +#define R367TER_DEBUG_LT4 0xf400 +#define F367TER_F_DEBUG_LT4 0xf40000ff + +/* DEBUG_LT5 */ +#define R367TER_DEBUG_LT5 0xf401 +#define F367TER_F_DEBUG_LT5 0xf40100ff + +/* DEBUG_LT6 */ +#define R367TER_DEBUG_LT6 0xf402 +#define F367TER_F_DEBUG_LT6 0xf40200ff + +/* DEBUG_LT7 */ +#define R367TER_DEBUG_LT7 0xf403 +#define F367TER_F_DEBUG_LT7 0xf40300ff + +/* DEBUG_LT8 */ +#define R367TER_DEBUG_LT8 0xf404 +#define F367TER_F_DEBUG_LT8 0xf40400ff + +/* DEBUG_LT9 */ +#define R367TER_DEBUG_LT9 0xf405 +#define F367TER_F_DEBUG_LT9 0xf40500ff + +/* ID */ +#define R367CAB_ID 0xf000 +#define F367CAB_IDENTIFICATIONREGISTER 0xf00000ff + +/* I2CRPT */ +#define R367CAB_I2CRPT 0xf001 +#define F367CAB_I2CT_ON 0xf0010080 +#define F367CAB_ENARPT_LEVEL 0xf0010070 +#define F367CAB_SCLT_DELAY 0xf0010008 +#define F367CAB_SCLT_NOD 0xf0010004 +#define F367CAB_STOP_ENABLE 0xf0010002 +#define F367CAB_SDAT_NOD 0xf0010001 + +/* TOPCTRL */ +#define R367CAB_TOPCTRL 0xf002 +#define F367CAB_STDBY 0xf0020080 +#define F367CAB_STDBY_CORE 0xf0020020 +#define F367CAB_QAM_COFDM 0xf0020010 +#define F367CAB_TS_DIS 0xf0020008 +#define F367CAB_DIR_CLK_216 0xf0020004 + +/* IOCFG0 */ +#define R367CAB_IOCFG0 0xf003 +#define F367CAB_OP0_SD 0xf0030080 +#define F367CAB_OP0_VAL 0xf0030040 +#define F367CAB_OP0_OD 0xf0030020 +#define F367CAB_OP0_INV 0xf0030010 +#define F367CAB_OP0_DACVALUE_HI 0xf003000f + +/* DAc0R */ +#define R367CAB_DAC0R 0xf004 +#define F367CAB_OP0_DACVALUE_LO 0xf00400ff + +/* IOCFG1 */ +#define R367CAB_IOCFG1 0xf005 +#define F367CAB_IP0 0xf0050040 +#define F367CAB_OP1_OD 0xf0050020 +#define F367CAB_OP1_INV 0xf0050010 +#define F367CAB_OP1_DACVALUE_HI 0xf005000f + +/* DAC1R */ +#define R367CAB_DAC1R 0xf006 +#define F367CAB_OP1_DACVALUE_LO 0xf00600ff + +/* IOCFG2 */ +#define R367CAB_IOCFG2 0xf007 +#define F367CAB_OP2_LOCK_CONF 0xf00700e0 +#define F367CAB_OP2_OD 0xf0070010 +#define F367CAB_OP2_VAL 0xf0070008 +#define F367CAB_OP1_LOCK_CONF 0xf0070007 + +/* SDFR */ +#define R367CAB_SDFR 0xf008 +#define F367CAB_OP0_FREQ 0xf00800f0 +#define F367CAB_OP1_FREQ 0xf008000f + +/* AUX_CLK */ +#define R367CAB_AUX_CLK 0xf00a +#define F367CAB_AUXFEC_CTL 0xf00a00c0 +#define F367CAB_DIS_CKX4 0xf00a0020 +#define F367CAB_CKSEL 0xf00a0018 +#define F367CAB_CKDIV_PROG 0xf00a0006 +#define F367CAB_AUXCLK_ENA 0xf00a0001 + +/* FREESYS1 */ +#define R367CAB_FREESYS1 0xf00b +#define F367CAB_FREESYS_1 0xf00b00ff + +/* FREESYS2 */ +#define R367CAB_FREESYS2 0xf00c +#define F367CAB_FREESYS_2 0xf00c00ff + +/* FREESYS3 */ +#define R367CAB_FREESYS3 0xf00d +#define F367CAB_FREESYS_3 0xf00d00ff + +/* GPIO_CFG */ +#define R367CAB_GPIO_CFG 0xf00e +#define F367CAB_GPIO7_OD 0xf00e0080 +#define F367CAB_GPIO7_CFG 0xf00e0040 +#define F367CAB_GPIO6_OD 0xf00e0020 +#define F367CAB_GPIO6_CFG 0xf00e0010 +#define F367CAB_GPIO5_OD 0xf00e0008 +#define F367CAB_GPIO5_CFG 0xf00e0004 +#define F367CAB_GPIO4_OD 0xf00e0002 +#define F367CAB_GPIO4_CFG 0xf00e0001 + +/* GPIO_CMD */ +#define R367CAB_GPIO_CMD 0xf00f +#define F367CAB_GPIO7_VAL 0xf00f0008 +#define F367CAB_GPIO6_VAL 0xf00f0004 +#define F367CAB_GPIO5_VAL 0xf00f0002 +#define F367CAB_GPIO4_VAL 0xf00f0001 + +/* TSTRES */ +#define R367CAB_TSTRES 0xf0c0 +#define F367CAB_FRES_DISPLAY 0xf0c00080 +#define F367CAB_FRES_FIFO_AD 0xf0c00020 +#define F367CAB_FRESRS 0xf0c00010 +#define F367CAB_FRESACS 0xf0c00008 +#define F367CAB_FRESFEC 0xf0c00004 +#define F367CAB_FRES_PRIF 0xf0c00002 +#define F367CAB_FRESCORE 0xf0c00001 + +/* ANACTRL */ +#define R367CAB_ANACTRL 0xf0c1 +#define F367CAB_BYPASS_XTAL 0xf0c10040 +#define F367CAB_BYPASS_PLLXN 0xf0c1000c +#define F367CAB_DIS_PAD_OSC 0xf0c10002 +#define F367CAB_STDBY_PLLXN 0xf0c10001 + +/* TSTBUS */ +#define R367CAB_TSTBUS 0xf0c2 +#define F367CAB_TS_BYTE_CLK_INV 0xf0c20080 +#define F367CAB_CFG_IP 0xf0c20070 +#define F367CAB_CFG_TST 0xf0c2000f + +/* RF_AGC1 */ +#define R367CAB_RF_AGC1 0xf0d4 +#define F367CAB_RF_AGC1_LEVEL_HI 0xf0d400ff + +/* RF_AGC2 */ +#define R367CAB_RF_AGC2 0xf0d5 +#define F367CAB_REF_ADGP 0xf0d50080 +#define F367CAB_STDBY_ADCGP 0xf0d50020 +#define F367CAB_RF_AGC1_LEVEL_LO 0xf0d50003 + +/* ANADIGCTRL */ +#define R367CAB_ANADIGCTRL 0xf0d7 +#define F367CAB_SEL_CLKDEM 0xf0d70020 +#define F367CAB_EN_BUFFER_Q 0xf0d70010 +#define F367CAB_EN_BUFFER_I 0xf0d70008 +#define F367CAB_ADC_RIS_EGDE 0xf0d70004 +#define F367CAB_SGN_ADC 0xf0d70002 +#define F367CAB_SEL_AD12_SYNC 0xf0d70001 + +/* PLLMDIV */ +#define R367CAB_PLLMDIV 0xf0d8 +#define F367CAB_PLL_MDIV 0xf0d800ff + +/* PLLNDIV */ +#define R367CAB_PLLNDIV 0xf0d9 +#define F367CAB_PLL_NDIV 0xf0d900ff + +/* PLLSETUP */ +#define R367CAB_PLLSETUP 0xf0da +#define F367CAB_PLL_PDIV 0xf0da0070 +#define F367CAB_PLL_KDIV 0xf0da000f + +/* DUAL_AD12 */ +#define R367CAB_DUAL_AD12 0xf0db +#define F367CAB_FS20M 0xf0db0020 +#define F367CAB_FS50M 0xf0db0010 +#define F367CAB_INMODe0 0xf0db0008 +#define F367CAB_POFFQ 0xf0db0004 +#define F367CAB_POFFI 0xf0db0002 +#define F367CAB_INMODE1 0xf0db0001 + +/* TSTBIST */ +#define R367CAB_TSTBIST 0xf0dc +#define F367CAB_TST_BYP_CLK 0xf0dc0080 +#define F367CAB_TST_GCLKENA_STD 0xf0dc0040 +#define F367CAB_TST_GCLKENA 0xf0dc0020 +#define F367CAB_TST_MEMBIST 0xf0dc001f + +/* CTRL_1 */ +#define R367CAB_CTRL_1 0xf402 +#define F367CAB_SOFT_RST 0xf4020080 +#define F367CAB_EQU_RST 0xf4020008 +#define F367CAB_CRL_RST 0xf4020004 +#define F367CAB_TRL_RST 0xf4020002 +#define F367CAB_AGC_RST 0xf4020001 + +/* CTRL_2 */ +#define R367CAB_CTRL_2 0xf403 +#define F367CAB_DEINT_RST 0xf4030008 +#define F367CAB_RS_RST 0xf4030004 + +/* IT_STATUS1 */ +#define R367CAB_IT_STATUS1 0xf408 +#define F367CAB_SWEEP_OUT 0xf4080080 +#define F367CAB_FSM_CRL 0xf4080040 +#define F367CAB_CRL_LOCK 0xf4080020 +#define F367CAB_MFSM 0xf4080010 +#define F367CAB_TRL_LOCK 0xf4080008 +#define F367CAB_TRL_AGC_LIMIT 0xf4080004 +#define F367CAB_ADJ_AGC_LOCK 0xf4080002 +#define F367CAB_AGC_QAM_LOCK 0xf4080001 + +/* IT_STATUS2 */ +#define R367CAB_IT_STATUS2 0xf409 +#define F367CAB_TSMF_CNT 0xf4090080 +#define F367CAB_TSMF_EOF 0xf4090040 +#define F367CAB_TSMF_RDY 0xf4090020 +#define F367CAB_FEC_NOCORR 0xf4090010 +#define F367CAB_SYNCSTATE 0xf4090008 +#define F367CAB_DEINT_LOCK 0xf4090004 +#define F367CAB_FADDING_FRZ 0xf4090002 +#define F367CAB_TAPMON_ALARM 0xf4090001 + +/* IT_EN1 */ +#define R367CAB_IT_EN1 0xf40a +#define F367CAB_SWEEP_OUTE 0xf40a0080 +#define F367CAB_FSM_CRLE 0xf40a0040 +#define F367CAB_CRL_LOCKE 0xf40a0020 +#define F367CAB_MFSME 0xf40a0010 +#define F367CAB_TRL_LOCKE 0xf40a0008 +#define F367CAB_TRL_AGC_LIMITE 0xf40a0004 +#define F367CAB_ADJ_AGC_LOCKE 0xf40a0002 +#define F367CAB_AGC_LOCKE 0xf40a0001 + +/* IT_EN2 */ +#define R367CAB_IT_EN2 0xf40b +#define F367CAB_TSMF_CNTE 0xf40b0080 +#define F367CAB_TSMF_EOFE 0xf40b0040 +#define F367CAB_TSMF_RDYE 0xf40b0020 +#define F367CAB_FEC_NOCORRE 0xf40b0010 +#define F367CAB_SYNCSTATEE 0xf40b0008 +#define F367CAB_DEINT_LOCKE 0xf40b0004 +#define F367CAB_FADDING_FRZE 0xf40b0002 +#define F367CAB_TAPMON_ALARME 0xf40b0001 + +/* CTRL_STATUS */ +#define R367CAB_CTRL_STATUS 0xf40c +#define F367CAB_QAMFEC_LOCK 0xf40c0004 +#define F367CAB_TSMF_LOCK 0xf40c0002 +#define F367CAB_TSMF_ERROR 0xf40c0001 + +/* TEST_CTL */ +#define R367CAB_TEST_CTL 0xf40f +#define F367CAB_TST_BLK_SEL 0xf40f0060 +#define F367CAB_TST_BUS_SEL 0xf40f001f + +/* AGC_CTL */ +#define R367CAB_AGC_CTL 0xf410 +#define F367CAB_AGC_LCK_TH 0xf41000f0 +#define F367CAB_AGC_ACCUMRSTSEL 0xf4100007 + +/* AGC_IF_CFG */ +#define R367CAB_AGC_IF_CFG 0xf411 +#define F367CAB_AGC_IF_BWSEL 0xf41100f0 +#define F367CAB_AGC_IF_FREEZE 0xf4110002 + +/* AGC_RF_CFG */ +#define R367CAB_AGC_RF_CFG 0xf412 +#define F367CAB_AGC_RF_BWSEL 0xf4120070 +#define F367CAB_AGC_RF_FREEZE 0xf4120002 + +/* AGC_PWM_CFG */ +#define R367CAB_AGC_PWM_CFG 0xf413 +#define F367CAB_AGC_RF_PWM_TST 0xf4130080 +#define F367CAB_AGC_RF_PWM_INV 0xf4130040 +#define F367CAB_AGC_IF_PWM_TST 0xf4130008 +#define F367CAB_AGC_IF_PWM_INV 0xf4130004 +#define F367CAB_AGC_PWM_CLKDIV 0xf4130003 + +/* AGC_PWR_REF_L */ +#define R367CAB_AGC_PWR_REF_L 0xf414 +#define F367CAB_AGC_PWRREF_LO 0xf41400ff + +/* AGC_PWR_REF_H */ +#define R367CAB_AGC_PWR_REF_H 0xf415 +#define F367CAB_AGC_PWRREF_HI 0xf4150003 + +/* AGC_RF_TH_L */ +#define R367CAB_AGC_RF_TH_L 0xf416 +#define F367CAB_AGC_RF_TH_LO 0xf41600ff + +/* AGC_RF_TH_H */ +#define R367CAB_AGC_RF_TH_H 0xf417 +#define F367CAB_AGC_RF_TH_HI 0xf417000f + +/* AGC_IF_LTH_L */ +#define R367CAB_AGC_IF_LTH_L 0xf418 +#define F367CAB_AGC_IF_THLO_LO 0xf41800ff + +/* AGC_IF_LTH_H */ +#define R367CAB_AGC_IF_LTH_H 0xf419 +#define F367CAB_AGC_IF_THLO_HI 0xf419000f + +/* AGC_IF_HTH_L */ +#define R367CAB_AGC_IF_HTH_L 0xf41a +#define F367CAB_AGC_IF_THHI_LO 0xf41a00ff + +/* AGC_IF_HTH_H */ +#define R367CAB_AGC_IF_HTH_H 0xf41b +#define F367CAB_AGC_IF_THHI_HI 0xf41b000f + +/* AGC_PWR_RD_L */ +#define R367CAB_AGC_PWR_RD_L 0xf41c +#define F367CAB_AGC_PWR_WORD_LO 0xf41c00ff + +/* AGC_PWR_RD_M */ +#define R367CAB_AGC_PWR_RD_M 0xf41d +#define F367CAB_AGC_PWR_WORD_ME 0xf41d00ff + +/* AGC_PWR_RD_H */ +#define R367CAB_AGC_PWR_RD_H 0xf41e +#define F367CAB_AGC_PWR_WORD_HI 0xf41e0003 + +/* AGC_PWM_IFCMD_L */ +#define R367CAB_AGC_PWM_IFCMD_L 0xf420 +#define F367CAB_AGC_IF_PWMCMD_LO 0xf42000ff + +/* AGC_PWM_IFCMD_H */ +#define R367CAB_AGC_PWM_IFCMD_H 0xf421 +#define F367CAB_AGC_IF_PWMCMD_HI 0xf421000f + +/* AGC_PWM_RFCMD_L */ +#define R367CAB_AGC_PWM_RFCMD_L 0xf422 +#define F367CAB_AGC_RF_PWMCMD_LO 0xf42200ff + +/* AGC_PWM_RFCMD_H */ +#define R367CAB_AGC_PWM_RFCMD_H 0xf423 +#define F367CAB_AGC_RF_PWMCMD_HI 0xf423000f + +/* IQDEM_CFG */ +#define R367CAB_IQDEM_CFG 0xf424 +#define F367CAB_IQDEM_CLK_SEL 0xf4240004 +#define F367CAB_IQDEM_INVIQ 0xf4240002 +#define F367CAB_IQDEM_A2dTYPE 0xf4240001 + +/* MIX_NCO_LL */ +#define R367CAB_MIX_NCO_LL 0xf425 +#define F367CAB_MIX_NCO_INC_LL 0xf42500ff + +/* MIX_NCO_HL */ +#define R367CAB_MIX_NCO_HL 0xf426 +#define F367CAB_MIX_NCO_INC_HL 0xf42600ff + +/* MIX_NCO_HH */ +#define R367CAB_MIX_NCO_HH 0xf427 +#define F367CAB_MIX_NCO_INVCNST 0xf4270080 +#define F367CAB_MIX_NCO_INC_HH 0xf427007f + +/* SRC_NCO_LL */ +#define R367CAB_SRC_NCO_LL 0xf428 +#define F367CAB_SRC_NCO_INC_LL 0xf42800ff + +/* SRC_NCO_LH */ +#define R367CAB_SRC_NCO_LH 0xf429 +#define F367CAB_SRC_NCO_INC_LH 0xf42900ff + +/* SRC_NCO_HL */ +#define R367CAB_SRC_NCO_HL 0xf42a +#define F367CAB_SRC_NCO_INC_HL 0xf42a00ff + +/* SRC_NCO_HH */ +#define R367CAB_SRC_NCO_HH 0xf42b +#define F367CAB_SRC_NCO_INC_HH 0xf42b007f + +/* IQDEM_GAIN_SRC_L */ +#define R367CAB_IQDEM_GAIN_SRC_L 0xf42c +#define F367CAB_GAIN_SRC_LO 0xf42c00ff + +/* IQDEM_GAIN_SRC_H */ +#define R367CAB_IQDEM_GAIN_SRC_H 0xf42d +#define F367CAB_GAIN_SRC_HI 0xf42d0003 + +/* IQDEM_DCRM_CFG_LL */ +#define R367CAB_IQDEM_DCRM_CFG_LL 0xf430 +#define F367CAB_DCRM0_DCIN_L 0xf43000ff + +/* IQDEM_DCRM_CFG_LH */ +#define R367CAB_IQDEM_DCRM_CFG_LH 0xf431 +#define F367CAB_DCRM1_I_DCIN_L 0xf43100fc +#define F367CAB_DCRM0_DCIN_H 0xf4310003 + +/* IQDEM_DCRM_CFG_HL */ +#define R367CAB_IQDEM_DCRM_CFG_HL 0xf432 +#define F367CAB_DCRM1_Q_DCIN_L 0xf43200f0 +#define F367CAB_DCRM1_I_DCIN_H 0xf432000f + +/* IQDEM_DCRM_CFG_HH */ +#define R367CAB_IQDEM_DCRM_CFG_HH 0xf433 +#define F367CAB_DCRM1_FRZ 0xf4330080 +#define F367CAB_DCRM0_FRZ 0xf4330040 +#define F367CAB_DCRM1_Q_DCIN_H 0xf433003f + +/* IQDEM_ADJ_COEFf0 */ +#define R367CAB_IQDEM_ADJ_COEFF0 0xf434 +#define F367CAB_ADJIIR_COEFF10_L 0xf43400ff + +/* IQDEM_ADJ_COEFF1 */ +#define R367CAB_IQDEM_ADJ_COEFF1 0xf435 +#define F367CAB_ADJIIR_COEFF11_L 0xf43500fc +#define F367CAB_ADJIIR_COEFF10_H 0xf4350003 + +/* IQDEM_ADJ_COEFF2 */ +#define R367CAB_IQDEM_ADJ_COEFF2 0xf436 +#define F367CAB_ADJIIR_COEFF12_L 0xf43600f0 +#define F367CAB_ADJIIR_COEFF11_H 0xf436000f + +/* IQDEM_ADJ_COEFF3 */ +#define R367CAB_IQDEM_ADJ_COEFF3 0xf437 +#define F367CAB_ADJIIR_COEFF20_L 0xf43700c0 +#define F367CAB_ADJIIR_COEFF12_H 0xf437003f + +/* IQDEM_ADJ_COEFF4 */ +#define R367CAB_IQDEM_ADJ_COEFF4 0xf438 +#define F367CAB_ADJIIR_COEFF20_H 0xf43800ff + +/* IQDEM_ADJ_COEFF5 */ +#define R367CAB_IQDEM_ADJ_COEFF5 0xf439 +#define F367CAB_ADJIIR_COEFF21_L 0xf43900ff + +/* IQDEM_ADJ_COEFF6 */ +#define R367CAB_IQDEM_ADJ_COEFF6 0xf43a +#define F367CAB_ADJIIR_COEFF22_L 0xf43a00fc +#define F367CAB_ADJIIR_COEFF21_H 0xf43a0003 + +/* IQDEM_ADJ_COEFF7 */ +#define R367CAB_IQDEM_ADJ_COEFF7 0xf43b +#define F367CAB_ADJIIR_COEFF22_H 0xf43b000f + +/* IQDEM_ADJ_EN */ +#define R367CAB_IQDEM_ADJ_EN 0xf43c +#define F367CAB_ALLPASSFILT_EN 0xf43c0008 +#define F367CAB_ADJ_AGC_EN 0xf43c0004 +#define F367CAB_ADJ_COEFF_FRZ 0xf43c0002 +#define F367CAB_ADJ_EN 0xf43c0001 + +/* IQDEM_ADJ_AGC_REF */ +#define R367CAB_IQDEM_ADJ_AGC_REF 0xf43d +#define F367CAB_ADJ_AGC_REF 0xf43d00ff + +/* ALLPASSFILT1 */ +#define R367CAB_ALLPASSFILT1 0xf440 +#define F367CAB_ALLPASSFILT_COEFF1_LO 0xf44000ff + +/* ALLPASSFILT2 */ +#define R367CAB_ALLPASSFILT2 0xf441 +#define F367CAB_ALLPASSFILT_COEFF1_ME 0xf44100ff + +/* ALLPASSFILT3 */ +#define R367CAB_ALLPASSFILT3 0xf442 +#define F367CAB_ALLPASSFILT_COEFF2_LO 0xf44200c0 +#define F367CAB_ALLPASSFILT_COEFF1_HI 0xf442003f + +/* ALLPASSFILT4 */ +#define R367CAB_ALLPASSFILT4 0xf443 +#define F367CAB_ALLPASSFILT_COEFF2_MEL 0xf44300ff + +/* ALLPASSFILT5 */ +#define R367CAB_ALLPASSFILT5 0xf444 +#define F367CAB_ALLPASSFILT_COEFF2_MEH 0xf44400ff + +/* ALLPASSFILT6 */ +#define R367CAB_ALLPASSFILT6 0xf445 +#define F367CAB_ALLPASSFILT_COEFF3_LO 0xf44500f0 +#define F367CAB_ALLPASSFILT_COEFF2_HI 0xf445000f + +/* ALLPASSFILT7 */ +#define R367CAB_ALLPASSFILT7 0xf446 +#define F367CAB_ALLPASSFILT_COEFF3_MEL 0xf44600ff + +/* ALLPASSFILT8 */ +#define R367CAB_ALLPASSFILT8 0xf447 +#define F367CAB_ALLPASSFILT_COEFF3_MEH 0xf44700ff + +/* ALLPASSFILT9 */ +#define R367CAB_ALLPASSFILT9 0xf448 +#define F367CAB_ALLPASSFILT_COEFF4_LO 0xf44800fc +#define F367CAB_ALLPASSFILT_COEFF3_HI 0xf4480003 + +/* ALLPASSFILT10 */ +#define R367CAB_ALLPASSFILT10 0xf449 +#define F367CAB_ALLPASSFILT_COEFF4_ME 0xf44900ff + +/* ALLPASSFILT11 */ +#define R367CAB_ALLPASSFILT11 0xf44a +#define F367CAB_ALLPASSFILT_COEFF4_HI 0xf44a00ff + +/* TRL_AGC_CFG */ +#define R367CAB_TRL_AGC_CFG 0xf450 +#define F367CAB_TRL_AGC_FREEZE 0xf4500080 +#define F367CAB_TRL_AGC_REF 0xf450007f + +/* TRL_LPF_CFG */ +#define R367CAB_TRL_LPF_CFG 0xf454 +#define F367CAB_NYQPOINT_INV 0xf4540040 +#define F367CAB_TRL_SHIFT 0xf4540030 +#define F367CAB_NYQ_COEFF_SEL 0xf454000c +#define F367CAB_TRL_LPF_FREEZE 0xf4540002 +#define F367CAB_TRL_LPF_CRT 0xf4540001 + +/* TRL_LPF_ACQ_GAIN */ +#define R367CAB_TRL_LPF_ACQ_GAIN 0xf455 +#define F367CAB_TRL_GDIR_ACQ 0xf4550070 +#define F367CAB_TRL_GINT_ACQ 0xf4550007 + +/* TRL_LPF_TRK_GAIN */ +#define R367CAB_TRL_LPF_TRK_GAIN 0xf456 +#define F367CAB_TRL_GDIR_TRK 0xf4560070 +#define F367CAB_TRL_GINT_TRK 0xf4560007 + +/* TRL_LPF_OUT_GAIN */ +#define R367CAB_TRL_LPF_OUT_GAIN 0xf457 +#define F367CAB_TRL_GAIN_OUT 0xf4570007 + +/* TRL_LOCKDET_LTH */ +#define R367CAB_TRL_LOCKDET_LTH 0xf458 +#define F367CAB_TRL_LCK_THLO 0xf4580007 + +/* TRL_LOCKDET_HTH */ +#define R367CAB_TRL_LOCKDET_HTH 0xf459 +#define F367CAB_TRL_LCK_THHI 0xf45900ff + +/* TRL_LOCKDET_TRGVAL */ +#define R367CAB_TRL_LOCKDET_TRGVAL 0xf45a +#define F367CAB_TRL_LCK_TRG 0xf45a00ff + +/* IQ_QAM */ +#define R367CAB_IQ_QAM 0xf45c +#define F367CAB_IQ_INPUT 0xf45c0008 +#define F367CAB_DETECT_MODE 0xf45c0007 + +/* FSM_STATE */ +#define R367CAB_FSM_STATE 0xf460 +#define F367CAB_CRL_DFE 0xf4600080 +#define F367CAB_DFE_START 0xf4600040 +#define F367CAB_CTRLG_START 0xf4600030 +#define F367CAB_FSM_FORCESTATE 0xf460000f + +/* FSM_CTL */ +#define R367CAB_FSM_CTL 0xf461 +#define F367CAB_FEC2_EN 0xf4610040 +#define F367CAB_SIT_EN 0xf4610020 +#define F367CAB_TRL_AHEAD 0xf4610010 +#define F367CAB_TRL2_EN 0xf4610008 +#define F367CAB_FSM_EQA1_EN 0xf4610004 +#define F367CAB_FSM_BKP_DIS 0xf4610002 +#define F367CAB_FSM_FORCE_EN 0xf4610001 + +/* FSM_STS */ +#define R367CAB_FSM_STS 0xf462 +#define F367CAB_FSM_STATUS 0xf462000f + +/* FSM_SNR0_HTH */ +#define R367CAB_FSM_SNR0_HTH 0xf463 +#define F367CAB_SNR0_HTH 0xf46300ff + +/* FSM_SNR1_HTH */ +#define R367CAB_FSM_SNR1_HTH 0xf464 +#define F367CAB_SNR1_HTH 0xf46400ff + +/* FSM_SNR2_HTH */ +#define R367CAB_FSM_SNR2_HTH 0xf465 +#define F367CAB_SNR2_HTH 0xf46500ff + +/* FSM_SNR0_LTH */ +#define R367CAB_FSM_SNR0_LTH 0xf466 +#define F367CAB_SNR0_LTH 0xf46600ff + +/* FSM_SNR1_LTH */ +#define R367CAB_FSM_SNR1_LTH 0xf467 +#define F367CAB_SNR1_LTH 0xf46700ff + +/* FSM_EQA1_HTH */ +#define R367CAB_FSM_EQA1_HTH 0xf468 +#define F367CAB_SNR3_HTH_LO 0xf46800f0 +#define F367CAB_EQA1_HTH 0xf468000f + +/* FSM_TEMPO */ +#define R367CAB_FSM_TEMPO 0xf469 +#define F367CAB_SIT 0xf46900c0 +#define F367CAB_WST 0xf4690038 +#define F367CAB_ELT 0xf4690006 +#define F367CAB_SNR3_HTH_HI 0xf4690001 + +/* FSM_CONFIG */ +#define R367CAB_FSM_CONFIG 0xf46a +#define F367CAB_FEC2_DFEOFF 0xf46a0004 +#define F367CAB_PRIT_STATE 0xf46a0002 +#define F367CAB_MODMAP_STATE 0xf46a0001 + +/* EQU_I_TESTTAP_L */ +#define R367CAB_EQU_I_TESTTAP_L 0xf474 +#define F367CAB_I_TEST_TAP_L 0xf47400ff + +/* EQU_I_TESTTAP_M */ +#define R367CAB_EQU_I_TESTTAP_M 0xf475 +#define F367CAB_I_TEST_TAP_M 0xf47500ff + +/* EQU_I_TESTTAP_H */ +#define R367CAB_EQU_I_TESTTAP_H 0xf476 +#define F367CAB_I_TEST_TAP_H 0xf476001f + +/* EQU_TESTAP_CFG */ +#define R367CAB_EQU_TESTAP_CFG 0xf477 +#define F367CAB_TEST_FFE_DFE_SEL 0xf4770040 +#define F367CAB_TEST_TAP_SELECT 0xf477003f + +/* EQU_Q_TESTTAP_L */ +#define R367CAB_EQU_Q_TESTTAP_L 0xf478 +#define F367CAB_Q_TEST_TAP_L 0xf47800ff + +/* EQU_Q_TESTTAP_M */ +#define R367CAB_EQU_Q_TESTTAP_M 0xf479 +#define F367CAB_Q_TEST_TAP_M 0xf47900ff + +/* EQU_Q_TESTTAP_H */ +#define R367CAB_EQU_Q_TESTTAP_H 0xf47a +#define F367CAB_Q_TEST_TAP_H 0xf47a001f + +/* EQU_TAP_CTRL */ +#define R367CAB_EQU_TAP_CTRL 0xf47b +#define F367CAB_MTAP_FRZ 0xf47b0010 +#define F367CAB_PRE_FREEZE 0xf47b0008 +#define F367CAB_DFE_TAPMON_EN 0xf47b0004 +#define F367CAB_FFE_TAPMON_EN 0xf47b0002 +#define F367CAB_MTAP_ONLY 0xf47b0001 + +/* EQU_CTR_CRL_CONTROL_L */ +#define R367CAB_EQU_CTR_CRL_CONTROL_L 0xf47c +#define F367CAB_EQU_CTR_CRL_CONTROL_LO 0xf47c00ff + +/* EQU_CTR_CRL_CONTROL_H */ +#define R367CAB_EQU_CTR_CRL_CONTROL_H 0xf47d +#define F367CAB_EQU_CTR_CRL_CONTROL_HI 0xf47d00ff + +/* EQU_CTR_HIPOW_L */ +#define R367CAB_EQU_CTR_HIPOW_L 0xf47e +#define F367CAB_CTR_HIPOW_L 0xf47e00ff + +/* EQU_CTR_HIPOW_H */ +#define R367CAB_EQU_CTR_HIPOW_H 0xf47f +#define F367CAB_CTR_HIPOW_H 0xf47f00ff + +/* EQU_I_EQU_LO */ +#define R367CAB_EQU_I_EQU_LO 0xf480 +#define F367CAB_EQU_I_EQU_L 0xf48000ff + +/* EQU_I_EQU_HI */ +#define R367CAB_EQU_I_EQU_HI 0xf481 +#define F367CAB_EQU_I_EQU_H 0xf4810003 + +/* EQU_Q_EQU_LO */ +#define R367CAB_EQU_Q_EQU_LO 0xf482 +#define F367CAB_EQU_Q_EQU_L 0xf48200ff + +/* EQU_Q_EQU_HI */ +#define R367CAB_EQU_Q_EQU_HI 0xf483 +#define F367CAB_EQU_Q_EQU_H 0xf4830003 + +/* EQU_MAPPER */ +#define R367CAB_EQU_MAPPER 0xf484 +#define F367CAB_QUAD_AUTO 0xf4840080 +#define F367CAB_QUAD_INV 0xf4840040 +#define F367CAB_QAM_MODE 0xf4840007 + +/* EQU_SWEEP_RATE */ +#define R367CAB_EQU_SWEEP_RATE 0xf485 +#define F367CAB_SNR_PER 0xf48500c0 +#define F367CAB_SWEEP_RATE 0xf485003f + +/* EQU_SNR_LO */ +#define R367CAB_EQU_SNR_LO 0xf486 +#define F367CAB_SNR_LO 0xf48600ff + +/* EQU_SNR_HI */ +#define R367CAB_EQU_SNR_HI 0xf487 +#define F367CAB_SNR_HI 0xf48700ff + +/* EQU_GAMMA_LO */ +#define R367CAB_EQU_GAMMA_LO 0xf488 +#define F367CAB_GAMMA_LO 0xf48800ff + +/* EQU_GAMMA_HI */ +#define R367CAB_EQU_GAMMA_HI 0xf489 +#define F367CAB_GAMMA_ME 0xf48900ff + +/* EQU_ERR_GAIN */ +#define R367CAB_EQU_ERR_GAIN 0xf48a +#define F367CAB_EQA1MU 0xf48a0070 +#define F367CAB_CRL2MU 0xf48a000e +#define F367CAB_GAMMA_HI 0xf48a0001 + +/* EQU_RADIUS */ +#define R367CAB_EQU_RADIUS 0xf48b +#define F367CAB_RADIUS 0xf48b00ff + +/* EQU_FFE_MAINTAP */ +#define R367CAB_EQU_FFE_MAINTAP 0xf48c +#define F367CAB_FFE_MAINTAP_INIT 0xf48c00ff + +/* EQU_FFE_LEAKAGE */ +#define R367CAB_EQU_FFE_LEAKAGE 0xf48e +#define F367CAB_LEAK_PER 0xf48e00f0 +#define F367CAB_EQU_OUTSEL 0xf48e0002 +#define F367CAB_PNT2dFE 0xf48e0001 + +/* EQU_FFE_MAINTAP_POS */ +#define R367CAB_EQU_FFE_MAINTAP_POS 0xf48f +#define F367CAB_FFE_LEAK_EN 0xf48f0080 +#define F367CAB_DFE_LEAK_EN 0xf48f0040 +#define F367CAB_FFE_MAINTAP_POS 0xf48f003f + +/* EQU_GAIN_WIDE */ +#define R367CAB_EQU_GAIN_WIDE 0xf490 +#define F367CAB_DFE_GAIN_WIDE 0xf49000f0 +#define F367CAB_FFE_GAIN_WIDE 0xf490000f + +/* EQU_GAIN_NARROW */ +#define R367CAB_EQU_GAIN_NARROW 0xf491 +#define F367CAB_DFE_GAIN_NARROW 0xf49100f0 +#define F367CAB_FFE_GAIN_NARROW 0xf491000f + +/* EQU_CTR_LPF_GAIN */ +#define R367CAB_EQU_CTR_LPF_GAIN 0xf492 +#define F367CAB_CTR_GTO 0xf4920080 +#define F367CAB_CTR_GDIR 0xf4920070 +#define F367CAB_SWEEP_EN 0xf4920008 +#define F367CAB_CTR_GINT 0xf4920007 + +/* EQU_CRL_LPF_GAIN */ +#define R367CAB_EQU_CRL_LPF_GAIN 0xf493 +#define F367CAB_CRL_GTO 0xf4930080 +#define F367CAB_CRL_GDIR 0xf4930070 +#define F367CAB_SWEEP_DIR 0xf4930008 +#define F367CAB_CRL_GINT 0xf4930007 + +/* EQU_GLOBAL_GAIN */ +#define R367CAB_EQU_GLOBAL_GAIN 0xf494 +#define F367CAB_CRL_GAIN 0xf49400f8 +#define F367CAB_CTR_INC_GAIN 0xf4940004 +#define F367CAB_CTR_FRAC 0xf4940003 + +/* EQU_CRL_LD_SEN */ +#define R367CAB_EQU_CRL_LD_SEN 0xf495 +#define F367CAB_CTR_BADPOINT_EN 0xf4950080 +#define F367CAB_CTR_GAIN 0xf4950070 +#define F367CAB_LIMANEN 0xf4950008 +#define F367CAB_CRL_LD_SEN 0xf4950007 + +/* EQU_CRL_LD_VAL */ +#define R367CAB_EQU_CRL_LD_VAL 0xf496 +#define F367CAB_CRL_BISTH_LIMIT 0xf4960080 +#define F367CAB_CARE_EN 0xf4960040 +#define F367CAB_CRL_LD_PER 0xf4960030 +#define F367CAB_CRL_LD_WST 0xf496000c +#define F367CAB_CRL_LD_TFS 0xf4960003 + +/* EQU_CRL_TFR */ +#define R367CAB_EQU_CRL_TFR 0xf497 +#define F367CAB_CRL_LD_TFR 0xf49700ff + +/* EQU_CRL_BISTH_LO */ +#define R367CAB_EQU_CRL_BISTH_LO 0xf498 +#define F367CAB_CRL_BISTH_LO 0xf49800ff + +/* EQU_CRL_BISTH_HI */ +#define R367CAB_EQU_CRL_BISTH_HI 0xf499 +#define F367CAB_CRL_BISTH_HI 0xf49900ff + +/* EQU_SWEEP_RANGE_LO */ +#define R367CAB_EQU_SWEEP_RANGE_LO 0xf49a +#define F367CAB_SWEEP_RANGE_LO 0xf49a00ff + +/* EQU_SWEEP_RANGE_HI */ +#define R367CAB_EQU_SWEEP_RANGE_HI 0xf49b +#define F367CAB_SWEEP_RANGE_HI 0xf49b00ff + +/* EQU_CRL_LIMITER */ +#define R367CAB_EQU_CRL_LIMITER 0xf49c +#define F367CAB_BISECTOR_EN 0xf49c0080 +#define F367CAB_PHEST128_EN 0xf49c0040 +#define F367CAB_CRL_LIM 0xf49c003f + +/* EQU_MODULUS_MAP */ +#define R367CAB_EQU_MODULUS_MAP 0xf49d +#define F367CAB_PNT_DEPTH 0xf49d00e0 +#define F367CAB_MODULUS_CMP 0xf49d001f + +/* EQU_PNT_GAIN */ +#define R367CAB_EQU_PNT_GAIN 0xf49e +#define F367CAB_PNT_EN 0xf49e0080 +#define F367CAB_MODULUSMAP_EN 0xf49e0040 +#define F367CAB_PNT_GAIN 0xf49e003f + +/* FEC_AC_CTR_0 */ +#define R367CAB_FEC_AC_CTR_0 0xf4a8 +#define F367CAB_BE_BYPASS 0xf4a80020 +#define F367CAB_REFRESH47 0xf4a80010 +#define F367CAB_CT_NBST 0xf4a80008 +#define F367CAB_TEI_ENA 0xf4a80004 +#define F367CAB_DS_ENA 0xf4a80002 +#define F367CAB_TSMF_EN 0xf4a80001 + +/* FEC_AC_CTR_1 */ +#define R367CAB_FEC_AC_CTR_1 0xf4a9 +#define F367CAB_DEINT_DEPTH 0xf4a900ff + +/* FEC_AC_CTR_2 */ +#define R367CAB_FEC_AC_CTR_2 0xf4aa +#define F367CAB_DEINT_M 0xf4aa00f8 +#define F367CAB_DIS_UNLOCK 0xf4aa0004 +#define F367CAB_DESCR_MODE 0xf4aa0003 + +/* FEC_AC_CTR_3 */ +#define R367CAB_FEC_AC_CTR_3 0xf4ab +#define F367CAB_DI_UNLOCK 0xf4ab0080 +#define F367CAB_DI_FREEZE 0xf4ab0040 +#define F367CAB_MISMATCH 0xf4ab0030 +#define F367CAB_ACQ_MODE 0xf4ab000c +#define F367CAB_TRK_MODE 0xf4ab0003 + +/* FEC_STATUS */ +#define R367CAB_FEC_STATUS 0xf4ac +#define F367CAB_DEINT_SMCNTR 0xf4ac00e0 +#define F367CAB_DEINT_SYNCSTATE 0xf4ac0018 +#define F367CAB_DEINT_SYNLOST 0xf4ac0004 +#define F367CAB_DESCR_SYNCSTATE 0xf4ac0002 + +/* RS_COUNTER_0 */ +#define R367CAB_RS_COUNTER_0 0xf4ae +#define F367CAB_BK_CT_L 0xf4ae00ff + +/* RS_COUNTER_1 */ +#define R367CAB_RS_COUNTER_1 0xf4af +#define F367CAB_BK_CT_H 0xf4af00ff + +/* RS_COUNTER_2 */ +#define R367CAB_RS_COUNTER_2 0xf4b0 +#define F367CAB_CORR_CT_L 0xf4b000ff + +/* RS_COUNTER_3 */ +#define R367CAB_RS_COUNTER_3 0xf4b1 +#define F367CAB_CORR_CT_H 0xf4b100ff + +/* RS_COUNTER_4 */ +#define R367CAB_RS_COUNTER_4 0xf4b2 +#define F367CAB_UNCORR_CT_L 0xf4b200ff + +/* RS_COUNTER_5 */ +#define R367CAB_RS_COUNTER_5 0xf4b3 +#define F367CAB_UNCORR_CT_H 0xf4b300ff + +/* BERT_0 */ +#define R367CAB_BERT_0 0xf4b4 +#define F367CAB_RS_NOCORR 0xf4b40004 +#define F367CAB_CT_HOLD 0xf4b40002 +#define F367CAB_CT_CLEAR 0xf4b40001 + +/* BERT_1 */ +#define R367CAB_BERT_1 0xf4b5 +#define F367CAB_BERT_ON 0xf4b50020 +#define F367CAB_BERT_ERR_SRC 0xf4b50010 +#define F367CAB_BERT_ERR_MODE 0xf4b50008 +#define F367CAB_BERT_NBYTE 0xf4b50007 + +/* BERT_2 */ +#define R367CAB_BERT_2 0xf4b6 +#define F367CAB_BERT_ERRCOUNT_L 0xf4b600ff + +/* BERT_3 */ +#define R367CAB_BERT_3 0xf4b7 +#define F367CAB_BERT_ERRCOUNT_H 0xf4b700ff + +/* OUTFORMAT_0 */ +#define R367CAB_OUTFORMAT_0 0xf4b8 +#define F367CAB_CLK_POLARITY 0xf4b80080 +#define F367CAB_FEC_TYPE 0xf4b80040 +#define F367CAB_SYNC_STRIP 0xf4b80008 +#define F367CAB_TS_SWAP 0xf4b80004 +#define F367CAB_OUTFORMAT 0xf4b80003 + +/* OUTFORMAT_1 */ +#define R367CAB_OUTFORMAT_1 0xf4b9 +#define F367CAB_CI_DIVRANGE 0xf4b900ff + +/* SMOOTHER_2 */ +#define R367CAB_SMOOTHER_2 0xf4be +#define F367CAB_FIFO_BYPASS 0xf4be0020 + +/* TSMF_CTRL_0 */ +#define R367CAB_TSMF_CTRL_0 0xf4c0 +#define F367CAB_TS_NUMBER 0xf4c0001e +#define F367CAB_SEL_MODE 0xf4c00001 + +/* TSMF_CTRL_1 */ +#define R367CAB_TSMF_CTRL_1 0xf4c1 +#define F367CAB_CHECK_ERROR_BIT 0xf4c10080 +#define F367CAB_CHCK_F_SYNC 0xf4c10040 +#define F367CAB_H_MODE 0xf4c10008 +#define F367CAB_D_V_MODE 0xf4c10004 +#define F367CAB_MODE 0xf4c10003 + +/* TSMF_CTRL_3 */ +#define R367CAB_TSMF_CTRL_3 0xf4c3 +#define F367CAB_SYNC_IN_COUNT 0xf4c300f0 +#define F367CAB_SYNC_OUT_COUNT 0xf4c3000f + +/* TS_ON_ID_0 */ +#define R367CAB_TS_ON_ID_0 0xf4c4 +#define F367CAB_TS_ID_L 0xf4c400ff + +/* TS_ON_ID_1 */ +#define R367CAB_TS_ON_ID_1 0xf4c5 +#define F367CAB_TS_ID_H 0xf4c500ff + +/* TS_ON_ID_2 */ +#define R367CAB_TS_ON_ID_2 0xf4c6 +#define F367CAB_ON_ID_L 0xf4c600ff + +/* TS_ON_ID_3 */ +#define R367CAB_TS_ON_ID_3 0xf4c7 +#define F367CAB_ON_ID_H 0xf4c700ff + +/* RE_STATUS_0 */ +#define R367CAB_RE_STATUS_0 0xf4c8 +#define F367CAB_RECEIVE_STATUS_L 0xf4c800ff + +/* RE_STATUS_1 */ +#define R367CAB_RE_STATUS_1 0xf4c9 +#define F367CAB_RECEIVE_STATUS_LH 0xf4c900ff + +/* RE_STATUS_2 */ +#define R367CAB_RE_STATUS_2 0xf4ca +#define F367CAB_RECEIVE_STATUS_HL 0xf4ca00ff + +/* RE_STATUS_3 */ +#define R367CAB_RE_STATUS_3 0xf4cb +#define F367CAB_RECEIVE_STATUS_HH 0xf4cb003f + +/* TS_STATUS_0 */ +#define R367CAB_TS_STATUS_0 0xf4cc +#define F367CAB_TS_STATUS_L 0xf4cc00ff + +/* TS_STATUS_1 */ +#define R367CAB_TS_STATUS_1 0xf4cd +#define F367CAB_TS_STATUS_H 0xf4cd007f + +/* TS_STATUS_2 */ +#define R367CAB_TS_STATUS_2 0xf4ce +#define F367CAB_ERROR 0xf4ce0080 +#define F367CAB_EMERGENCY 0xf4ce0040 +#define F367CAB_CRE_TS 0xf4ce0030 +#define F367CAB_VER 0xf4ce000e +#define F367CAB_M_LOCK 0xf4ce0001 + +/* TS_STATUS_3 */ +#define R367CAB_TS_STATUS_3 0xf4cf +#define F367CAB_UPDATE_READY 0xf4cf0080 +#define F367CAB_END_FRAME_HEADER 0xf4cf0040 +#define F367CAB_CONTCNT 0xf4cf0020 +#define F367CAB_TS_IDENTIFIER_SEL 0xf4cf000f + +/* T_O_ID_0 */ +#define R367CAB_T_O_ID_0 0xf4d0 +#define F367CAB_ON_ID_I_L 0xf4d000ff + +/* T_O_ID_1 */ +#define R367CAB_T_O_ID_1 0xf4d1 +#define F367CAB_ON_ID_I_H 0xf4d100ff + +/* T_O_ID_2 */ +#define R367CAB_T_O_ID_2 0xf4d2 +#define F367CAB_TS_ID_I_L 0xf4d200ff + +/* T_O_ID_3 */ +#define R367CAB_T_O_ID_3 0xf4d3 +#define F367CAB_TS_ID_I_H 0xf4d300ff + +#endif diff --git a/drivers/media/dvb-frontends/stv0900.h b/drivers/media/dvb-frontends/stv0900.h new file mode 100644 index 0000000000..b7f40919f7 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0900.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0900.h + * + * Driver for ST STV0900 satellite demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0900_H +#define STV0900_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +struct stv0900_reg { + u16 addr; + u8 val; +}; + +struct stv0900_config { + u8 demod_address; + u8 demod_mode; + u32 xtal; + u8 clkmode;/* 0 for CLKI, 2 for XTALI */ + + u8 diseqc_mode; + + u8 path1_mode; + u8 path2_mode; + struct stv0900_reg *ts_config_regs; + u8 tun1_maddress;/* 0, 1, 2, 3 for 0xc0, 0xc2, 0xc4, 0xc6 */ + u8 tun2_maddress; + u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ + u8 tun2_adc; + u8 tun1_type;/* for now 3 for stb6100 auto, else - software */ + u8 tun2_type; + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + /* Hook for Lock LED */ + void (*set_lock_led)(struct dvb_frontend *fe, int offon); +}; + +#if IS_REACHABLE(CONFIG_DVB_STV0900) +extern struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, + struct i2c_adapter *i2c, int demod); +#else +static inline struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, + struct i2c_adapter *i2c, int demod) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif + diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c new file mode 100644 index 0000000000..e7b9b9b11d --- /dev/null +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -0,0 +1,1966 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * stv0900_core.c + * + * Driver for ST STV0900 satellite demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/i2c.h> + +#include "stv0900.h" +#include "stv0900_reg.h" +#include "stv0900_priv.h" +#include "stv0900_init.h" + +int stvdebug = 1; +module_param_named(debug, stvdebug, int, 0644); + +/* internal params node */ +struct stv0900_inode { + /* pointer for internal params, one for each pair of demods */ + struct stv0900_internal *internal; + struct stv0900_inode *next_inode; +}; + +/* first internal params */ +static struct stv0900_inode *stv0900_first_inode; + +/* find chip by i2c adapter and i2c address */ +static struct stv0900_inode *find_inode(struct i2c_adapter *i2c_adap, + u8 i2c_addr) +{ + struct stv0900_inode *temp_chip = stv0900_first_inode; + + if (temp_chip != NULL) { + /* + Search of the last stv0900 chip or + find it by i2c adapter and i2c address */ + while ((temp_chip != NULL) && + ((temp_chip->internal->i2c_adap != i2c_adap) || + (temp_chip->internal->i2c_addr != i2c_addr))) + + temp_chip = temp_chip->next_inode; + + } + + return temp_chip; +} + +/* deallocating chip */ +static void remove_inode(struct stv0900_internal *internal) +{ + struct stv0900_inode *prev_node = stv0900_first_inode; + struct stv0900_inode *del_node = find_inode(internal->i2c_adap, + internal->i2c_addr); + + if (del_node != NULL) { + if (del_node == stv0900_first_inode) { + stv0900_first_inode = del_node->next_inode; + } else { + while (prev_node->next_inode != del_node) + prev_node = prev_node->next_inode; + + if (del_node->next_inode == NULL) + prev_node->next_inode = NULL; + else + prev_node->next_inode = + prev_node->next_inode->next_inode; + } + + kfree(del_node); + } +} + +/* allocating new chip */ +static struct stv0900_inode *append_internal(struct stv0900_internal *internal) +{ + struct stv0900_inode *new_node = stv0900_first_inode; + + if (new_node == NULL) { + new_node = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL); + stv0900_first_inode = new_node; + } else { + while (new_node->next_inode != NULL) + new_node = new_node->next_inode; + + new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), + GFP_KERNEL); + if (new_node->next_inode != NULL) + new_node = new_node->next_inode; + else + new_node = NULL; + } + + if (new_node != NULL) { + new_node->internal = internal; + new_node->next_inode = NULL; + } + + return new_node; +} + +s32 ge2comp(s32 a, s32 width) +{ + if (width == 32) + return a; + else + return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a; +} + +void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr, + u8 reg_data) +{ + u8 data[3]; + int ret; + struct i2c_msg i2cmsg = { + .addr = intp->i2c_addr, + .flags = 0, + .len = 3, + .buf = data, + }; + + data[0] = MSB(reg_addr); + data[1] = LSB(reg_addr); + data[2] = reg_data; + + ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1); + if (ret != 1) + dprintk("%s: i2c error %d\n", __func__, ret); +} + +u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg) +{ + int ret; + u8 b0[] = { MSB(reg), LSB(reg) }; + u8 buf = 0; + struct i2c_msg msg[] = { + { + .addr = intp->i2c_addr, + .flags = 0, + .buf = b0, + .len = 2, + }, { + .addr = intp->i2c_addr, + .flags = I2C_M_RD, + .buf = &buf, + .len = 1, + }, + }; + + ret = i2c_transfer(intp->i2c_adap, msg, 2); + if (ret != 2) + dprintk("%s: i2c error %d, reg[0x%02x]\n", + __func__, ret, reg); + + return buf; +} + +static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) +{ + u8 position = 0, i = 0; + + (*mask) = label & 0xff; + + while ((position == 0) && (i < 8)) { + position = ((*mask) >> i) & 0x01; + i++; + } + + (*pos) = (i - 1); +} + +void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val) +{ + u8 reg, mask, pos; + + reg = stv0900_read_reg(intp, (label >> 16) & 0xffff); + extract_mask_pos(label, &mask, &pos); + + val = mask & (val << pos); + + reg = (reg & (~mask)) | val; + stv0900_write_reg(intp, (label >> 16) & 0xffff, reg); + +} + +u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) +{ + u8 val; + u8 mask, pos; + + extract_mask_pos(label, &mask, &pos); + + val = stv0900_read_reg(intp, label >> 16); + val = (val & mask) >> pos; + + return val; +} + +static enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) +{ + s32 i; + + if (intp == NULL) + return STV0900_INVALID_HANDLE; + + intp->chip_id = stv0900_read_reg(intp, R0900_MID); + + if (intp->errs != STV0900_NO_ERROR) + return intp->errs; + + /*Startup sequence*/ + stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c); + stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c); + msleep(3); + stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c); + stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f); + stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20); + stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20); + stv0900_write_reg(intp, R0900_NCOARSE, 0x13); + msleep(3); + stv0900_write_reg(intp, R0900_I2CCFG, 0x08); + + switch (intp->clkmode) { + case 0: + case 2: + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 + | intp->clkmode); + break; + default: + /* preserve SELOSCI bit */ + i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i); + break; + } + + msleep(3); + for (i = 0; i < 181; i++) + stv0900_write_reg(intp, STV0900_InitVal[i][0], + STV0900_InitVal[i][1]); + + if (stv0900_read_reg(intp, R0900_MID) >= 0x20) { + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c); + for (i = 0; i < 32; i++) + stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0], + STV0900_Cut20_AddOnVal[i][1]); + } + + stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c); + stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c); + + stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01); + stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21); + + stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20); + stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20); + + stv0900_write_reg(intp, R0900_TSTRES0, 0x80); + stv0900_write_reg(intp, R0900_TSTRES0, 0x00); + + return STV0900_NO_ERROR; +} + +static u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) +{ + u32 mclk, div, ad_div; + + div = stv0900_get_bits(intp, F0900_M_DIV); + ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); + + mclk = (div + 1) * ext_clk / ad_div; + + dprintk("%s: Calculated Mclk = %d\n", __func__, mclk); + + return mclk; +} + +static enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) +{ + u32 m_div, clk_sel; + + if (intp == NULL) + return STV0900_INVALID_HANDLE; + + if (intp->errs) + return STV0900_I2C_ERROR; + + dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, + intp->quartz); + + clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); + m_div = ((clk_sel * mclk) / intp->quartz) - 1; + stv0900_write_bits(intp, F0900_M_DIV, m_div); + intp->mclk = stv0900_get_mclk_freq(intp, + intp->quartz); + + /*Set the DiseqC frequency to 22KHz */ + /* + Formula: + DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) + DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) + */ + m_div = intp->mclk / 704000; + stv0900_write_reg(intp, R0900_P1_F22TX, m_div); + stv0900_write_reg(intp, R0900_P1_F22RX, m_div); + + stv0900_write_reg(intp, R0900_P2_F22TX, m_div); + stv0900_write_reg(intp, R0900_P2_F22RX, m_div); + + if ((intp->errs)) + return STV0900_I2C_ERROR; + + return STV0900_NO_ERROR; +} + +static u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, + enum fe_stv0900_demod_num demod) +{ + u32 lsb, msb, hsb, err_val; + + switch (cntr) { + case 0: + default: + hsb = stv0900_get_bits(intp, ERR_CNT12); + msb = stv0900_get_bits(intp, ERR_CNT11); + lsb = stv0900_get_bits(intp, ERR_CNT10); + break; + case 1: + hsb = stv0900_get_bits(intp, ERR_CNT22); + msb = stv0900_get_bits(intp, ERR_CNT21); + lsb = stv0900_get_bits(intp, ERR_CNT20); + break; + } + + err_val = (hsb << 16) + (msb << 8) + (lsb); + + return err_val; +} + +static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + stv0900_write_bits(intp, I2CT_ON, enable); + + return 0; +} + +static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp, + enum fe_stv0900_clock_type path1_ts, + enum fe_stv0900_clock_type path2_ts) +{ + + dprintk("%s\n", __func__); + + if (intp->chip_id >= 0x20) { + switch (path1_ts) { + case STV0900_PARALLEL_PUNCT_CLOCK: + case STV0900_DVBCI_CLOCK: + switch (path2_ts) { + case STV0900_SERIAL_PUNCT_CLOCK: + case STV0900_SERIAL_CONT_CLOCK: + default: + stv0900_write_reg(intp, R0900_TSGENERAL, + 0x00); + break; + case STV0900_PARALLEL_PUNCT_CLOCK: + case STV0900_DVBCI_CLOCK: + stv0900_write_reg(intp, R0900_TSGENERAL, + 0x06); + stv0900_write_bits(intp, + F0900_P1_TSFIFO_MANSPEED, 3); + stv0900_write_bits(intp, + F0900_P2_TSFIFO_MANSPEED, 0); + stv0900_write_reg(intp, + R0900_P1_TSSPEED, 0x14); + stv0900_write_reg(intp, + R0900_P2_TSSPEED, 0x28); + break; + } + break; + case STV0900_SERIAL_PUNCT_CLOCK: + case STV0900_SERIAL_CONT_CLOCK: + default: + switch (path2_ts) { + case STV0900_SERIAL_PUNCT_CLOCK: + case STV0900_SERIAL_CONT_CLOCK: + default: + stv0900_write_reg(intp, + R0900_TSGENERAL, 0x0C); + break; + case STV0900_PARALLEL_PUNCT_CLOCK: + case STV0900_DVBCI_CLOCK: + stv0900_write_reg(intp, + R0900_TSGENERAL, 0x0A); + dprintk("%s: 0x0a\n", __func__); + break; + } + break; + } + } else { + switch (path1_ts) { + case STV0900_PARALLEL_PUNCT_CLOCK: + case STV0900_DVBCI_CLOCK: + switch (path2_ts) { + case STV0900_SERIAL_PUNCT_CLOCK: + case STV0900_SERIAL_CONT_CLOCK: + default: + stv0900_write_reg(intp, R0900_TSGENERAL1X, + 0x10); + break; + case STV0900_PARALLEL_PUNCT_CLOCK: + case STV0900_DVBCI_CLOCK: + stv0900_write_reg(intp, R0900_TSGENERAL1X, + 0x16); + stv0900_write_bits(intp, + F0900_P1_TSFIFO_MANSPEED, 3); + stv0900_write_bits(intp, + F0900_P2_TSFIFO_MANSPEED, 0); + stv0900_write_reg(intp, R0900_P1_TSSPEED, + 0x14); + stv0900_write_reg(intp, R0900_P2_TSSPEED, + 0x28); + break; + } + + break; + case STV0900_SERIAL_PUNCT_CLOCK: + case STV0900_SERIAL_CONT_CLOCK: + default: + switch (path2_ts) { + case STV0900_SERIAL_PUNCT_CLOCK: + case STV0900_SERIAL_CONT_CLOCK: + default: + stv0900_write_reg(intp, R0900_TSGENERAL1X, + 0x14); + break; + case STV0900_PARALLEL_PUNCT_CLOCK: + case STV0900_DVBCI_CLOCK: + stv0900_write_reg(intp, R0900_TSGENERAL1X, + 0x12); + dprintk("%s: 0x12\n", __func__); + break; + } + + break; + } + } + + switch (path1_ts) { + case STV0900_PARALLEL_PUNCT_CLOCK: + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); + break; + case STV0900_DVBCI_CLOCK: + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); + break; + case STV0900_SERIAL_PUNCT_CLOCK: + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); + break; + case STV0900_SERIAL_CONT_CLOCK: + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); + break; + default: + break; + } + + switch (path2_ts) { + case STV0900_PARALLEL_PUNCT_CLOCK: + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); + break; + case STV0900_DVBCI_CLOCK: + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); + break; + case STV0900_SERIAL_PUNCT_CLOCK: + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); + break; + case STV0900_SERIAL_CONT_CLOCK: + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); + break; + default: + break; + } + + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); +} + +void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, + u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; + + if (tuner_ops->set_frequency) { + if ((tuner_ops->set_frequency(fe, frequency)) < 0) + dprintk("%s: Invalid parameter\n", __func__); + else + dprintk("%s: Frequency=%d\n", __func__, frequency); + + } + + if (tuner_ops->set_bandwidth) { + if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) + dprintk("%s: Invalid parameter\n", __func__); + else + dprintk("%s: Bandwidth=%d\n", __func__, bandwidth); + + } +} + +void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; + + if (tuner_ops->set_bandwidth) { + if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) + dprintk("%s: Invalid parameter\n", __func__); + else + dprintk("%s: Bandwidth=%d\n", __func__, bandwidth); + + } +} + +u32 stv0900_get_freq_auto(struct stv0900_internal *intp, int demod) +{ + u32 freq, round; + /* Formulat : + Tuner_Frequency(MHz) = Regs / 64 + Tuner_granularity(MHz) = Regs / 2048 + real_Tuner_Frequency = Tuner_Frequency(MHz) - Tuner_granularity(MHz) + */ + freq = (stv0900_get_bits(intp, TUN_RFFREQ2) << 10) + + (stv0900_get_bits(intp, TUN_RFFREQ1) << 2) + + stv0900_get_bits(intp, TUN_RFFREQ0); + + freq = (freq * 1000) / 64; + + round = (stv0900_get_bits(intp, TUN_RFRESTE1) >> 2) + + stv0900_get_bits(intp, TUN_RFRESTE0); + + round = (round * 1000) / 2048; + + return freq + round; +} + +void stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, + u32 Bandwidth, int demod) +{ + u32 tunerFrequency; + /* Formulat: + Tuner_frequency_reg= Frequency(MHz)*64 + */ + tunerFrequency = (Frequency * 64) / 1000; + + stv0900_write_bits(intp, TUN_RFFREQ2, (tunerFrequency >> 10)); + stv0900_write_bits(intp, TUN_RFFREQ1, (tunerFrequency >> 2) & 0xff); + stv0900_write_bits(intp, TUN_RFFREQ0, (tunerFrequency & 0x03)); + /* Low Pass Filter = BW /2 (MHz)*/ + stv0900_write_bits(intp, TUN_BW, Bandwidth / 2000000); + /* Tuner Write trig */ + stv0900_write_reg(intp, TNRLD, 1); +} + +static s32 stv0900_get_rf_level(struct stv0900_internal *intp, + const struct stv0900_table *lookup, + enum fe_stv0900_demod_num demod) +{ + s32 agc_gain = 0, + imin, + imax, + i, + rf_lvl = 0; + + dprintk("%s\n", __func__); + + if ((lookup == NULL) || (lookup->size <= 0)) + return 0; + + agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), + stv0900_get_bits(intp, AGCIQ_VALUE0)); + + imin = 0; + imax = lookup->size - 1; + if (INRANGE(lookup->table[imin].regval, agc_gain, + lookup->table[imax].regval)) { + while ((imax - imin) > 1) { + i = (imax + imin) >> 1; + + if (INRANGE(lookup->table[imin].regval, + agc_gain, + lookup->table[i].regval)) + imax = i; + else + imin = i; + } + + rf_lvl = (s32)agc_gain - lookup->table[imin].regval; + rf_lvl *= (lookup->table[imax].realval - + lookup->table[imin].realval); + rf_lvl /= (lookup->table[imax].regval - + lookup->table[imin].regval); + rf_lvl += lookup->table[imin].realval; + } else if (agc_gain > lookup->table[0].regval) + rf_lvl = 5; + else if (agc_gain < lookup->table[lookup->size-1].regval) + rf_lvl = -100; + + dprintk("%s: RFLevel = %d\n", __func__, rf_lvl); + + return rf_lvl; +} + +static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *internal = state->internal; + s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf, + state->demod); + + rflevel = (rflevel + 100) * (65535 / 70); + if (rflevel < 0) + rflevel = 0; + + if (rflevel > 65535) + rflevel = 65535; + + *strength = rflevel; + + return 0; +} + +static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, + const struct stv0900_table *lookup) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + s32 c_n = -100, + regval, + imin, + imax, + i, + noise_field1, + noise_field0; + + dprintk("%s\n", __func__); + + if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { + noise_field1 = NOSPLHT_NORMED1; + noise_field0 = NOSPLHT_NORMED0; + } else { + noise_field1 = NOSDATAT_NORMED1; + noise_field0 = NOSDATAT_NORMED0; + } + + if (stv0900_get_bits(intp, LOCK_DEFINITIF)) { + if ((lookup != NULL) && lookup->size) { + regval = 0; + msleep(5); + for (i = 0; i < 16; i++) { + regval += MAKEWORD(stv0900_get_bits(intp, + noise_field1), + stv0900_get_bits(intp, + noise_field0)); + msleep(1); + } + + regval /= 16; + imin = 0; + imax = lookup->size - 1; + if (INRANGE(lookup->table[imin].regval, + regval, + lookup->table[imax].regval)) { + while ((imax - imin) > 1) { + i = (imax + imin) >> 1; + if (INRANGE(lookup->table[imin].regval, + regval, + lookup->table[i].regval)) + imax = i; + else + imin = i; + } + + c_n = ((regval - lookup->table[imin].regval) + * (lookup->table[imax].realval + - lookup->table[imin].realval) + / (lookup->table[imax].regval + - lookup->table[imin].regval)) + + lookup->table[imin].realval; + } else if (regval < lookup->table[imin].regval) + c_n = 1000; + } + } + + return c_n; +} + +static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + u8 err_val1, err_val0; + u32 header_err_val = 0; + + *ucblocks = 0x0; + if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { + /* DVB-S2 delineator errors count */ + + /* retrieving number for errnous headers */ + err_val1 = stv0900_read_reg(intp, BBFCRCKO1); + err_val0 = stv0900_read_reg(intp, BBFCRCKO0); + header_err_val = (err_val1 << 8) | err_val0; + + /* retrieving number for errnous packets */ + err_val1 = stv0900_read_reg(intp, UPCRCKO1); + err_val0 = stv0900_read_reg(intp, UPCRCKO0); + *ucblocks = (err_val1 << 8) | err_val0; + *ucblocks += header_err_val; + } + + return 0; +} + +static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + s32 snrlcl = stv0900_carr_get_quality(fe, + (const struct stv0900_table *)&stv0900_s2_cn); + snrlcl = (snrlcl + 30) * 384; + if (snrlcl < 0) + snrlcl = 0; + + if (snrlcl > 65535) + snrlcl = 65535; + + *snr = snrlcl; + + return 0; +} + +static u32 stv0900_get_ber(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + u32 ber = 10000000, i; + s32 demod_state; + + demod_state = stv0900_get_bits(intp, HEADER_MODE); + + switch (demod_state) { + case STV0900_SEARCH: + case STV0900_PLH_DETECTED: + default: + ber = 10000000; + break; + case STV0900_DVBS_FOUND: + ber = 0; + for (i = 0; i < 5; i++) { + msleep(5); + ber += stv0900_get_err_count(intp, 0, demod); + } + + ber /= 5; + if (stv0900_get_bits(intp, PRFVIT)) { + ber *= 9766; + ber = ber >> 13; + } + + break; + case STV0900_DVBS2_FOUND: + ber = 0; + for (i = 0; i < 5; i++) { + msleep(5); + ber += stv0900_get_err_count(intp, 0, demod); + } + + ber /= 5; + if (stv0900_get_bits(intp, PKTDELIN_LOCK)) { + ber *= 9766; + ber = ber >> 13; + } + + break; + } + + return ber; +} + +static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *internal = state->internal; + + *ber = stv0900_get_ber(internal, state->demod); + + return 0; +} + +int stv0900_get_demod_lock(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, s32 time_out) +{ + s32 timer = 0, + lock = 0; + + enum fe_stv0900_search_state dmd_state; + + while ((timer < time_out) && (lock == 0)) { + dmd_state = stv0900_get_bits(intp, HEADER_MODE); + dprintk("Demod State = %d\n", dmd_state); + switch (dmd_state) { + case STV0900_SEARCH: + case STV0900_PLH_DETECTED: + default: + lock = 0; + break; + case STV0900_DVBS2_FOUND: + case STV0900_DVBS_FOUND: + lock = stv0900_get_bits(intp, LOCK_DEFINITIF); + break; + } + + if (lock == 0) + msleep(10); + + timer += 10; + } + + if (lock) + dprintk("DEMOD LOCK OK\n"); + else + dprintk("DEMOD LOCK FAIL\n"); + + return lock; +} + +void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + s32 regflist, + i; + + dprintk("%s\n", __func__); + + regflist = MODCODLST0; + + for (i = 0; i < 16; i++) + stv0900_write_reg(intp, regflist + i, 0xff); +} + +void stv0900_activate_s2_modcod(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + u32 matype, + mod_code, + fmod, + reg_index, + field_index; + + dprintk("%s\n", __func__); + + if (intp->chip_id <= 0x11) { + msleep(5); + + mod_code = stv0900_read_reg(intp, PLHMODCOD); + matype = mod_code & 0x3; + mod_code = (mod_code & 0x7f) >> 2; + + reg_index = MODCODLSTF - mod_code / 2; + field_index = mod_code % 2; + + switch (matype) { + case 0: + default: + fmod = 14; + break; + case 1: + fmod = 13; + break; + case 2: + fmod = 11; + break; + case 3: + fmod = 7; + break; + } + + if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910)) + && (matype <= 1)) { + if (field_index == 0) + stv0900_write_reg(intp, reg_index, + 0xf0 | fmod); + else + stv0900_write_reg(intp, reg_index, + (fmod << 4) | 0xf); + } + + } else if (intp->chip_id >= 0x12) { + for (reg_index = 0; reg_index < 7; reg_index++) + stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff); + + stv0900_write_reg(intp, MODCODLSTE, 0xff); + stv0900_write_reg(intp, MODCODLSTF, 0xcf); + for (reg_index = 0; reg_index < 8; reg_index++) + stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc); + + + } +} + +void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + u32 reg_index; + + dprintk("%s\n", __func__); + + stv0900_write_reg(intp, MODCODLST0, 0xff); + stv0900_write_reg(intp, MODCODLST1, 0xf0); + stv0900_write_reg(intp, MODCODLSTF, 0x0f); + for (reg_index = 0; reg_index < 13; reg_index++) + stv0900_write_reg(intp, MODCODLST2 + reg_index, 0); + +} + +static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_CUSTOM; +} + +void stv0900_start_search(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + u32 freq; + s16 freq_s16 ; + + stv0900_write_bits(intp, DEMOD_MODE, 0x1f); + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0xaa); + + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x55); + + if (intp->chip_id <= 0x20) { + if (intp->symbol_rate[0] <= 5000000) { + stv0900_write_reg(intp, CARCFG, 0x44); + stv0900_write_reg(intp, CFRUP1, 0x0f); + stv0900_write_reg(intp, CFRUP0, 0xff); + stv0900_write_reg(intp, CFRLOW1, 0xf0); + stv0900_write_reg(intp, CFRLOW0, 0x00); + stv0900_write_reg(intp, RTCS2, 0x68); + } else { + stv0900_write_reg(intp, CARCFG, 0xc4); + stv0900_write_reg(intp, RTCS2, 0x44); + } + + } else { /*cut 3.0 above*/ + if (intp->symbol_rate[demod] <= 5000000) + stv0900_write_reg(intp, RTCS2, 0x68); + else + stv0900_write_reg(intp, RTCS2, 0x44); + + stv0900_write_reg(intp, CARCFG, 0x46); + if (intp->srch_algo[demod] == STV0900_WARM_START) { + freq = 1000 << 16; + freq /= (intp->mclk / 1000); + freq_s16 = (s16)freq; + } else { + freq = (intp->srch_range[demod] / 2000); + if (intp->symbol_rate[demod] <= 5000000) + freq += 80; + else + freq += 600; + + freq = freq << 16; + freq /= (intp->mclk / 1000); + freq_s16 = (s16)freq; + } + + stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16)); + stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16)); + freq_s16 *= (-1); + stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16)); + stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16)); + } + + stv0900_write_reg(intp, CFRINIT1, 0); + stv0900_write_reg(intp, CFRINIT0, 0); + + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, EQUALCFG, 0x41); + stv0900_write_reg(intp, FFECFG, 0x41); + + if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || + (intp->srch_standard[demod] == STV0900_SEARCH_DSS) || + (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) { + stv0900_write_reg(intp, VITSCALE, + 0x82); + stv0900_write_reg(intp, VAVSRVIT, 0x0); + } + } + + stv0900_write_reg(intp, SFRSTEP, 0x00); + stv0900_write_reg(intp, TMGTHRISE, 0xe0); + stv0900_write_reg(intp, TMGTHFALL, 0xc0); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0); + stv0900_write_reg(intp, RTC, 0x88); + if (intp->chip_id >= 0x20) { + if (intp->symbol_rate[demod] < 2000000) { + if (intp->chip_id <= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x39); + else /*cut 3.0*/ + stv0900_write_reg(intp, CARFREQ, 0x89); + + stv0900_write_reg(intp, CARHDR, 0x40); + } else if (intp->symbol_rate[demod] < 10000000) { + stv0900_write_reg(intp, CARFREQ, 0x4c); + stv0900_write_reg(intp, CARHDR, 0x20); + } else { + stv0900_write_reg(intp, CARFREQ, 0x4b); + stv0900_write_reg(intp, CARHDR, 0x20); + } + + } else { + if (intp->symbol_rate[demod] < 10000000) + stv0900_write_reg(intp, CARFREQ, 0xef); + else + stv0900_write_reg(intp, CARFREQ, 0xed); + } + + switch (intp->srch_algo[demod]) { + case STV0900_WARM_START: + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x18); + break; + case STV0900_COLD_START: + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + break; + default: + break; + } +} + +u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, + s32 pilot, u8 chip_id) +{ + u8 aclc_value = 0x29; + s32 i, cllas2_size; + const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2; + + dprintk("%s\n", __func__); + + if (chip_id <= 0x12) { + cls2 = FE_STV0900_S2CarLoop; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; + cllas2 = FE_STV0900_S2APSKCarLoopCut30; + cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut30); + } else if (chip_id == 0x20) { + cls2 = FE_STV0900_S2CarLoopCut20; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut20; + cllas2 = FE_STV0900_S2APSKCarLoopCut20; + cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut20); + } else { + cls2 = FE_STV0900_S2CarLoopCut30; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; + cllas2 = FE_STV0900_S2APSKCarLoopCut30; + cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut30); + } + + if (modcode < STV0900_QPSK_12) { + i = 0; + while ((i < 3) && (modcode != cllqs2[i].modcode)) + i++; + + if (i >= 3) + i = 2; + } else { + i = 0; + while ((i < 14) && (modcode != cls2[i].modcode)) + i++; + + if (i >= 14) { + i = 0; + while ((i < 11) && (modcode != cllas2[i].modcode)) + i++; + + if (i >= 11) + i = 10; + } + } + + if (modcode <= STV0900_QPSK_25) { + if (pilot) { + if (srate <= 3000000) + aclc_value = cllqs2[i].car_loop_pilots_on_2; + else if (srate <= 7000000) + aclc_value = cllqs2[i].car_loop_pilots_on_5; + else if (srate <= 15000000) + aclc_value = cllqs2[i].car_loop_pilots_on_10; + else if (srate <= 25000000) + aclc_value = cllqs2[i].car_loop_pilots_on_20; + else + aclc_value = cllqs2[i].car_loop_pilots_on_30; + } else { + if (srate <= 3000000) + aclc_value = cllqs2[i].car_loop_pilots_off_2; + else if (srate <= 7000000) + aclc_value = cllqs2[i].car_loop_pilots_off_5; + else if (srate <= 15000000) + aclc_value = cllqs2[i].car_loop_pilots_off_10; + else if (srate <= 25000000) + aclc_value = cllqs2[i].car_loop_pilots_off_20; + else + aclc_value = cllqs2[i].car_loop_pilots_off_30; + } + + } else if (modcode <= STV0900_8PSK_910) { + if (pilot) { + if (srate <= 3000000) + aclc_value = cls2[i].car_loop_pilots_on_2; + else if (srate <= 7000000) + aclc_value = cls2[i].car_loop_pilots_on_5; + else if (srate <= 15000000) + aclc_value = cls2[i].car_loop_pilots_on_10; + else if (srate <= 25000000) + aclc_value = cls2[i].car_loop_pilots_on_20; + else + aclc_value = cls2[i].car_loop_pilots_on_30; + } else { + if (srate <= 3000000) + aclc_value = cls2[i].car_loop_pilots_off_2; + else if (srate <= 7000000) + aclc_value = cls2[i].car_loop_pilots_off_5; + else if (srate <= 15000000) + aclc_value = cls2[i].car_loop_pilots_off_10; + else if (srate <= 25000000) + aclc_value = cls2[i].car_loop_pilots_off_20; + else + aclc_value = cls2[i].car_loop_pilots_off_30; + } + + } else if (i < cllas2_size) { + if (srate <= 3000000) + aclc_value = cllas2[i].car_loop_pilots_on_2; + else if (srate <= 7000000) + aclc_value = cllas2[i].car_loop_pilots_on_5; + else if (srate <= 15000000) + aclc_value = cllas2[i].car_loop_pilots_on_10; + else if (srate <= 25000000) + aclc_value = cllas2[i].car_loop_pilots_on_20; + else + aclc_value = cllas2[i].car_loop_pilots_on_30; + } + + return aclc_value; +} + +u8 stv0900_get_optim_short_carr_loop(s32 srate, + enum fe_stv0900_modulation modulation, + u8 chip_id) +{ + const struct stv0900_short_frames_car_loop_optim *s2scl; + const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30; + s32 mod_index = 0; + u8 aclc_value = 0x0b; + + dprintk("%s\n", __func__); + + s2scl = FE_STV0900_S2ShortCarLoop; + s2sclc30 = FE_STV0900_S2ShortCarLoopCut30; + + switch (modulation) { + case STV0900_QPSK: + default: + mod_index = 0; + break; + case STV0900_8PSK: + mod_index = 1; + break; + case STV0900_16APSK: + mod_index = 2; + break; + case STV0900_32APSK: + mod_index = 3; + break; + } + + if (chip_id >= 0x30) { + if (srate <= 3000000) + aclc_value = s2sclc30[mod_index].car_loop_2; + else if (srate <= 7000000) + aclc_value = s2sclc30[mod_index].car_loop_5; + else if (srate <= 15000000) + aclc_value = s2sclc30[mod_index].car_loop_10; + else if (srate <= 25000000) + aclc_value = s2sclc30[mod_index].car_loop_20; + else + aclc_value = s2sclc30[mod_index].car_loop_30; + + } else if (chip_id >= 0x20) { + if (srate <= 3000000) + aclc_value = s2scl[mod_index].car_loop_cut20_2; + else if (srate <= 7000000) + aclc_value = s2scl[mod_index].car_loop_cut20_5; + else if (srate <= 15000000) + aclc_value = s2scl[mod_index].car_loop_cut20_10; + else if (srate <= 25000000) + aclc_value = s2scl[mod_index].car_loop_cut20_20; + else + aclc_value = s2scl[mod_index].car_loop_cut20_30; + + } else { + if (srate <= 3000000) + aclc_value = s2scl[mod_index].car_loop_cut12_2; + else if (srate <= 7000000) + aclc_value = s2scl[mod_index].car_loop_cut12_5; + else if (srate <= 15000000) + aclc_value = s2scl[mod_index].car_loop_cut12_10; + else if (srate <= 25000000) + aclc_value = s2scl[mod_index].car_loop_cut12_20; + else + aclc_value = s2scl[mod_index].car_loop_cut12_30; + + } + + return aclc_value; +} + +static +enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, + enum fe_stv0900_demod_mode LDPC_Mode, + enum fe_stv0900_demod_num demod) +{ + s32 reg_ind; + + dprintk("%s\n", __func__); + + switch (LDPC_Mode) { + case STV0900_DUAL: + default: + if ((intp->demod_mode != STV0900_DUAL) + || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) { + stv0900_write_reg(intp, R0900_GENCFG, 0x1d); + + intp->demod_mode = STV0900_DUAL; + + stv0900_write_bits(intp, F0900_FRESFEC, 1); + stv0900_write_bits(intp, F0900_FRESFEC, 0); + + for (reg_ind = 0; reg_ind < 7; reg_ind++) + stv0900_write_reg(intp, + R0900_P1_MODCODLST0 + reg_ind, + 0xff); + for (reg_ind = 0; reg_ind < 8; reg_ind++) + stv0900_write_reg(intp, + R0900_P1_MODCODLST7 + reg_ind, + 0xcc); + + stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff); + stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf); + + for (reg_ind = 0; reg_ind < 7; reg_ind++) + stv0900_write_reg(intp, + R0900_P2_MODCODLST0 + reg_ind, + 0xff); + for (reg_ind = 0; reg_ind < 8; reg_ind++) + stv0900_write_reg(intp, + R0900_P2_MODCODLST7 + reg_ind, + 0xcc); + + stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff); + stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf); + } + + break; + case STV0900_SINGLE: + if (demod == STV0900_DEMOD_2) { + stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1); + stv0900_activate_s2_modcod_single(intp, + STV0900_DEMOD_2); + stv0900_write_reg(intp, R0900_GENCFG, 0x06); + } else { + stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2); + stv0900_activate_s2_modcod_single(intp, + STV0900_DEMOD_1); + stv0900_write_reg(intp, R0900_GENCFG, 0x04); + } + + intp->demod_mode = STV0900_SINGLE; + + stv0900_write_bits(intp, F0900_FRESFEC, 1); + stv0900_write_bits(intp, F0900_FRESFEC, 0); + stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1); + stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0); + stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1); + stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0); + break; + } + + return STV0900_NO_ERROR; +} + +static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, + struct stv0900_init_params *p_init) +{ + struct stv0900_state *state = fe->demodulator_priv; + enum fe_stv0900_error error = STV0900_NO_ERROR; + enum fe_stv0900_error demodError = STV0900_NO_ERROR; + struct stv0900_internal *intp = NULL; + int selosci, i; + + struct stv0900_inode *temp_int = find_inode(state->i2c_adap, + state->config->demod_address); + + dprintk("%s\n", __func__); + + if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) { + state->internal = temp_int->internal; + (state->internal->dmds_used)++; + dprintk("%s: Find Internal Structure!\n", __func__); + return STV0900_NO_ERROR; + } else { + state->internal = kmalloc(sizeof(struct stv0900_internal), + GFP_KERNEL); + if (state->internal == NULL) + return STV0900_INVALID_HANDLE; + temp_int = append_internal(state->internal); + if (temp_int == NULL) { + kfree(state->internal); + state->internal = NULL; + return STV0900_INVALID_HANDLE; + } + state->internal->dmds_used = 1; + state->internal->i2c_adap = state->i2c_adap; + state->internal->i2c_addr = state->config->demod_address; + state->internal->clkmode = state->config->clkmode; + state->internal->errs = STV0900_NO_ERROR; + dprintk("%s: Create New Internal Structure!\n", __func__); + } + + if (state->internal == NULL) { + error = STV0900_INVALID_HANDLE; + return error; + } + + demodError = stv0900_initialize(state->internal); + if (demodError == STV0900_NO_ERROR) { + error = STV0900_NO_ERROR; + } else { + if (demodError == STV0900_INVALID_HANDLE) + error = STV0900_INVALID_HANDLE; + else + error = STV0900_I2C_ERROR; + + return error; + } + + intp = state->internal; + + intp->demod_mode = p_init->demod_mode; + stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1); + intp->chip_id = stv0900_read_reg(intp, R0900_MID); + intp->rolloff = p_init->rolloff; + intp->quartz = p_init->dmd_ref_clk; + + stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); + stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); + + intp->ts_config = p_init->ts_config; + if (intp->ts_config == NULL) + stv0900_set_ts_parallel_serial(intp, + p_init->path1_ts_clock, + p_init->path2_ts_clock); + else { + for (i = 0; intp->ts_config[i].addr != 0xffff; i++) + stv0900_write_reg(intp, + intp->ts_config[i].addr, + intp->ts_config[i].val); + + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); + } + + intp->tuner_type[0] = p_init->tuner1_type; + intp->tuner_type[1] = p_init->tuner2_type; + /* tuner init */ + switch (p_init->tuner1_type) { + case 3: /*FE_AUTO_STB6100:*/ + stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x3c); + stv0900_write_reg(intp, R0900_P1_TNRCFG2, 0x86); + stv0900_write_reg(intp, R0900_P1_TNRCFG3, 0x18); + stv0900_write_reg(intp, R0900_P1_TNRXTAL, 27); /* 27MHz */ + stv0900_write_reg(intp, R0900_P1_TNRSTEPS, 0x05); + stv0900_write_reg(intp, R0900_P1_TNRGAIN, 0x17); + stv0900_write_reg(intp, R0900_P1_TNRADJ, 0x1f); + stv0900_write_reg(intp, R0900_P1_TNRCTL2, 0x0); + stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 3); + break; + /* case FE_SW_TUNER: */ + default: + stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 6); + break; + } + + stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); + switch (p_init->tuner1_adc) { + case 1: + stv0900_write_reg(intp, R0900_TSTTNR1, 0x26); + break; + default: + break; + } + + stv0900_write_reg(intp, R0900_P1_TNRLD, 1); /* hw tuner */ + + /* tuner init */ + switch (p_init->tuner2_type) { + case 3: /*FE_AUTO_STB6100:*/ + stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x3c); + stv0900_write_reg(intp, R0900_P2_TNRCFG2, 0x86); + stv0900_write_reg(intp, R0900_P2_TNRCFG3, 0x18); + stv0900_write_reg(intp, R0900_P2_TNRXTAL, 27); /* 27MHz */ + stv0900_write_reg(intp, R0900_P2_TNRSTEPS, 0x05); + stv0900_write_reg(intp, R0900_P2_TNRGAIN, 0x17); + stv0900_write_reg(intp, R0900_P2_TNRADJ, 0x1f); + stv0900_write_reg(intp, R0900_P2_TNRCTL2, 0x0); + stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 3); + break; + /* case FE_SW_TUNER: */ + default: + stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 6); + break; + } + + stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); + switch (p_init->tuner2_adc) { + case 1: + stv0900_write_reg(intp, R0900_TSTTNR3, 0x26); + break; + default: + break; + } + + stv0900_write_reg(intp, R0900_P2_TNRLD, 1); /* hw tuner */ + + stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv); + stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv); + stv0900_set_mclk(intp, 135000000); + msleep(3); + + switch (intp->clkmode) { + case 0: + case 2: + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode); + break; + default: + selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci); + break; + } + msleep(3); + + intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz); + if (intp->errs) + error = STV0900_I2C_ERROR; + + return error; +} + +static int stv0900_status(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + enum fe_stv0900_search_state demod_state; + int locked = FALSE; + u8 tsbitrate0_val, tsbitrate1_val; + s32 bitrate; + + demod_state = stv0900_get_bits(intp, HEADER_MODE); + switch (demod_state) { + case STV0900_SEARCH: + case STV0900_PLH_DETECTED: + default: + locked = FALSE; + break; + case STV0900_DVBS2_FOUND: + locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && + stv0900_get_bits(intp, PKTDELIN_LOCK) && + stv0900_get_bits(intp, TSFIFO_LINEOK); + break; + case STV0900_DVBS_FOUND: + locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && + stv0900_get_bits(intp, LOCKEDVIT) && + stv0900_get_bits(intp, TSFIFO_LINEOK); + break; + } + + dprintk("%s: locked = %d\n", __func__, locked); + + if (stvdebug) { + /* Print TS bitrate */ + tsbitrate0_val = stv0900_read_reg(intp, TSBITRATE0); + tsbitrate1_val = stv0900_read_reg(intp, TSBITRATE1); + /* Formula Bit rate = Mclk * px_tsfifo_bitrate / 16384 */ + bitrate = (stv0900_get_mclk_freq(intp, intp->quartz)/1000000) + * (tsbitrate1_val << 8 | tsbitrate0_val); + bitrate /= 16384; + dprintk("TS bitrate = %d Mbit/sec\n", bitrate); + } + + return locked; +} + +static int stv0900_set_mis(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, int mis) +{ + dprintk("%s\n", __func__); + + if (mis < 0 || mis > 255) { + dprintk("Disable MIS filtering\n"); + stv0900_write_bits(intp, FILTER_EN, 0); + } else { + dprintk("Enable MIS filtering - %d\n", mis); + stv0900_write_bits(intp, FILTER_EN, 1); + stv0900_write_reg(intp, ISIENTRY, mis); + stv0900_write_reg(intp, ISIBITENA, 0xff); + } + + return STV0900_NO_ERROR; +} + + +static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + struct stv0900_search_params p_search; + struct stv0900_signal_info p_result = intp->result[demod]; + + enum fe_stv0900_error error = STV0900_NO_ERROR; + + dprintk("%s: ", __func__); + + if (!(INRANGE(100000, c->symbol_rate, 70000000))) + return DVBFE_ALGO_SEARCH_FAILED; + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + stv0900_set_mis(intp, demod, c->stream_id); + + p_result.locked = FALSE; + p_search.path = demod; + p_search.frequency = c->frequency; + p_search.symbol_rate = c->symbol_rate; + p_search.search_range = 10000000; + p_search.fec = STV0900_FEC_UNKNOWN; + p_search.standard = STV0900_AUTO_SEARCH; + p_search.iq_inversion = STV0900_IQ_AUTO; + p_search.search_algo = STV0900_BLIND_SEARCH; + /* Speeds up DVB-S searching */ + if (c->delivery_system == SYS_DVBS) + p_search.standard = STV0900_SEARCH_DVBS1; + + intp->srch_standard[demod] = p_search.standard; + intp->symbol_rate[demod] = p_search.symbol_rate; + intp->srch_range[demod] = p_search.search_range; + intp->freq[demod] = p_search.frequency; + intp->srch_algo[demod] = p_search.search_algo; + intp->srch_iq_inv[demod] = p_search.iq_inversion; + intp->fec[demod] = p_search.fec; + if ((stv0900_algo(fe) == STV0900_RANGEOK) && + (intp->errs == STV0900_NO_ERROR)) { + p_result.locked = intp->result[demod].locked; + p_result.standard = intp->result[demod].standard; + p_result.frequency = intp->result[demod].frequency; + p_result.symbol_rate = intp->result[demod].symbol_rate; + p_result.fec = intp->result[demod].fec; + p_result.modcode = intp->result[demod].modcode; + p_result.pilot = intp->result[demod].pilot; + p_result.frame_len = intp->result[demod].frame_len; + p_result.spectrum = intp->result[demod].spectrum; + p_result.rolloff = intp->result[demod].rolloff; + p_result.modulation = intp->result[demod].modulation; + } else { + p_result.locked = FALSE; + switch (intp->err[demod]) { + case STV0900_I2C_ERROR: + error = STV0900_I2C_ERROR; + break; + case STV0900_NO_ERROR: + default: + error = STV0900_SEARCH_FAILED; + break; + } + } + + if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) { + dprintk("Search Success\n"); + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + dprintk("Search Fail\n"); + return DVBFE_ALGO_SEARCH_FAILED; + } + +} + +static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct stv0900_state *state = fe->demodulator_priv; + + dprintk("%s: ", __func__); + + if ((stv0900_status(state->internal, state->demod)) == TRUE) { + dprintk("DEMOD LOCK OK\n"); + *status = FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC + | FE_HAS_LOCK; + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 1); + } else { + *status = 0; + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("DEMOD LOCK FAIL\n"); + } + + return 0; +} + +static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) +{ + + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + if (stop_ts == TRUE) + stv0900_write_bits(intp, RST_HWARE, 1); + else + stv0900_write_bits(intp, RST_HWARE, 0); + + return 0; +} + +static int stv0900_diseqc_init(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode); + stv0900_write_bits(intp, DISEQC_RESET, 1); + stv0900_write_bits(intp, DISEQC_RESET, 0); + + return 0; +} + +static int stv0900_init(struct dvb_frontend *fe) +{ + dprintk("%s\n", __func__); + + stv0900_stop_ts(fe, 1); + stv0900_diseqc_init(fe); + + return 0; +} + +static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data, + u32 NbData, enum fe_stv0900_demod_num demod) +{ + s32 i = 0; + + stv0900_write_bits(intp, DIS_PRECHARGE, 1); + while (i < NbData) { + while (stv0900_get_bits(intp, FIFO_FULL)) + ;/* checkpatch complains */ + stv0900_write_reg(intp, DISTXDATA, data[i]); + i++; + } + + stv0900_write_bits(intp, DIS_PRECHARGE, 0); + i = 0; + while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) { + msleep(10); + i++; + } + + return 0; +} + +static int stv0900_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct stv0900_state *state = fe->demodulator_priv; + + return stv0900_diseqc_send(state->internal, + cmd->msg, + cmd->msg_len, + state->demod); +} + +static int stv0900_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + u8 data; + + + switch (burst) { + case SEC_MINI_A: + stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */ + data = 0x00; + stv0900_diseqc_send(intp, &data, 1, state->demod); + break; + case SEC_MINI_B: + stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */ + data = 0xff; + stv0900_diseqc_send(intp, &data, 1, state->demod); + break; + } + + return 0; +} + +static int stv0900_recv_slave_reply(struct dvb_frontend *fe, + struct dvb_diseqc_slave_reply *reply) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + s32 i = 0; + + reply->msg_len = 0; + + while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) { + msleep(10); + i++; + } + + if (stv0900_get_bits(intp, RX_END)) { + reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR); + + for (i = 0; i < reply->msg_len; i++) + reply->msg[i] = stv0900_read_reg(intp, DISRXDATA); + } + + return 0; +} + +static int stv0900_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode toneoff) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off")); + + switch (toneoff) { + case SEC_TONE_ON: + /*Set the DiseqC mode to 22Khz _continues_ tone*/ + stv0900_write_bits(intp, DISTX_MODE, 0); + stv0900_write_bits(intp, DISEQC_RESET, 1); + /*release DiseqC reset to enable the 22KHz tone*/ + stv0900_write_bits(intp, DISEQC_RESET, 0); + break; + case SEC_TONE_OFF: + /*return diseqc mode to config->diseqc_mode. + Usually it's without _continues_ tone */ + stv0900_write_bits(intp, DISTX_MODE, + state->config->diseqc_mode); + /*maintain the DiseqC reset to disable the 22KHz tone*/ + stv0900_write_bits(intp, DISEQC_RESET, 1); + stv0900_write_bits(intp, DISEQC_RESET, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stv0900_release(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + + if ((--(state->internal->dmds_used)) <= 0) { + + dprintk("%s: Actually removing\n", __func__); + + remove_inode(state->internal); + kfree(state->internal); + } + + kfree(state); +} + +static int stv0900_sleep(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + + return 0; +} + +static int stv0900_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + struct stv0900_signal_info p_result = intp->result[demod]; + + p->frequency = p_result.locked ? p_result.frequency : 0; + p->symbol_rate = p_result.locked ? p_result.symbol_rate : 0; + return 0; +} + +static const struct dvb_frontend_ops stv0900_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, + .info = { + .name = "STV0900 frontend", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | FE_CAN_QPSK | + FE_CAN_2G_MODULATION | + FE_CAN_FEC_AUTO + }, + .release = stv0900_release, + .init = stv0900_init, + .get_frontend = stv0900_get_frontend, + .sleep = stv0900_sleep, + .get_frontend_algo = stv0900_frontend_algo, + .i2c_gate_ctrl = stv0900_i2c_gate_ctrl, + .diseqc_send_master_cmd = stv0900_send_master_cmd, + .diseqc_send_burst = stv0900_send_burst, + .diseqc_recv_slave_reply = stv0900_recv_slave_reply, + .set_tone = stv0900_set_tone, + .search = stv0900_search, + .read_status = stv0900_read_status, + .read_ber = stv0900_read_ber, + .read_signal_strength = stv0900_read_signal_strength, + .read_snr = stv0900_read_snr, + .read_ucblocks = stv0900_read_ucblocks, +}; + +struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, + struct i2c_adapter *i2c, + int demod) +{ + struct stv0900_state *state = NULL; + struct stv0900_init_params init_params; + enum fe_stv0900_error err_stv0900; + + state = kzalloc(sizeof(struct stv0900_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->demod = demod; + state->config = config; + state->i2c_adap = i2c; + + memcpy(&state->frontend.ops, &stv0900_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + switch (demod) { + case 0: + case 1: + init_params.dmd_ref_clk = config->xtal; + init_params.demod_mode = config->demod_mode; + init_params.rolloff = STV0900_35; + init_params.path1_ts_clock = config->path1_mode; + init_params.tun1_maddress = config->tun1_maddress; + init_params.tun1_iq_inv = STV0900_IQ_NORMAL; + init_params.tuner1_adc = config->tun1_adc; + init_params.tuner1_type = config->tun1_type; + init_params.path2_ts_clock = config->path2_mode; + init_params.ts_config = config->ts_config_regs; + init_params.tun2_maddress = config->tun2_maddress; + init_params.tuner2_adc = config->tun2_adc; + init_params.tuner2_type = config->tun2_type; + init_params.tun2_iq_inv = STV0900_IQ_SWAPPED; + + err_stv0900 = stv0900_init_internal(&state->frontend, + &init_params); + + if (err_stv0900) + goto error; + + if (state->internal->chip_id >= 0x30) + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; + + break; + default: + goto error; + break; + } + + dprintk("%s: Attaching STV0900 demodulator(%d) \n", __func__, demod); + return &state->frontend; + +error: + dprintk("%s: Failed to attach STV0900 demodulator(%d) \n", + __func__, demod); + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stv0900_attach); + +MODULE_PARM_DESC(debug, "Set debug"); + +MODULE_AUTHOR("Igor M. Liplianin"); +MODULE_DESCRIPTION("ST STV0900 frontend"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stv0900_init.h b/drivers/media/dvb-frontends/stv0900_init.h new file mode 100644 index 0000000000..a3c8b6e5b6 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0900_init.h @@ -0,0 +1,570 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0900_init.h + * + * Driver for ST STV0900 satellite demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0900_INIT_H +#define STV0900_INIT_H + +#include "stv0900_priv.h" + +/* DVBS2 C/N Look-Up table */ +static const struct stv0900_table stv0900_s2_cn = { + 55, + { + { -30, 13348 }, /*C/N=-3dB*/ + { -20, 12640 }, /*C/N=-2dB*/ + { -10, 11883 }, /*C/N=-1dB*/ + { 0, 11101 }, /*C/N=-0dB*/ + { 5, 10718 }, /*C/N=0.5dB*/ + { 10, 10339 }, /*C/N=1.0dB*/ + { 15, 9947 }, /*C/N=1.5dB*/ + { 20, 9552 }, /*C/N=2.0dB*/ + { 25, 9183 }, /*C/N=2.5dB*/ + { 30, 8799 }, /*C/N=3.0dB*/ + { 35, 8422 }, /*C/N=3.5dB*/ + { 40, 8062 }, /*C/N=4.0dB*/ + { 45, 7707 }, /*C/N=4.5dB*/ + { 50, 7353 }, /*C/N=5.0dB*/ + { 55, 7025 }, /*C/N=5.5dB*/ + { 60, 6684 }, /*C/N=6.0dB*/ + { 65, 6331 }, /*C/N=6.5dB*/ + { 70, 6036 }, /*C/N=7.0dB*/ + { 75, 5727 }, /*C/N=7.5dB*/ + { 80, 5437 }, /*C/N=8.0dB*/ + { 85, 5164 }, /*C/N=8.5dB*/ + { 90, 4902 }, /*C/N=9.0dB*/ + { 95, 4653 }, /*C/N=9.5dB*/ + { 100, 4408 }, /*C/N=10.0dB*/ + { 105, 4187 }, /*C/N=10.5dB*/ + { 110, 3961 }, /*C/N=11.0dB*/ + { 115, 3751 }, /*C/N=11.5dB*/ + { 120, 3558 }, /*C/N=12.0dB*/ + { 125, 3368 }, /*C/N=12.5dB*/ + { 130, 3191 }, /*C/N=13.0dB*/ + { 135, 3017 }, /*C/N=13.5dB*/ + { 140, 2862 }, /*C/N=14.0dB*/ + { 145, 2710 }, /*C/N=14.5dB*/ + { 150, 2565 }, /*C/N=15.0dB*/ + { 160, 2300 }, /*C/N=16.0dB*/ + { 170, 2058 }, /*C/N=17.0dB*/ + { 180, 1849 }, /*C/N=18.0dB*/ + { 190, 1663 }, /*C/N=19.0dB*/ + { 200, 1495 }, /*C/N=20.0dB*/ + { 210, 1349 }, /*C/N=21.0dB*/ + { 220, 1222 }, /*C/N=22.0dB*/ + { 230, 1110 }, /*C/N=23.0dB*/ + { 240, 1011 }, /*C/N=24.0dB*/ + { 250, 925 }, /*C/N=25.0dB*/ + { 260, 853 }, /*C/N=26.0dB*/ + { 270, 789 }, /*C/N=27.0dB*/ + { 280, 734 }, /*C/N=28.0dB*/ + { 290, 690 }, /*C/N=29.0dB*/ + { 300, 650 }, /*C/N=30.0dB*/ + { 310, 619 }, /*C/N=31.0dB*/ + { 320, 593 }, /*C/N=32.0dB*/ + { 330, 571 }, /*C/N=33.0dB*/ + { 400, 498 }, /*C/N=40.0dB*/ + { 450, 484 }, /*C/N=45.0dB*/ + { 500, 481 } /*C/N=50.0dB*/ + } +}; + +/* RF level C/N Look-Up table */ +static const struct stv0900_table stv0900_rf = { + 14, + { + { -5, 0xCAA1 }, /*-5dBm*/ + { -10, 0xC229 }, /*-10dBm*/ + { -15, 0xBB08 }, /*-15dBm*/ + { -20, 0xB4BC }, /*-20dBm*/ + { -25, 0xAD5A }, /*-25dBm*/ + { -30, 0xA298 }, /*-30dBm*/ + { -35, 0x98A8 }, /*-35dBm*/ + { -40, 0x8389 }, /*-40dBm*/ + { -45, 0x59BE }, /*-45dBm*/ + { -50, 0x3A14 }, /*-50dBm*/ + { -55, 0x2D11 }, /*-55dBm*/ + { -60, 0x210D }, /*-60dBm*/ + { -65, 0xA14F }, /*-65dBm*/ + { -70, 0x7AA } /*-70dBm*/ + } +}; + +struct stv0900_car_loop_optim { + enum fe_stv0900_modcode modcode; + u8 car_loop_pilots_on_2; + u8 car_loop_pilots_off_2; + u8 car_loop_pilots_on_5; + u8 car_loop_pilots_off_5; + u8 car_loop_pilots_on_10; + u8 car_loop_pilots_off_10; + u8 car_loop_pilots_on_20; + u8 car_loop_pilots_off_20; + u8 car_loop_pilots_on_30; + u8 car_loop_pilots_off_30; + +}; + +struct stv0900_short_frames_car_loop_optim { + enum fe_stv0900_modulation modulation; + u8 car_loop_cut12_2; /* Cut 1.2, SR<=3msps */ + u8 car_loop_cut20_2; /* Cut 2.0, SR<3msps */ + u8 car_loop_cut12_5; /* Cut 1.2, 3<SR<=7msps */ + u8 car_loop_cut20_5; /* Cut 2.0, 3<SR<=7msps */ + u8 car_loop_cut12_10; /* Cut 1.2, 7<SR<=15msps */ + u8 car_loop_cut20_10; /* Cut 2.0, 7<SR<=15msps */ + u8 car_loop_cut12_20; /* Cut 1.2, 10<SR<=25msps */ + u8 car_loop_cut20_20; /* Cut 2.0, 10<SR<=25msps */ + u8 car_loop_cut12_30; /* Cut 1.2, 25<SR<=45msps */ + u8 car_loop_cut20_30; /* Cut 2.0, 10<SR<=45msps */ + +}; + +struct stv0900_short_frames_car_loop_optim_vs_mod { + enum fe_stv0900_modulation modulation; + u8 car_loop_2; /* SR<3msps */ + u8 car_loop_5; /* 3<SR<=7msps */ + u8 car_loop_10; /* 7<SR<=15msps */ + u8 car_loop_20; /* 10<SR<=25msps */ + u8 car_loop_30; /* 10<SR<=45msps */ +}; + +/* Cut 1.x Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */ +static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_12, 0x1C, 0x0D, 0x1B, 0x2C, 0x3A, + 0x1C, 0x2A, 0x3B, 0x2A, 0x1B }, + { STV0900_QPSK_35, 0x2C, 0x0D, 0x2B, 0x2C, 0x3A, + 0x0C, 0x3A, 0x2B, 0x2A, 0x0B }, + { STV0900_QPSK_23, 0x2C, 0x0D, 0x2B, 0x2C, 0x0B, + 0x0C, 0x3A, 0x1B, 0x2A, 0x3A }, + { STV0900_QPSK_34, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_45, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_56, 0x0D, 0x0D, 0x3B, 0x1C, 0x0B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_89, 0x0D, 0x0D, 0x3B, 0x1C, 0x1B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_QPSK_910, 0x1D, 0x0D, 0x3B, 0x1C, 0x1B, + 0x3B, 0x3A, 0x0B, 0x2A, 0x3A }, + { STV0900_8PSK_35, 0x29, 0x3B, 0x09, 0x2B, 0x38, + 0x0B, 0x18, 0x1A, 0x08, 0x0A }, + { STV0900_8PSK_23, 0x0A, 0x3B, 0x29, 0x2B, 0x19, + 0x0B, 0x38, 0x1A, 0x18, 0x0A }, + { STV0900_8PSK_34, 0x3A, 0x3B, 0x2A, 0x2B, 0x39, + 0x0B, 0x19, 0x1A, 0x38, 0x0A }, + { STV0900_8PSK_56, 0x1B, 0x3B, 0x0B, 0x2B, 0x1A, + 0x0B, 0x39, 0x1A, 0x19, 0x0A }, + { STV0900_8PSK_89, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, + 0x0B, 0x39, 0x1A, 0x29, 0x39 }, + { STV0900_8PSK_910, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, + 0x0B, 0x39, 0x1A, 0x29, 0x39 } +}; + + +/* Cut 2.0 Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */ +static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = { + /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_12, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, + 0x1F, 0x3D, 0x3E, 0x3D, 0x1E }, + { STV0900_QPSK_35, 0x2F, 0x3F, 0x2E, 0x2F, 0x3D, + 0x0F, 0x0E, 0x2E, 0x3D, 0x0E }, + { STV0900_QPSK_23, 0x2F, 0x3F, 0x2E, 0x2F, 0x0E, + 0x0F, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_34, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_45, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_56, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_89, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_QPSK_910, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, + 0x3E, 0x0E, 0x1E, 0x3D, 0x3D }, + { STV0900_8PSK_35, 0x3c, 0x0c, 0x1c, 0x3b, 0x0c, + 0x3b, 0x2b, 0x2b, 0x1b, 0x2b }, + { STV0900_8PSK_23, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, + 0x3b, 0x0c, 0x2b, 0x2b, 0x2b }, + { STV0900_8PSK_34, 0x0e, 0x1c, 0x3d, 0x0c, 0x0d, + 0x3b, 0x2c, 0x3b, 0x0c, 0x2b }, + { STV0900_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, + 0x1e, 0x3c, 0x2d, 0x2c, 0x1d }, + { STV0900_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, + 0x1e, 0x0d, 0x2d, 0x3c, 0x1d }, + { STV0900_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, + 0x1e, 0x1d, 0x2d, 0x0d, 0x1d }, +}; + + + +/* Cut 2.0 Tracking carrier loop carrier 16APSK 2/3 to 32APSK 9/10 long Frame */ +static const struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut20[11] = { + /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_16APSK_23, 0x0C, 0x0C, 0x0C, 0x0C, 0x1D, + 0x0C, 0x3C, 0x0C, 0x2C, 0x0C }, + { STV0900_16APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, + 0x0C, 0x2D, 0x0C, 0x1D, 0x0C }, + { STV0900_16APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, + 0x0C, 0x3D, 0x0C, 0x2D, 0x0C }, + { STV0900_16APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, + 0x0C, 0x3D, 0x0C, 0x2D, 0x0C }, + { STV0900_16APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, + 0x0C, 0x0E, 0x0C, 0x3D, 0x0C }, + { STV0900_16APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, + 0x0C, 0x0E, 0x0C, 0x3D, 0x0C }, + { STV0900_32APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { STV0900_32APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, +}; + + +/* Cut 2.0 Tracking carrier loop carrier QPSK 1/4 to QPSK 2/5 long Frame */ +static const struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut20[3] = { + /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_14, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, + 0x2F, 0x2D, 0x1F, 0x3D, 0x3E }, + { STV0900_QPSK_13, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, + 0x2F, 0x3D, 0x0F, 0x3D, 0x2E }, + { STV0900_QPSK_25, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, + 0x1F, 0x3D, 0x3E, 0x3D, 0x2E } +}; + + +/* Cut 2.0 Tracking carrier loop carrier short Frame, cut 1.2 and 2.0 */ +static const +struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = { + /*Mod 2Mcut1.2 2Mcut2.0 5Mcut1.2 5Mcut2.0 10Mcut1.2 + 10Mcut2.0 20Mcut1.2 20M_cut2.0 30Mcut1.2 30Mcut2.0*/ + { STV0900_QPSK, 0x3C, 0x2F, 0x2B, 0x2E, 0x0B, + 0x0E, 0x3A, 0x0E, 0x2A, 0x3D }, + { STV0900_8PSK, 0x0B, 0x3E, 0x2A, 0x0E, 0x0A, + 0x2D, 0x19, 0x0D, 0x09, 0x3C }, + { STV0900_16APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, + 0x1E, 0x3A, 0x3D, 0x2A, 0x2D }, + { STV0900_32APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, + 0x1E, 0x3A, 0x3D, 0x2A, 0x2D } +}; + +static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut30[14] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_QPSK_12, 0x3C, 0x2C, 0x0C, 0x2C, 0x1B, + 0x2C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_35, 0x0D, 0x0D, 0x0C, 0x0D, 0x1B, + 0x3C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_23, 0x1D, 0x0D, 0x0C, 0x1D, 0x2B, + 0x3C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_34, 0x1D, 0x1D, 0x0C, 0x1D, 0x2B, + 0x3C, 0x1B, 0x1C, 0x0B, 0x3B }, + { STV0900_QPSK_45, 0x2D, 0x1D, 0x1C, 0x1D, 0x2B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_QPSK_56, 0x2D, 0x1D, 0x1C, 0x1D, 0x2B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_QPSK_89, 0x3D, 0x2D, 0x1C, 0x1D, 0x3B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_QPSK_910, 0x3D, 0x2D, 0x1C, 0x1D, 0x3B, + 0x3C, 0x2B, 0x0C, 0x1B, 0x3B }, + { STV0900_8PSK_35, 0x39, 0x19, 0x39, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x09, 0x19 }, + { STV0900_8PSK_23, 0x2A, 0x39, 0x1A, 0x0A, 0x39, + 0x0A, 0x29, 0x39, 0x29, 0x0A }, + { STV0900_8PSK_34, 0x0B, 0x3A, 0x0B, 0x0B, 0x3A, + 0x1B, 0x1A, 0x0B, 0x1A, 0x3A }, + { STV0900_8PSK_56, 0x0C, 0x1B, 0x3B, 0x2B, 0x1B, + 0x3B, 0x3A, 0x3B, 0x3A, 0x1B }, + { STV0900_8PSK_89, 0x2C, 0x2C, 0x2C, 0x1C, 0x2B, + 0x0C, 0x0B, 0x3B, 0x0B, 0x1B }, + { STV0900_8PSK_910, 0x2C, 0x3C, 0x2C, 0x1C, 0x3B, + 0x1C, 0x0B, 0x3B, 0x0B, 0x1B } +}; + +static const +struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut30[11] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV0900_16APSK_23, 0x0A, 0x0A, 0x0A, 0x0A, 0x1A, + 0x0A, 0x3A, 0x0A, 0x2A, 0x0A }, + { STV0900_16APSK_34, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, + 0x0A, 0x3B, 0x0A, 0x1B, 0x0A }, + { STV0900_16APSK_45, 0x0A, 0x0A, 0x0A, 0x0A, 0x1B, + 0x0A, 0x3B, 0x0A, 0x2B, 0x0A }, + { STV0900_16APSK_56, 0x0A, 0x0A, 0x0A, 0x0A, 0x1B, + 0x0A, 0x3B, 0x0A, 0x2B, 0x0A }, + { STV0900_16APSK_89, 0x0A, 0x0A, 0x0A, 0x0A, 0x2B, + 0x0A, 0x0C, 0x0A, 0x3B, 0x0A }, + { STV0900_16APSK_910, 0x0A, 0x0A, 0x0A, 0x0A, 0x2B, + 0x0A, 0x0C, 0x0A, 0x3B, 0x0A }, + { STV0900_32APSK_34, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_45, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_56, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_89, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }, + { STV0900_32APSK_910, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A } +}; + +static const +struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut30[3] = { + /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon + 10MPoff 20MPon 20MPoff 30MPon 30MPoff*/ + { STV0900_QPSK_14, 0x0C, 0x3C, 0x0B, 0x3C, 0x2A, + 0x2C, 0x2A, 0x1C, 0x3A, 0x3B }, + { STV0900_QPSK_13, 0x0C, 0x3C, 0x0B, 0x3C, 0x2A, + 0x2C, 0x3A, 0x0C, 0x3A, 0x2B }, + { STV0900_QPSK_25, 0x1C, 0x3C, 0x1B, 0x3C, 0x3A, + 0x1C, 0x3A, 0x3B, 0x3A, 0x2B } +}; + +static const struct stv0900_short_frames_car_loop_optim_vs_mod +FE_STV0900_S2ShortCarLoopCut30[4] = { + /*Mod 2Mcut3.0 5Mcut3.0 10Mcut3.0 20Mcut3.0 30Mcut3.0*/ + { STV0900_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A }, + { STV0900_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 }, + { STV0900_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, + { STV0900_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, + +}; + +static const u16 STV0900_InitVal[181][2] = { + { R0900_OUTCFG , 0x00 }, + { R0900_AGCRF1CFG , 0x11 }, + { R0900_AGCRF2CFG , 0x13 }, + { R0900_TSGENERAL1X , 0x14 }, + { R0900_TSTTNR2 , 0x21 }, + { R0900_TSTTNR4 , 0x21 }, + { R0900_P2_DISTXCTL , 0x22 }, + { R0900_P2_F22TX , 0xc0 }, + { R0900_P2_F22RX , 0xc0 }, + { R0900_P2_DISRXCTL , 0x00 }, + { R0900_P2_TNRSTEPS , 0x87 }, + { R0900_P2_TNRGAIN , 0x09 }, + { R0900_P2_DMDCFGMD , 0xF9 }, + { R0900_P2_DEMOD , 0x08 }, + { R0900_P2_DMDCFG3 , 0xc4 }, + { R0900_P2_CARFREQ , 0xed }, + { R0900_P2_TNRCFG2 , 0x02 }, + { R0900_P2_TNRCFG3 , 0x02 }, + { R0900_P2_LDT , 0xd0 }, + { R0900_P2_LDT2 , 0xb8 }, + { R0900_P2_TMGCFG , 0xd2 }, + { R0900_P2_TMGTHRISE , 0x20 }, + { R0900_P2_TMGTHFALL , 0x00 }, + { R0900_P2_FECSPY , 0x88 }, + { R0900_P2_FSPYDATA , 0x3a }, + { R0900_P2_FBERCPT4 , 0x00 }, + { R0900_P2_FSPYBER , 0x10 }, + { R0900_P2_ERRCTRL1 , 0x35 }, + { R0900_P2_ERRCTRL2 , 0xc1 }, + { R0900_P2_CFRICFG , 0xf8 }, + { R0900_P2_NOSCFG , 0x1c }, + { R0900_P2_DMDT0M , 0x20 }, + { R0900_P2_CORRELMANT , 0x70 }, + { R0900_P2_CORRELABS , 0x88 }, + { R0900_P2_AGC2O , 0x5b }, + { R0900_P2_AGC2REF , 0x38 }, + { R0900_P2_CARCFG , 0xe4 }, + { R0900_P2_ACLC , 0x1A }, + { R0900_P2_BCLC , 0x09 }, + { R0900_P2_CARHDR , 0x08 }, + { R0900_P2_KREFTMG , 0xc1 }, + { R0900_P2_SFRUPRATIO , 0xf0 }, + { R0900_P2_SFRLOWRATIO , 0x70 }, + { R0900_P2_SFRSTEP , 0x58 }, + { R0900_P2_TMGCFG2 , 0x01 }, + { R0900_P2_CAR2CFG , 0x26 }, + { R0900_P2_BCLC2S2Q , 0x86 }, + { R0900_P2_BCLC2S28 , 0x86 }, + { R0900_P2_SMAPCOEF7 , 0x77 }, + { R0900_P2_SMAPCOEF6 , 0x85 }, + { R0900_P2_SMAPCOEF5 , 0x77 }, + { R0900_P2_TSCFGL , 0x20 }, + { R0900_P2_DMDCFG2 , 0x3b }, + { R0900_P2_MODCODLST0 , 0xff }, + { R0900_P2_MODCODLST1 , 0xff }, + { R0900_P2_MODCODLST2 , 0xff }, + { R0900_P2_MODCODLST3 , 0xff }, + { R0900_P2_MODCODLST4 , 0xff }, + { R0900_P2_MODCODLST5 , 0xff }, + { R0900_P2_MODCODLST6 , 0xff }, + { R0900_P2_MODCODLST7 , 0xcc }, + { R0900_P2_MODCODLST8 , 0xcc }, + { R0900_P2_MODCODLST9 , 0xcc }, + { R0900_P2_MODCODLSTA , 0xcc }, + { R0900_P2_MODCODLSTB , 0xcc }, + { R0900_P2_MODCODLSTC , 0xcc }, + { R0900_P2_MODCODLSTD , 0xcc }, + { R0900_P2_MODCODLSTE , 0xcc }, + { R0900_P2_MODCODLSTF , 0xcf }, + { R0900_P1_DISTXCTL , 0x22 }, + { R0900_P1_F22TX , 0xc0 }, + { R0900_P1_F22RX , 0xc0 }, + { R0900_P1_DISRXCTL , 0x00 }, + { R0900_P1_TNRSTEPS , 0x87 }, + { R0900_P1_TNRGAIN , 0x09 }, + { R0900_P1_DMDCFGMD , 0xf9 }, + { R0900_P1_DEMOD , 0x08 }, + { R0900_P1_DMDCFG3 , 0xc4 }, + { R0900_P1_DMDT0M , 0x20 }, + { R0900_P1_CARFREQ , 0xed }, + { R0900_P1_TNRCFG2 , 0x82 }, + { R0900_P1_TNRCFG3 , 0x02 }, + { R0900_P1_LDT , 0xd0 }, + { R0900_P1_LDT2 , 0xb8 }, + { R0900_P1_TMGCFG , 0xd2 }, + { R0900_P1_TMGTHRISE , 0x20 }, + { R0900_P1_TMGTHFALL , 0x00 }, + { R0900_P1_SFRUPRATIO , 0xf0 }, + { R0900_P1_SFRLOWRATIO , 0x70 }, + { R0900_P1_TSCFGL , 0x20 }, + { R0900_P1_FECSPY , 0x88 }, + { R0900_P1_FSPYDATA , 0x3a }, + { R0900_P1_FBERCPT4 , 0x00 }, + { R0900_P1_FSPYBER , 0x10 }, + { R0900_P1_ERRCTRL1 , 0x35 }, + { R0900_P1_ERRCTRL2 , 0xc1 }, + { R0900_P1_CFRICFG , 0xf8 }, + { R0900_P1_NOSCFG , 0x1c }, + { R0900_P1_CORRELMANT , 0x70 }, + { R0900_P1_CORRELABS , 0x88 }, + { R0900_P1_AGC2O , 0x5b }, + { R0900_P1_AGC2REF , 0x38 }, + { R0900_P1_CARCFG , 0xe4 }, + { R0900_P1_ACLC , 0x1A }, + { R0900_P1_BCLC , 0x09 }, + { R0900_P1_CARHDR , 0x08 }, + { R0900_P1_KREFTMG , 0xc1 }, + { R0900_P1_SFRSTEP , 0x58 }, + { R0900_P1_TMGCFG2 , 0x01 }, + { R0900_P1_CAR2CFG , 0x26 }, + { R0900_P1_BCLC2S2Q , 0x86 }, + { R0900_P1_BCLC2S28 , 0x86 }, + { R0900_P1_SMAPCOEF7 , 0x77 }, + { R0900_P1_SMAPCOEF6 , 0x85 }, + { R0900_P1_SMAPCOEF5 , 0x77 }, + { R0900_P1_DMDCFG2 , 0x3b }, + { R0900_P1_MODCODLST0 , 0xff }, + { R0900_P1_MODCODLST1 , 0xff }, + { R0900_P1_MODCODLST2 , 0xff }, + { R0900_P1_MODCODLST3 , 0xff }, + { R0900_P1_MODCODLST4 , 0xff }, + { R0900_P1_MODCODLST5 , 0xff }, + { R0900_P1_MODCODLST6 , 0xff }, + { R0900_P1_MODCODLST7 , 0xcc }, + { R0900_P1_MODCODLST8 , 0xcc }, + { R0900_P1_MODCODLST9 , 0xcc }, + { R0900_P1_MODCODLSTA , 0xcc }, + { R0900_P1_MODCODLSTB , 0xcc }, + { R0900_P1_MODCODLSTC , 0xcc }, + { R0900_P1_MODCODLSTD , 0xcc }, + { R0900_P1_MODCODLSTE , 0xcc }, + { R0900_P1_MODCODLSTF , 0xcf }, + { R0900_GENCFG , 0x1d }, + { R0900_NBITER_NF4 , 0x37 }, + { R0900_NBITER_NF5 , 0x29 }, + { R0900_NBITER_NF6 , 0x37 }, + { R0900_NBITER_NF7 , 0x33 }, + { R0900_NBITER_NF8 , 0x31 }, + { R0900_NBITER_NF9 , 0x2f }, + { R0900_NBITER_NF10 , 0x39 }, + { R0900_NBITER_NF11 , 0x3a }, + { R0900_NBITER_NF12 , 0x29 }, + { R0900_NBITER_NF13 , 0x37 }, + { R0900_NBITER_NF14 , 0x33 }, + { R0900_NBITER_NF15 , 0x2f }, + { R0900_NBITER_NF16 , 0x39 }, + { R0900_NBITER_NF17 , 0x3a }, + { R0900_NBITERNOERR , 0x04 }, + { R0900_GAINLLR_NF4 , 0x0C }, + { R0900_GAINLLR_NF5 , 0x0F }, + { R0900_GAINLLR_NF6 , 0x11 }, + { R0900_GAINLLR_NF7 , 0x14 }, + { R0900_GAINLLR_NF8 , 0x17 }, + { R0900_GAINLLR_NF9 , 0x19 }, + { R0900_GAINLLR_NF10 , 0x20 }, + { R0900_GAINLLR_NF11 , 0x21 }, + { R0900_GAINLLR_NF12 , 0x0D }, + { R0900_GAINLLR_NF13 , 0x0F }, + { R0900_GAINLLR_NF14 , 0x13 }, + { R0900_GAINLLR_NF15 , 0x1A }, + { R0900_GAINLLR_NF16 , 0x1F }, + { R0900_GAINLLR_NF17 , 0x21 }, + { R0900_RCCFG2 , 0x20 }, + { R0900_P1_FECM , 0x01 }, /*disable DSS modes*/ + { R0900_P2_FECM , 0x01 }, /*disable DSS modes*/ + { R0900_P1_PRVIT , 0x2F }, /*disable puncture rate 6/7*/ + { R0900_P2_PRVIT , 0x2F }, /*disable puncture rate 6/7*/ + { R0900_STROUT1CFG , 0x4c }, + { R0900_STROUT2CFG , 0x4c }, + { R0900_CLKOUT1CFG , 0x50 }, + { R0900_CLKOUT2CFG , 0x50 }, + { R0900_DPN1CFG , 0x4a }, + { R0900_DPN2CFG , 0x4a }, + { R0900_DATA71CFG , 0x52 }, + { R0900_DATA72CFG , 0x52 }, + { R0900_P1_TSCFGM , 0xc0 }, + { R0900_P2_TSCFGM , 0xc0 }, + { R0900_P1_TSCFGH , 0xe0 }, /* DVB-CI timings */ + { R0900_P2_TSCFGH , 0xe0 }, /* DVB-CI timings */ + { R0900_P1_TSSPEED , 0x40 }, + { R0900_P2_TSSPEED , 0x40 }, +}; + +static const u16 STV0900_Cut20_AddOnVal[32][2] = { + { R0900_P2_DMDCFG3 , 0xe8 }, + { R0900_P2_DMDCFG4 , 0x10 }, + { R0900_P2_CARFREQ , 0x38 }, + { R0900_P2_CARHDR , 0x20 }, + { R0900_P2_KREFTMG , 0x5a }, + { R0900_P2_SMAPCOEF7 , 0x06 }, + { R0900_P2_SMAPCOEF6 , 0x00 }, + { R0900_P2_SMAPCOEF5 , 0x04 }, + { R0900_P2_NOSCFG , 0x0c }, + { R0900_P1_DMDCFG3 , 0xe8 }, + { R0900_P1_DMDCFG4 , 0x10 }, + { R0900_P1_CARFREQ , 0x38 }, + { R0900_P1_CARHDR , 0x20 }, + { R0900_P1_KREFTMG , 0x5a }, + { R0900_P1_SMAPCOEF7 , 0x06 }, + { R0900_P1_SMAPCOEF6 , 0x00 }, + { R0900_P1_SMAPCOEF5 , 0x04 }, + { R0900_P1_NOSCFG , 0x0c }, + { R0900_GAINLLR_NF4 , 0x21 }, + { R0900_GAINLLR_NF5 , 0x21 }, + { R0900_GAINLLR_NF6 , 0x20 }, + { R0900_GAINLLR_NF7 , 0x1F }, + { R0900_GAINLLR_NF8 , 0x1E }, + { R0900_GAINLLR_NF9 , 0x1E }, + { R0900_GAINLLR_NF10 , 0x1D }, + { R0900_GAINLLR_NF11 , 0x1B }, + { R0900_GAINLLR_NF12 , 0x20 }, + { R0900_GAINLLR_NF13 , 0x20 }, + { R0900_GAINLLR_NF14 , 0x20 }, + { R0900_GAINLLR_NF15 , 0x20 }, + { R0900_GAINLLR_NF16 , 0x20 }, + { R0900_GAINLLR_NF17 , 0x21 } + +}; + +#endif diff --git a/drivers/media/dvb-frontends/stv0900_priv.h b/drivers/media/dvb-frontends/stv0900_priv.h new file mode 100644 index 0000000000..370d5fc1c2 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0900_priv.h @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0900_priv.h + * + * Driver for ST STV0900 satellite demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0900_PRIV_H +#define STV0900_PRIV_H + +#include <linux/i2c.h> + +#define INRANGE(X, Y, Z) ((((X) <= (Y)) && ((Y) <= (Z))) \ + || (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) + +#ifndef MAKEWORD +#define MAKEWORD(X, Y) (((X) << 8) + (Y)) +#endif + +#define LSB(X) (((X) & 0xFF)) +#define MSB(Y) (((Y) >> 8) & 0xFF) + +#ifndef TRUE +#define TRUE (1 == 1) +#endif +#ifndef FALSE +#define FALSE (!TRUE) +#endif + +#define dprintk(args...) \ + do { \ + if (stvdebug) \ + printk(KERN_DEBUG args); \ + } while (0) + +#define STV0900_MAXLOOKUPSIZE 500 +#define STV0900_BLIND_SEARCH_AGC2_TH 700 +#define STV0900_BLIND_SEARCH_AGC2_TH_CUT30 1400 +#define IQPOWER_THRESHOLD 30 + +/* One point of the lookup table */ +struct stv000_lookpoint { + s32 realval;/* real value */ + s32 regval;/* binary value */ +}; + +/* Lookup table definition */ +struct stv0900_table{ + s32 size;/* Size of the lookup table */ + struct stv000_lookpoint table[STV0900_MAXLOOKUPSIZE];/* Lookup table */ +}; + +enum fe_stv0900_error { + STV0900_NO_ERROR = 0, + STV0900_INVALID_HANDLE, + STV0900_BAD_PARAMETER, + STV0900_I2C_ERROR, + STV0900_SEARCH_FAILED, +}; + +enum fe_stv0900_clock_type { + STV0900_USE_REGISTERS_DEFAULT, + STV0900_SERIAL_PUNCT_CLOCK,/*Serial punctured clock */ + STV0900_SERIAL_CONT_CLOCK,/*Serial continues clock */ + STV0900_PARALLEL_PUNCT_CLOCK,/*Parallel punctured clock */ + STV0900_DVBCI_CLOCK/*Parallel continues clock : DVBCI */ +}; + +enum fe_stv0900_search_state { + STV0900_SEARCH = 0, + STV0900_PLH_DETECTED, + STV0900_DVBS2_FOUND, + STV0900_DVBS_FOUND + +}; + +enum fe_stv0900_ldpc_state { + STV0900_PATH1_OFF_PATH2_OFF = 0, + STV0900_PATH1_ON_PATH2_OFF = 1, + STV0900_PATH1_OFF_PATH2_ON = 2, + STV0900_PATH1_ON_PATH2_ON = 3 +}; + +enum fe_stv0900_signal_type { + STV0900_NOAGC1 = 0, + STV0900_AGC1OK, + STV0900_NOTIMING, + STV0900_ANALOGCARRIER, + STV0900_TIMINGOK, + STV0900_NOAGC2, + STV0900_AGC2OK, + STV0900_NOCARRIER, + STV0900_CARRIEROK, + STV0900_NODATA, + STV0900_DATAOK, + STV0900_OUTOFRANGE, + STV0900_RANGEOK +}; + +enum fe_stv0900_demod_num { + STV0900_DEMOD_1, + STV0900_DEMOD_2 +}; + +enum fe_stv0900_tracking_standard { + STV0900_DVBS1_STANDARD,/* Found Standard*/ + STV0900_DVBS2_STANDARD, + STV0900_DSS_STANDARD, + STV0900_TURBOCODE_STANDARD, + STV0900_UNKNOWN_STANDARD +}; + +enum fe_stv0900_search_standard { + STV0900_AUTO_SEARCH, + STV0900_SEARCH_DVBS1,/* Search Standard*/ + STV0900_SEARCH_DVBS2, + STV0900_SEARCH_DSS, + STV0900_SEARCH_TURBOCODE +}; + +enum fe_stv0900_search_algo { + STV0900_BLIND_SEARCH,/* offset freq and SR are Unknown */ + STV0900_COLD_START,/* only the SR is known */ + STV0900_WARM_START/* offset freq and SR are known */ +}; + +enum fe_stv0900_modulation { + STV0900_QPSK, + STV0900_8PSK, + STV0900_16APSK, + STV0900_32APSK, + STV0900_UNKNOWN +}; + +enum fe_stv0900_modcode { + STV0900_DUMMY_PLF, + STV0900_QPSK_14, + STV0900_QPSK_13, + STV0900_QPSK_25, + STV0900_QPSK_12, + STV0900_QPSK_35, + STV0900_QPSK_23, + STV0900_QPSK_34, + STV0900_QPSK_45, + STV0900_QPSK_56, + STV0900_QPSK_89, + STV0900_QPSK_910, + STV0900_8PSK_35, + STV0900_8PSK_23, + STV0900_8PSK_34, + STV0900_8PSK_56, + STV0900_8PSK_89, + STV0900_8PSK_910, + STV0900_16APSK_23, + STV0900_16APSK_34, + STV0900_16APSK_45, + STV0900_16APSK_56, + STV0900_16APSK_89, + STV0900_16APSK_910, + STV0900_32APSK_34, + STV0900_32APSK_45, + STV0900_32APSK_56, + STV0900_32APSK_89, + STV0900_32APSK_910, + STV0900_MODCODE_UNKNOWN +}; + +enum fe_stv0900_fec {/*DVBS1, DSS and turbo code puncture rate*/ + STV0900_FEC_1_2 = 0, + STV0900_FEC_2_3, + STV0900_FEC_3_4, + STV0900_FEC_4_5,/*for turbo code only*/ + STV0900_FEC_5_6, + STV0900_FEC_6_7,/*for DSS only */ + STV0900_FEC_7_8, + STV0900_FEC_8_9,/*for turbo code only*/ + STV0900_FEC_UNKNOWN +}; + +enum fe_stv0900_frame_length { + STV0900_LONG_FRAME, + STV0900_SHORT_FRAME +}; + +enum fe_stv0900_pilot { + STV0900_PILOTS_OFF, + STV0900_PILOTS_ON +}; + +enum fe_stv0900_rolloff { + STV0900_35, + STV0900_25, + STV0900_20 +}; + +enum fe_stv0900_search_iq { + STV0900_IQ_AUTO, + STV0900_IQ_AUTO_NORMAL_FIRST, + STV0900_IQ_FORCE_NORMAL, + STV0900_IQ_FORCE_SWAPPED +}; + +enum stv0900_iq_inversion { + STV0900_IQ_NORMAL, + STV0900_IQ_SWAPPED +}; + +enum fe_stv0900_diseqc_mode { + STV0900_22KHZ_Continues = 0, + STV0900_DISEQC_2_3_PWM = 2, + STV0900_DISEQC_3_3_PWM = 3, + STV0900_DISEQC_2_3_ENVELOP = 4, + STV0900_DISEQC_3_3_ENVELOP = 5 +}; + +enum fe_stv0900_demod_mode { + STV0900_SINGLE = 0, + STV0900_DUAL +}; + +struct stv0900_init_params{ + u32 dmd_ref_clk;/* Reference,Input clock for the demod in Hz */ + + /* Demodulator Type (single demod or dual demod) */ + enum fe_stv0900_demod_mode demod_mode; + enum fe_stv0900_rolloff rolloff; + enum fe_stv0900_clock_type path1_ts_clock; + + u8 tun1_maddress; + int tuner1_adc; + int tuner1_type; + + /* IQ from the tuner1 to the demod */ + enum stv0900_iq_inversion tun1_iq_inv; + enum fe_stv0900_clock_type path2_ts_clock; + + u8 tun2_maddress; + int tuner2_adc; + int tuner2_type; + + /* IQ from the tuner2 to the demod */ + enum stv0900_iq_inversion tun2_iq_inv; + struct stv0900_reg *ts_config; +}; + +struct stv0900_search_params { + enum fe_stv0900_demod_num path;/* Path Used demod1 or 2 */ + + u32 frequency;/* Transponder frequency (in KHz) */ + u32 symbol_rate;/* Transponder symbol rate (in bds)*/ + u32 search_range;/* Range of the search (in Hz) */ + + enum fe_stv0900_search_standard standard; + enum fe_stv0900_modulation modulation; + enum fe_stv0900_fec fec; + enum fe_stv0900_modcode modcode; + enum fe_stv0900_search_iq iq_inversion; + enum fe_stv0900_search_algo search_algo; + +}; + +struct stv0900_signal_info { + int locked;/* Transponder locked */ + u32 frequency;/* Transponder frequency (in KHz) */ + u32 symbol_rate;/* Transponder symbol rate (in Mbds) */ + + enum fe_stv0900_tracking_standard standard; + enum fe_stv0900_fec fec; + enum fe_stv0900_modcode modcode; + enum fe_stv0900_modulation modulation; + enum fe_stv0900_pilot pilot; + enum fe_stv0900_frame_length frame_len; + enum stv0900_iq_inversion spectrum; + enum fe_stv0900_rolloff rolloff; + + s32 Power;/* Power of the RF signal (dBm) */ + s32 C_N;/* Carrier to noise ratio (dB x10)*/ + u32 BER;/* Bit error rate (x10^7) */ + +}; + +struct stv0900_internal{ + s32 quartz; + s32 mclk; + /* manual RollOff for DVBS1/DSS only */ + enum fe_stv0900_rolloff rolloff; + /* Demodulator use for single demod or for dual demod) */ + enum fe_stv0900_demod_mode demod_mode; + + /*Demods */ + s32 freq[2]; + s32 bw[2]; + s32 symbol_rate[2]; + s32 srch_range[2]; + /* for software/auto tuner */ + int tuner_type[2]; + + /* algorithm for search Blind, Cold or Warm*/ + enum fe_stv0900_search_algo srch_algo[2]; + /* search standard: Auto, DVBS1/DSS only or DVBS2 only*/ + enum fe_stv0900_search_standard srch_standard[2]; + /* inversion search : auto, auto norma first, normal or inverted */ + enum fe_stv0900_search_iq srch_iq_inv[2]; + enum fe_stv0900_modcode modcode[2]; + enum fe_stv0900_modulation modulation[2]; + enum fe_stv0900_fec fec[2]; + + struct stv0900_signal_info result[2]; + enum fe_stv0900_error err[2]; + + + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + u8 clkmode;/* 0 for CLKI, 2 for XTALI */ + u8 chip_id; + struct stv0900_reg *ts_config; + enum fe_stv0900_error errs; + int dmds_used; +}; + +/* state for each demod */ +struct stv0900_state { + /* pointer for internal params, one for each pair of demods */ + struct stv0900_internal *internal; + struct i2c_adapter *i2c_adap; + const struct stv0900_config *config; + struct dvb_frontend frontend; + int demod; +}; + +extern int stvdebug; + +extern s32 ge2comp(s32 a, s32 width); + +extern void stv0900_write_reg(struct stv0900_internal *i_params, + u16 reg_addr, u8 reg_data); + +extern u8 stv0900_read_reg(struct stv0900_internal *i_params, + u16 reg_addr); + +extern void stv0900_write_bits(struct stv0900_internal *i_params, + u32 label, u8 val); + +extern u8 stv0900_get_bits(struct stv0900_internal *i_params, + u32 label); + +extern int stv0900_get_demod_lock(struct stv0900_internal *i_params, + enum fe_stv0900_demod_num demod, s32 time_out); +extern int stv0900_check_signal_presence(struct stv0900_internal *i_params, + enum fe_stv0900_demod_num demod); + +extern enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe); + +extern void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, + u32 bandwidth); +extern void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth); + +extern void stv0900_start_search(struct stv0900_internal *i_params, + enum fe_stv0900_demod_num demod); + +extern u8 stv0900_get_optim_carr_loop(s32 srate, + enum fe_stv0900_modcode modcode, + s32 pilot, u8 chip_id); + +extern u8 stv0900_get_optim_short_carr_loop(s32 srate, + enum fe_stv0900_modulation modulation, + u8 chip_id); + +extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params, + enum fe_stv0900_demod_num demod); + +extern void stv0900_activate_s2_modcod(struct stv0900_internal *i_params, + enum fe_stv0900_demod_num demod); + +extern void stv0900_activate_s2_modcod_single(struct stv0900_internal *i_params, + enum fe_stv0900_demod_num demod); + +extern enum +fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, + enum fe_stv0900_demod_num demod); + +extern u32 +stv0900_get_freq_auto(struct stv0900_internal *intp, int demod); + +extern void +stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, + u32 Bandwidth, int demod); + +#endif diff --git a/drivers/media/dvb-frontends/stv0900_reg.h b/drivers/media/dvb-frontends/stv0900_reg.h new file mode 100644 index 0000000000..a5073f2bbb --- /dev/null +++ b/drivers/media/dvb-frontends/stv0900_reg.h @@ -0,0 +1,3970 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv0900_reg.h + * + * Driver for ST STV0900 satellite demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef STV0900_REG_H +#define STV0900_REG_H + +extern s32 shiftx(s32 x, int demod, s32 shift); + +#define REGx(x) shiftx(x, demod, 0x200) +#define FLDx(x) shiftx(x, demod, 0x2000000) + +/*MID*/ +#define R0900_MID 0xf100 +#define F0900_MCHIP_IDENT 0xf10000f0 +#define F0900_MRELEASE 0xf100000f + +/*DACR1*/ +#define R0900_DACR1 0xf113 +#define F0900_DAC_MODE 0xf11300e0 +#define F0900_DAC_VALUE1 0xf113000f + +/*DACR2*/ +#define R0900_DACR2 0xf114 +#define F0900_DAC_VALUE0 0xf11400ff + +/*OUTCFG*/ +#define R0900_OUTCFG 0xf11c +#define F0900_OUTSERRS1_HZ 0xf11c0040 +#define F0900_OUTSERRS2_HZ 0xf11c0020 +#define F0900_OUTSERRS3_HZ 0xf11c0010 +#define F0900_OUTPARRS3_HZ 0xf11c0008 + +/*IRQSTATUS3*/ +#define R0900_IRQSTATUS3 0xf120 +#define F0900_SPLL_LOCK 0xf1200020 +#define F0900_SSTREAM_LCK_3 0xf1200010 +#define F0900_SSTREAM_LCK_2 0xf1200008 +#define F0900_SSTREAM_LCK_1 0xf1200004 +#define F0900_SDVBS1_PRF_2 0xf1200002 +#define F0900_SDVBS1_PRF_1 0xf1200001 + +/*IRQSTATUS2*/ +#define R0900_IRQSTATUS2 0xf121 +#define F0900_SSPY_ENDSIM_3 0xf1210080 +#define F0900_SSPY_ENDSIM_2 0xf1210040 +#define F0900_SSPY_ENDSIM_1 0xf1210020 +#define F0900_SPKTDEL_ERROR_2 0xf1210010 +#define F0900_SPKTDEL_LOCKB_2 0xf1210008 +#define F0900_SPKTDEL_LOCK_2 0xf1210004 +#define F0900_SPKTDEL_ERROR_1 0xf1210002 +#define F0900_SPKTDEL_LOCKB_1 0xf1210001 + +/*IRQSTATUS1*/ +#define R0900_IRQSTATUS1 0xf122 +#define F0900_SPKTDEL_LOCK_1 0xf1220080 +#define F0900_SDEMOD_LOCKB_2 0xf1220004 +#define F0900_SDEMOD_LOCK_2 0xf1220002 +#define F0900_SDEMOD_IRQ_2 0xf1220001 + +/*IRQSTATUS0*/ +#define R0900_IRQSTATUS0 0xf123 +#define F0900_SDEMOD_LOCKB_1 0xf1230080 +#define F0900_SDEMOD_LOCK_1 0xf1230040 +#define F0900_SDEMOD_IRQ_1 0xf1230020 +#define F0900_SBCH_ERRFLAG 0xf1230010 +#define F0900_SDISEQC2RX_IRQ 0xf1230008 +#define F0900_SDISEQC2TX_IRQ 0xf1230004 +#define F0900_SDISEQC1RX_IRQ 0xf1230002 +#define F0900_SDISEQC1TX_IRQ 0xf1230001 + +/*IRQMASK3*/ +#define R0900_IRQMASK3 0xf124 +#define F0900_MPLL_LOCK 0xf1240020 +#define F0900_MSTREAM_LCK_3 0xf1240010 +#define F0900_MSTREAM_LCK_2 0xf1240008 +#define F0900_MSTREAM_LCK_1 0xf1240004 +#define F0900_MDVBS1_PRF_2 0xf1240002 +#define F0900_MDVBS1_PRF_1 0xf1240001 + +/*IRQMASK2*/ +#define R0900_IRQMASK2 0xf125 +#define F0900_MSPY_ENDSIM_3 0xf1250080 +#define F0900_MSPY_ENDSIM_2 0xf1250040 +#define F0900_MSPY_ENDSIM_1 0xf1250020 +#define F0900_MPKTDEL_ERROR_2 0xf1250010 +#define F0900_MPKTDEL_LOCKB_2 0xf1250008 +#define F0900_MPKTDEL_LOCK_2 0xf1250004 +#define F0900_MPKTDEL_ERROR_1 0xf1250002 +#define F0900_MPKTDEL_LOCKB_1 0xf1250001 + +/*IRQMASK1*/ +#define R0900_IRQMASK1 0xf126 +#define F0900_MPKTDEL_LOCK_1 0xf1260080 +#define F0900_MEXTPINB2 0xf1260040 +#define F0900_MEXTPIN2 0xf1260020 +#define F0900_MEXTPINB1 0xf1260010 +#define F0900_MEXTPIN1 0xf1260008 +#define F0900_MDEMOD_LOCKB_2 0xf1260004 +#define F0900_MDEMOD_LOCK_2 0xf1260002 +#define F0900_MDEMOD_IRQ_2 0xf1260001 + +/*IRQMASK0*/ +#define R0900_IRQMASK0 0xf127 +#define F0900_MDEMOD_LOCKB_1 0xf1270080 +#define F0900_MDEMOD_LOCK_1 0xf1270040 +#define F0900_MDEMOD_IRQ_1 0xf1270020 +#define F0900_MBCH_ERRFLAG 0xf1270010 +#define F0900_MDISEQC2RX_IRQ 0xf1270008 +#define F0900_MDISEQC2TX_IRQ 0xf1270004 +#define F0900_MDISEQC1RX_IRQ 0xf1270002 +#define F0900_MDISEQC1TX_IRQ 0xf1270001 + +/*I2CCFG*/ +#define R0900_I2CCFG 0xf129 +#define F0900_I2C_FASTMODE 0xf1290008 +#define F0900_I2CADDR_INC 0xf1290003 + +/*P1_I2CRPT*/ +#define R0900_P1_I2CRPT 0xf12a +#define I2CRPT shiftx(R0900_P1_I2CRPT, demod, -1) +#define F0900_P1_I2CT_ON 0xf12a0080 +#define I2CT_ON shiftx(F0900_P1_I2CT_ON, demod, -0x10000) +#define F0900_P1_ENARPT_LEVEL 0xf12a0070 +#define F0900_P1_SCLT_DELAY 0xf12a0008 +#define F0900_P1_STOP_ENABLE 0xf12a0004 +#define F0900_P1_STOP_SDAT2SDA 0xf12a0002 + +/*P2_I2CRPT*/ +#define R0900_P2_I2CRPT 0xf12b +#define F0900_P2_I2CT_ON 0xf12b0080 +#define F0900_P2_ENARPT_LEVEL 0xf12b0070 +#define F0900_P2_SCLT_DELAY 0xf12b0008 +#define F0900_P2_STOP_ENABLE 0xf12b0004 +#define F0900_P2_STOP_SDAT2SDA 0xf12b0002 + +/*IOPVALUE6*/ +#define R0900_IOPVALUE6 0xf138 +#define F0900_VSCL 0xf1380004 +#define F0900_VSDA 0xf1380002 +#define F0900_VDATA3_0 0xf1380001 + +/*IOPVALUE5*/ +#define R0900_IOPVALUE5 0xf139 +#define F0900_VDATA3_1 0xf1390080 +#define F0900_VDATA3_2 0xf1390040 +#define F0900_VDATA3_3 0xf1390020 +#define F0900_VDATA3_4 0xf1390010 +#define F0900_VDATA3_5 0xf1390008 +#define F0900_VDATA3_6 0xf1390004 +#define F0900_VDATA3_7 0xf1390002 +#define F0900_VCLKOUT3 0xf1390001 + +/*IOPVALUE4*/ +#define R0900_IOPVALUE4 0xf13a +#define F0900_VSTROUT3 0xf13a0080 +#define F0900_VDPN3 0xf13a0040 +#define F0900_VERROR3 0xf13a0020 +#define F0900_VDATA2_7 0xf13a0010 +#define F0900_VCLKOUT2 0xf13a0008 +#define F0900_VSTROUT2 0xf13a0004 +#define F0900_VDPN2 0xf13a0002 +#define F0900_VERROR2 0xf13a0001 + +/*IOPVALUE3*/ +#define R0900_IOPVALUE3 0xf13b +#define F0900_VDATA1_7 0xf13b0080 +#define F0900_VCLKOUT1 0xf13b0040 +#define F0900_VSTROUT1 0xf13b0020 +#define F0900_VDPN1 0xf13b0010 +#define F0900_VERROR1 0xf13b0008 +#define F0900_VCLKOUT27 0xf13b0004 +#define F0900_VDISEQCOUT2 0xf13b0002 +#define F0900_VSCLT2 0xf13b0001 + +/*IOPVALUE2*/ +#define R0900_IOPVALUE2 0xf13c +#define F0900_VSDAT2 0xf13c0080 +#define F0900_VAGCRF2 0xf13c0040 +#define F0900_VDISEQCOUT1 0xf13c0020 +#define F0900_VSCLT1 0xf13c0010 +#define F0900_VSDAT1 0xf13c0008 +#define F0900_VAGCRF1 0xf13c0004 +#define F0900_VDIRCLK 0xf13c0002 +#define F0900_VSTDBY 0xf13c0001 + +/*IOPVALUE1*/ +#define R0900_IOPVALUE1 0xf13d +#define F0900_VCS1 0xf13d0080 +#define F0900_VCS0 0xf13d0040 +#define F0900_VGPIO13 0xf13d0020 +#define F0900_VGPIO12 0xf13d0010 +#define F0900_VGPIO11 0xf13d0008 +#define F0900_VGPIO10 0xf13d0004 +#define F0900_VGPIO9 0xf13d0002 +#define F0900_VGPIO8 0xf13d0001 + +/*IOPVALUE0*/ +#define R0900_IOPVALUE0 0xf13e +#define F0900_VGPIO7 0xf13e0080 +#define F0900_VGPIO6 0xf13e0040 +#define F0900_VGPIO5 0xf13e0020 +#define F0900_VGPIO4 0xf13e0010 +#define F0900_VGPIO3 0xf13e0008 +#define F0900_VGPIO2 0xf13e0004 +#define F0900_VGPIO1 0xf13e0002 +#define F0900_VCLKI2 0xf13e0001 + +/*CLKI2CFG*/ +#define R0900_CLKI2CFG 0xf140 +#define F0900_CLKI2_OPD 0xf1400080 +#define F0900_CLKI2_CONFIG 0xf140007e +#define F0900_CLKI2_XOR 0xf1400001 + +/*GPIO1CFG*/ +#define R0900_GPIO1CFG 0xf141 +#define F0900_GPIO1_OPD 0xf1410080 +#define F0900_GPIO1_CONFIG 0xf141007e +#define F0900_GPIO1_XOR 0xf1410001 + +/*GPIO2CFG*/ +#define R0900_GPIO2CFG 0xf142 +#define F0900_GPIO2_OPD 0xf1420080 +#define F0900_GPIO2_CONFIG 0xf142007e +#define F0900_GPIO2_XOR 0xf1420001 + +/*GPIO3CFG*/ +#define R0900_GPIO3CFG 0xf143 +#define F0900_GPIO3_OPD 0xf1430080 +#define F0900_GPIO3_CONFIG 0xf143007e +#define F0900_GPIO3_XOR 0xf1430001 + +/*GPIO4CFG*/ +#define R0900_GPIO4CFG 0xf144 +#define F0900_GPIO4_OPD 0xf1440080 +#define F0900_GPIO4_CONFIG 0xf144007e +#define F0900_GPIO4_XOR 0xf1440001 + +/*GPIO5CFG*/ +#define R0900_GPIO5CFG 0xf145 +#define F0900_GPIO5_OPD 0xf1450080 +#define F0900_GPIO5_CONFIG 0xf145007e +#define F0900_GPIO5_XOR 0xf1450001 + +/*GPIO6CFG*/ +#define R0900_GPIO6CFG 0xf146 +#define F0900_GPIO6_OPD 0xf1460080 +#define F0900_GPIO6_CONFIG 0xf146007e +#define F0900_GPIO6_XOR 0xf1460001 + +/*GPIO7CFG*/ +#define R0900_GPIO7CFG 0xf147 +#define F0900_GPIO7_OPD 0xf1470080 +#define F0900_GPIO7_CONFIG 0xf147007e +#define F0900_GPIO7_XOR 0xf1470001 + +/*GPIO8CFG*/ +#define R0900_GPIO8CFG 0xf148 +#define F0900_GPIO8_OPD 0xf1480080 +#define F0900_GPIO8_CONFIG 0xf148007e +#define F0900_GPIO8_XOR 0xf1480001 + +/*GPIO9CFG*/ +#define R0900_GPIO9CFG 0xf149 +#define F0900_GPIO9_OPD 0xf1490080 +#define F0900_GPIO9_CONFIG 0xf149007e +#define F0900_GPIO9_XOR 0xf1490001 + +/*GPIO10CFG*/ +#define R0900_GPIO10CFG 0xf14a +#define F0900_GPIO10_OPD 0xf14a0080 +#define F0900_GPIO10_CONFIG 0xf14a007e +#define F0900_GPIO10_XOR 0xf14a0001 + +/*GPIO11CFG*/ +#define R0900_GPIO11CFG 0xf14b +#define F0900_GPIO11_OPD 0xf14b0080 +#define F0900_GPIO11_CONFIG 0xf14b007e +#define F0900_GPIO11_XOR 0xf14b0001 + +/*GPIO12CFG*/ +#define R0900_GPIO12CFG 0xf14c +#define F0900_GPIO12_OPD 0xf14c0080 +#define F0900_GPIO12_CONFIG 0xf14c007e +#define F0900_GPIO12_XOR 0xf14c0001 + +/*GPIO13CFG*/ +#define R0900_GPIO13CFG 0xf14d +#define F0900_GPIO13_OPD 0xf14d0080 +#define F0900_GPIO13_CONFIG 0xf14d007e +#define F0900_GPIO13_XOR 0xf14d0001 + +/*CS0CFG*/ +#define R0900_CS0CFG 0xf14e +#define F0900_CS0_OPD 0xf14e0080 +#define F0900_CS0_CONFIG 0xf14e007e +#define F0900_CS0_XOR 0xf14e0001 + +/*CS1CFG*/ +#define R0900_CS1CFG 0xf14f +#define F0900_CS1_OPD 0xf14f0080 +#define F0900_CS1_CONFIG 0xf14f007e +#define F0900_CS1_XOR 0xf14f0001 + +/*STDBYCFG*/ +#define R0900_STDBYCFG 0xf150 +#define F0900_STDBY_OPD 0xf1500080 +#define F0900_STDBY_CONFIG 0xf150007e +#define F0900_STBDY_XOR 0xf1500001 + +/*DIRCLKCFG*/ +#define R0900_DIRCLKCFG 0xf151 +#define F0900_DIRCLK_OPD 0xf1510080 +#define F0900_DIRCLK_CONFIG 0xf151007e +#define F0900_DIRCLK_XOR 0xf1510001 + +/*AGCRF1CFG*/ +#define R0900_AGCRF1CFG 0xf152 +#define F0900_AGCRF1_OPD 0xf1520080 +#define F0900_AGCRF1_CONFIG 0xf152007e +#define F0900_AGCRF1_XOR 0xf1520001 + +/*SDAT1CFG*/ +#define R0900_SDAT1CFG 0xf153 +#define F0900_SDAT1_OPD 0xf1530080 +#define F0900_SDAT1_CONFIG 0xf153007e +#define F0900_SDAT1_XOR 0xf1530001 + +/*SCLT1CFG*/ +#define R0900_SCLT1CFG 0xf154 +#define F0900_SCLT1_OPD 0xf1540080 +#define F0900_SCLT1_CONFIG 0xf154007e +#define F0900_SCLT1_XOR 0xf1540001 + +/*DISEQCO1CFG*/ +#define R0900_DISEQCO1CFG 0xf155 +#define F0900_DISEQCO1_OPD 0xf1550080 +#define F0900_DISEQCO1_CONFIG 0xf155007e +#define F0900_DISEQC1_XOR 0xf1550001 + +/*AGCRF2CFG*/ +#define R0900_AGCRF2CFG 0xf156 +#define F0900_AGCRF2_OPD 0xf1560080 +#define F0900_AGCRF2_CONFIG 0xf156007e +#define F0900_AGCRF2_XOR 0xf1560001 + +/*SDAT2CFG*/ +#define R0900_SDAT2CFG 0xf157 +#define F0900_SDAT2_OPD 0xf1570080 +#define F0900_SDAT2_CONFIG 0xf157007e +#define F0900_SDAT2_XOR 0xf1570001 + +/*SCLT2CFG*/ +#define R0900_SCLT2CFG 0xf158 +#define F0900_SCLT2_OPD 0xf1580080 +#define F0900_SCLT2_CONFIG 0xf158007e +#define F0900_SCLT2_XOR 0xf1580001 + +/*DISEQCO2CFG*/ +#define R0900_DISEQCO2CFG 0xf159 +#define F0900_DISEQCO2_OPD 0xf1590080 +#define F0900_DISEQCO2_CONFIG 0xf159007e +#define F0900_DISEQC2_XOR 0xf1590001 + +/*CLKOUT27CFG*/ +#define R0900_CLKOUT27CFG 0xf15a +#define F0900_CLKOUT27_OPD 0xf15a0080 +#define F0900_CLKOUT27_CONFIG 0xf15a007e +#define F0900_CLKOUT27_XOR 0xf15a0001 + +/*ERROR1CFG*/ +#define R0900_ERROR1CFG 0xf15b +#define F0900_ERROR1_OPD 0xf15b0080 +#define F0900_ERROR1_CONFIG 0xf15b007e +#define F0900_ERROR1_XOR 0xf15b0001 + +/*DPN1CFG*/ +#define R0900_DPN1CFG 0xf15c +#define F0900_DPN1_OPD 0xf15c0080 +#define F0900_DPN1_CONFIG 0xf15c007e +#define F0900_DPN1_XOR 0xf15c0001 + +/*STROUT1CFG*/ +#define R0900_STROUT1CFG 0xf15d +#define F0900_STROUT1_OPD 0xf15d0080 +#define F0900_STROUT1_CONFIG 0xf15d007e +#define F0900_STROUT1_XOR 0xf15d0001 + +/*CLKOUT1CFG*/ +#define R0900_CLKOUT1CFG 0xf15e +#define F0900_CLKOUT1_OPD 0xf15e0080 +#define F0900_CLKOUT1_CONFIG 0xf15e007e +#define F0900_CLKOUT1_XOR 0xf15e0001 + +/*DATA71CFG*/ +#define R0900_DATA71CFG 0xf15f +#define F0900_DATA71_OPD 0xf15f0080 +#define F0900_DATA71_CONFIG 0xf15f007e +#define F0900_DATA71_XOR 0xf15f0001 + +/*ERROR2CFG*/ +#define R0900_ERROR2CFG 0xf160 +#define F0900_ERROR2_OPD 0xf1600080 +#define F0900_ERROR2_CONFIG 0xf160007e +#define F0900_ERROR2_XOR 0xf1600001 + +/*DPN2CFG*/ +#define R0900_DPN2CFG 0xf161 +#define F0900_DPN2_OPD 0xf1610080 +#define F0900_DPN2_CONFIG 0xf161007e +#define F0900_DPN2_XOR 0xf1610001 + +/*STROUT2CFG*/ +#define R0900_STROUT2CFG 0xf162 +#define F0900_STROUT2_OPD 0xf1620080 +#define F0900_STROUT2_CONFIG 0xf162007e +#define F0900_STROUT2_XOR 0xf1620001 + +/*CLKOUT2CFG*/ +#define R0900_CLKOUT2CFG 0xf163 +#define F0900_CLKOUT2_OPD 0xf1630080 +#define F0900_CLKOUT2_CONFIG 0xf163007e +#define F0900_CLKOUT2_XOR 0xf1630001 + +/*DATA72CFG*/ +#define R0900_DATA72CFG 0xf164 +#define F0900_DATA72_OPD 0xf1640080 +#define F0900_DATA72_CONFIG 0xf164007e +#define F0900_DATA72_XOR 0xf1640001 + +/*ERROR3CFG*/ +#define R0900_ERROR3CFG 0xf165 +#define F0900_ERROR3_OPD 0xf1650080 +#define F0900_ERROR3_CONFIG 0xf165007e +#define F0900_ERROR3_XOR 0xf1650001 + +/*DPN3CFG*/ +#define R0900_DPN3CFG 0xf166 +#define F0900_DPN3_OPD 0xf1660080 +#define F0900_DPN3_CONFIG 0xf166007e +#define F0900_DPN3_XOR 0xf1660001 + +/*STROUT3CFG*/ +#define R0900_STROUT3CFG 0xf167 +#define F0900_STROUT3_OPD 0xf1670080 +#define F0900_STROUT3_CONFIG 0xf167007e +#define F0900_STROUT3_XOR 0xf1670001 + +/*CLKOUT3CFG*/ +#define R0900_CLKOUT3CFG 0xf168 +#define F0900_CLKOUT3_OPD 0xf1680080 +#define F0900_CLKOUT3_CONFIG 0xf168007e +#define F0900_CLKOUT3_XOR 0xf1680001 + +/*DATA73CFG*/ +#define R0900_DATA73CFG 0xf169 +#define F0900_DATA73_OPD 0xf1690080 +#define F0900_DATA73_CONFIG 0xf169007e +#define F0900_DATA73_XOR 0xf1690001 + +/*STRSTATUS1*/ +#define R0900_STRSTATUS1 0xf16a +#define F0900_STRSTATUS_SEL2 0xf16a00f0 +#define F0900_STRSTATUS_SEL1 0xf16a000f + +/*STRSTATUS2*/ +#define R0900_STRSTATUS2 0xf16b +#define F0900_STRSTATUS_SEL4 0xf16b00f0 +#define F0900_STRSTATUS_SEL3 0xf16b000f + +/*STRSTATUS3*/ +#define R0900_STRSTATUS3 0xf16c +#define F0900_STRSTATUS_SEL6 0xf16c00f0 +#define F0900_STRSTATUS_SEL5 0xf16c000f + +/*FSKTFC2*/ +#define R0900_FSKTFC2 0xf170 +#define F0900_FSKT_KMOD 0xf17000fc +#define F0900_FSKT_CAR2 0xf1700003 + +/*FSKTFC1*/ +#define R0900_FSKTFC1 0xf171 +#define F0900_FSKT_CAR1 0xf17100ff + +/*FSKTFC0*/ +#define R0900_FSKTFC0 0xf172 +#define F0900_FSKT_CAR0 0xf17200ff + +/*FSKTDELTAF1*/ +#define R0900_FSKTDELTAF1 0xf173 +#define F0900_FSKT_DELTAF1 0xf173000f + +/*FSKTDELTAF0*/ +#define R0900_FSKTDELTAF0 0xf174 +#define F0900_FSKT_DELTAF0 0xf17400ff + +/*FSKTCTRL*/ +#define R0900_FSKTCTRL 0xf175 +#define F0900_FSKT_EN_SGN 0xf1750040 +#define F0900_FSKT_MOD_SGN 0xf1750020 +#define F0900_FSKT_MOD_EN 0xf175001c +#define F0900_FSKT_DACMODE 0xf1750003 + +/*FSKRFC2*/ +#define R0900_FSKRFC2 0xf176 +#define F0900_FSKR_DETSGN 0xf1760040 +#define F0900_FSKR_OUTSGN 0xf1760020 +#define F0900_FSKR_KAGC 0xf176001c +#define F0900_FSKR_CAR2 0xf1760003 + +/*FSKRFC1*/ +#define R0900_FSKRFC1 0xf177 +#define F0900_FSKR_CAR1 0xf17700ff + +/*FSKRFC0*/ +#define R0900_FSKRFC0 0xf178 +#define F0900_FSKR_CAR0 0xf17800ff + +/*FSKRK1*/ +#define R0900_FSKRK1 0xf179 +#define F0900_FSKR_K1_EXP 0xf17900e0 +#define F0900_FSKR_K1_MANT 0xf179001f + +/*FSKRK2*/ +#define R0900_FSKRK2 0xf17a +#define F0900_FSKR_K2_EXP 0xf17a00e0 +#define F0900_FSKR_K2_MANT 0xf17a001f + +/*FSKRAGCR*/ +#define R0900_FSKRAGCR 0xf17b +#define F0900_FSKR_OUTCTL 0xf17b00c0 +#define F0900_FSKR_AGC_REF 0xf17b003f + +/*FSKRAGC*/ +#define R0900_FSKRAGC 0xf17c +#define F0900_FSKR_AGC_ACCU 0xf17c00ff + +/*FSKRALPHA*/ +#define R0900_FSKRALPHA 0xf17d +#define F0900_FSKR_ALPHA_EXP 0xf17d001c +#define F0900_FSKR_ALPHA_M 0xf17d0003 + +/*FSKRPLTH1*/ +#define R0900_FSKRPLTH1 0xf17e +#define F0900_FSKR_BETA 0xf17e00f0 +#define F0900_FSKR_PLL_TRESH1 0xf17e000f + +/*FSKRPLTH0*/ +#define R0900_FSKRPLTH0 0xf17f +#define F0900_FSKR_PLL_TRESH0 0xf17f00ff + +/*FSKRDF1*/ +#define R0900_FSKRDF1 0xf180 +#define F0900_FSKR_OUT 0xf1800080 +#define F0900_FSKR_DELTAF1 0xf180001f + +/*FSKRDF0*/ +#define R0900_FSKRDF0 0xf181 +#define F0900_FSKR_DELTAF0 0xf18100ff + +/*FSKRSTEPP*/ +#define R0900_FSKRSTEPP 0xf182 +#define F0900_FSKR_STEP_PLUS 0xf18200ff + +/*FSKRSTEPM*/ +#define R0900_FSKRSTEPM 0xf183 +#define F0900_FSKR_STEP_MINUS 0xf18300ff + +/*FSKRDET1*/ +#define R0900_FSKRDET1 0xf184 +#define F0900_FSKR_DETECT 0xf1840080 +#define F0900_FSKR_CARDET_ACCU1 0xf184000f + +/*FSKRDET0*/ +#define R0900_FSKRDET0 0xf185 +#define F0900_FSKR_CARDET_ACCU0 0xf18500ff + +/*FSKRDTH1*/ +#define R0900_FSKRDTH1 0xf186 +#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0 +#define F0900_FSKR_CARDET_THRESH1 0xf186000f + +/*FSKRDTH0*/ +#define R0900_FSKRDTH0 0xf187 +#define F0900_FSKR_CARDET_THRESH0 0xf18700ff + +/*FSKRLOSS*/ +#define R0900_FSKRLOSS 0xf188 +#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff + +/*P2_DISTXCTL*/ +#define R0900_P2_DISTXCTL 0xf190 +#define F0900_P2_TIM_OFF 0xf1900080 +#define F0900_P2_DISEQC_RESET 0xf1900040 +#define F0900_P2_TIM_CMD 0xf1900030 +#define F0900_P2_DIS_PRECHARGE 0xf1900008 +#define F0900_P2_DISTX_MODE 0xf1900007 + +/*P2_DISRXCTL*/ +#define R0900_P2_DISRXCTL 0xf191 +#define F0900_P2_RECEIVER_ON 0xf1910080 +#define F0900_P2_IGNO_SHORT22K 0xf1910040 +#define F0900_P2_ONECHIP_TRX 0xf1910020 +#define F0900_P2_EXT_ENVELOP 0xf1910010 +#define F0900_P2_PIN_SELECT0 0xf191000c +#define F0900_P2_IRQ_RXEND 0xf1910002 +#define F0900_P2_IRQ_4NBYTES 0xf1910001 + +/*P2_DISRX_ST0*/ +#define R0900_P2_DISRX_ST0 0xf194 +#define F0900_P2_RX_END 0xf1940080 +#define F0900_P2_RX_ACTIVE 0xf1940040 +#define F0900_P2_SHORT_22KHZ 0xf1940020 +#define F0900_P2_CONT_TONE 0xf1940010 +#define F0900_P2_FIFO_4BREADY 0xf1940008 +#define F0900_P2_FIFO_EMPTY 0xf1940004 +#define F0900_P2_ABORT_DISRX 0xf1940001 + +/*P2_DISRX_ST1*/ +#define R0900_P2_DISRX_ST1 0xf195 +#define F0900_P2_RX_FAIL 0xf1950080 +#define F0900_P2_FIFO_PARITYFAIL 0xf1950040 +#define F0900_P2_RX_NONBYTE 0xf1950020 +#define F0900_P2_FIFO_OVERFLOW 0xf1950010 +#define F0900_P2_FIFO_BYTENBR 0xf195000f + +/*P2_DISRXDATA*/ +#define R0900_P2_DISRXDATA 0xf196 +#define F0900_P2_DISRX_DATA 0xf19600ff + +/*P2_DISTXDATA*/ +#define R0900_P2_DISTXDATA 0xf197 +#define F0900_P2_DISEQC_FIFO 0xf19700ff + +/*P2_DISTXSTATUS*/ +#define R0900_P2_DISTXSTATUS 0xf198 +#define F0900_P2_TX_FAIL 0xf1980080 +#define F0900_P2_FIFO_FULL 0xf1980040 +#define F0900_P2_TX_IDLE 0xf1980020 +#define F0900_P2_GAP_BURST 0xf1980010 +#define F0900_P2_TXFIFO_BYTES 0xf198000f + +/*P2_F22TX*/ +#define R0900_P2_F22TX 0xf199 +#define F0900_P2_F22_REG 0xf19900ff + +/*P2_F22RX*/ +#define R0900_P2_F22RX 0xf19a +#define F0900_P2_F22RX_REG 0xf19a00ff + +/*P2_ACRPRESC*/ +#define R0900_P2_ACRPRESC 0xf19c +#define F0900_P2_ACR_PRESC 0xf19c0007 + +/*P2_ACRDIV*/ +#define R0900_P2_ACRDIV 0xf19d +#define F0900_P2_ACR_DIV 0xf19d00ff + +/*P1_DISTXCTL*/ +#define R0900_P1_DISTXCTL 0xf1a0 +#define DISTXCTL shiftx(R0900_P1_DISTXCTL, demod, 0x10) +#define F0900_P1_TIM_OFF 0xf1a00080 +#define F0900_P1_DISEQC_RESET 0xf1a00040 +#define DISEQC_RESET shiftx(F0900_P1_DISEQC_RESET, demod, 0x100000) +#define F0900_P1_TIM_CMD 0xf1a00030 +#define F0900_P1_DIS_PRECHARGE 0xf1a00008 +#define DIS_PRECHARGE shiftx(F0900_P1_DIS_PRECHARGE, demod, 0x100000) +#define F0900_P1_DISTX_MODE 0xf1a00007 +#define DISTX_MODE shiftx(F0900_P1_DISTX_MODE, demod, 0x100000) + +/*P1_DISRXCTL*/ +#define R0900_P1_DISRXCTL 0xf1a1 +#define DISRXCTL shiftx(R0900_P1_DISRXCTL, demod, 0x10) +#define F0900_P1_RECEIVER_ON 0xf1a10080 +#define F0900_P1_IGNO_SHORT22K 0xf1a10040 +#define F0900_P1_ONECHIP_TRX 0xf1a10020 +#define F0900_P1_EXT_ENVELOP 0xf1a10010 +#define F0900_P1_PIN_SELECT0 0xf1a1000c +#define F0900_P1_IRQ_RXEND 0xf1a10002 +#define F0900_P1_IRQ_4NBYTES 0xf1a10001 + +/*P1_DISRX_ST0*/ +#define R0900_P1_DISRX_ST0 0xf1a4 +#define DISRX_ST0 shiftx(R0900_P1_DISRX_ST0, demod, 0x10) +#define F0900_P1_RX_END 0xf1a40080 +#define RX_END shiftx(F0900_P1_RX_END, demod, 0x100000) +#define F0900_P1_RX_ACTIVE 0xf1a40040 +#define F0900_P1_SHORT_22KHZ 0xf1a40020 +#define F0900_P1_CONT_TONE 0xf1a40010 +#define F0900_P1_FIFO_4BREADY 0xf1a40008 +#define F0900_P1_FIFO_EMPTY 0xf1a40004 +#define F0900_P1_ABORT_DISRX 0xf1a40001 + +/*P1_DISRX_ST1*/ +#define R0900_P1_DISRX_ST1 0xf1a5 +#define DISRX_ST1 shiftx(R0900_P1_DISRX_ST1, demod, 0x10) +#define F0900_P1_RX_FAIL 0xf1a50080 +#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040 +#define F0900_P1_RX_NONBYTE 0xf1a50020 +#define F0900_P1_FIFO_OVERFLOW 0xf1a50010 +#define F0900_P1_FIFO_BYTENBR 0xf1a5000f +#define FIFO_BYTENBR shiftx(F0900_P1_FIFO_BYTENBR, demod, 0x100000) + +/*P1_DISRXDATA*/ +#define R0900_P1_DISRXDATA 0xf1a6 +#define DISRXDATA shiftx(R0900_P1_DISRXDATA, demod, 0x10) +#define F0900_P1_DISRX_DATA 0xf1a600ff + +/*P1_DISTXDATA*/ +#define R0900_P1_DISTXDATA 0xf1a7 +#define DISTXDATA shiftx(R0900_P1_DISTXDATA, demod, 0x10) +#define F0900_P1_DISEQC_FIFO 0xf1a700ff + +/*P1_DISTXSTATUS*/ +#define R0900_P1_DISTXSTATUS 0xf1a8 +#define F0900_P1_TX_FAIL 0xf1a80080 +#define F0900_P1_FIFO_FULL 0xf1a80040 +#define FIFO_FULL shiftx(F0900_P1_FIFO_FULL, demod, 0x100000) +#define F0900_P1_TX_IDLE 0xf1a80020 +#define TX_IDLE shiftx(F0900_P1_TX_IDLE, demod, 0x100000) +#define F0900_P1_GAP_BURST 0xf1a80010 +#define F0900_P1_TXFIFO_BYTES 0xf1a8000f + +/*P1_F22TX*/ +#define R0900_P1_F22TX 0xf1a9 +#define F22TX shiftx(R0900_P1_F22TX, demod, 0x10) +#define F0900_P1_F22_REG 0xf1a900ff + +/*P1_F22RX*/ +#define R0900_P1_F22RX 0xf1aa +#define F22RX shiftx(R0900_P1_F22RX, demod, 0x10) +#define F0900_P1_F22RX_REG 0xf1aa00ff + +/*P1_ACRPRESC*/ +#define R0900_P1_ACRPRESC 0xf1ac +#define ACRPRESC shiftx(R0900_P1_ACRPRESC, demod, 0x10) +#define F0900_P1_ACR_PRESC 0xf1ac0007 + +/*P1_ACRDIV*/ +#define R0900_P1_ACRDIV 0xf1ad +#define ACRDIV shiftx(R0900_P1_ACRDIV, demod, 0x10) +#define F0900_P1_ACR_DIV 0xf1ad00ff + +/*NCOARSE*/ +#define R0900_NCOARSE 0xf1b3 +#define F0900_M_DIV 0xf1b300ff + +/*SYNTCTRL*/ +#define R0900_SYNTCTRL 0xf1b6 +#define F0900_STANDBY 0xf1b60080 +#define F0900_BYPASSPLLCORE 0xf1b60040 +#define F0900_SELX1RATIO 0xf1b60020 +#define F0900_STOP_PLL 0xf1b60008 +#define F0900_BYPASSPLLFSK 0xf1b60004 +#define F0900_SELOSCI 0xf1b60002 +#define F0900_BYPASSPLLADC 0xf1b60001 + +/*FILTCTRL*/ +#define R0900_FILTCTRL 0xf1b7 +#define F0900_INV_CLK135 0xf1b70080 +#define F0900_SEL_FSKCKDIV 0xf1b70004 +#define F0900_INV_CLKFSK 0xf1b70002 +#define F0900_BYPASS_APPLI 0xf1b70001 + +/*PLLSTAT*/ +#define R0900_PLLSTAT 0xf1b8 +#define F0900_PLLLOCK 0xf1b80001 + +/*STOPCLK1*/ +#define R0900_STOPCLK1 0xf1c2 +#define F0900_STOP_CLKPKDT2 0xf1c20040 +#define F0900_STOP_CLKPKDT1 0xf1c20020 +#define F0900_STOP_CLKFEC 0xf1c20010 +#define F0900_STOP_CLKADCI2 0xf1c20008 +#define F0900_INV_CLKADCI2 0xf1c20004 +#define F0900_STOP_CLKADCI1 0xf1c20002 +#define F0900_INV_CLKADCI1 0xf1c20001 + +/*STOPCLK2*/ +#define R0900_STOPCLK2 0xf1c3 +#define F0900_STOP_CLKSAMP2 0xf1c30010 +#define F0900_STOP_CLKSAMP1 0xf1c30008 +#define F0900_STOP_CLKVIT2 0xf1c30004 +#define F0900_STOP_CLKVIT1 0xf1c30002 +#define STOP_CLKVIT shiftx(F0900_STOP_CLKVIT1, demod, -2) +#define F0900_STOP_CLKTS 0xf1c30001 + +/*TSTTNR0*/ +#define R0900_TSTTNR0 0xf1df +#define F0900_SEL_FSK 0xf1df0080 +#define F0900_FSK_PON 0xf1df0004 + +/*TSTTNR1*/ +#define R0900_TSTTNR1 0xf1e0 +#define F0900_ADC1_PON 0xf1e00002 +#define F0900_ADC1_INMODE 0xf1e00001 + +/*TSTTNR2*/ +#define R0900_TSTTNR2 0xf1e1 +#define F0900_DISEQC1_PON 0xf1e10020 + +/*TSTTNR3*/ +#define R0900_TSTTNR3 0xf1e2 +#define F0900_ADC2_PON 0xf1e20002 +#define F0900_ADC2_INMODE 0xf1e20001 + +/*TSTTNR4*/ +#define R0900_TSTTNR4 0xf1e3 +#define F0900_DISEQC2_PON 0xf1e30020 + +/*P2_IQCONST*/ +#define R0900_P2_IQCONST 0xf200 +#define F0900_P2_CONSTEL_SELECT 0xf2000060 +#define F0900_P2_IQSYMB_SEL 0xf200001f + +/*P2_NOSCFG*/ +#define R0900_P2_NOSCFG 0xf201 +#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020 +#define F0900_P2_NOSPLH_BETA 0xf2010018 +#define F0900_P2_NOSDATA_BETA 0xf2010007 + +/*P2_ISYMB*/ +#define R0900_P2_ISYMB 0xf202 +#define F0900_P2_I_SYMBOL 0xf20201ff + +/*P2_QSYMB*/ +#define R0900_P2_QSYMB 0xf203 +#define F0900_P2_Q_SYMBOL 0xf20301ff + +/*P2_AGC1CFG*/ +#define R0900_P2_AGC1CFG 0xf204 +#define F0900_P2_DC_FROZEN 0xf2040080 +#define F0900_P2_DC_CORRECT 0xf2040040 +#define F0900_P2_AMM_FROZEN 0xf2040020 +#define F0900_P2_AMM_CORRECT 0xf2040010 +#define F0900_P2_QUAD_FROZEN 0xf2040008 +#define F0900_P2_QUAD_CORRECT 0xf2040004 + +/*P2_AGC1CN*/ +#define R0900_P2_AGC1CN 0xf206 +#define F0900_P2_AGC1_LOCKED 0xf2060080 +#define F0900_P2_AGC1_MINPOWER 0xf2060010 +#define F0900_P2_AGCOUT_FAST 0xf2060008 +#define F0900_P2_AGCIQ_BETA 0xf2060007 + +/*P2_AGC1REF*/ +#define R0900_P2_AGC1REF 0xf207 +#define F0900_P2_AGCIQ_REF 0xf20700ff + +/*P2_IDCCOMP*/ +#define R0900_P2_IDCCOMP 0xf208 +#define F0900_P2_IAVERAGE_ADJ 0xf20801ff + +/*P2_QDCCOMP*/ +#define R0900_P2_QDCCOMP 0xf209 +#define F0900_P2_QAVERAGE_ADJ 0xf20901ff + +/*P2_POWERI*/ +#define R0900_P2_POWERI 0xf20a +#define F0900_P2_POWER_I 0xf20a00ff + +/*P2_POWERQ*/ +#define R0900_P2_POWERQ 0xf20b +#define F0900_P2_POWER_Q 0xf20b00ff + +/*P2_AGC1AMM*/ +#define R0900_P2_AGC1AMM 0xf20c +#define F0900_P2_AMM_VALUE 0xf20c00ff + +/*P2_AGC1QUAD*/ +#define R0900_P2_AGC1QUAD 0xf20d +#define F0900_P2_QUAD_VALUE 0xf20d01ff + +/*P2_AGCIQIN1*/ +#define R0900_P2_AGCIQIN1 0xf20e +#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff + +/*P2_AGCIQIN0*/ +#define R0900_P2_AGCIQIN0 0xf20f +#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff + +/*P2_DEMOD*/ +#define R0900_P2_DEMOD 0xf210 +#define F0900_P2_MANUALS2_ROLLOFF 0xf2100080 +#define F0900_P2_SPECINV_CONTROL 0xf2100030 +#define F0900_P2_FORCE_ENASAMP 0xf2100008 +#define F0900_P2_MANUALSX_ROLLOFF 0xf2100004 +#define F0900_P2_ROLLOFF_CONTROL 0xf2100003 + +/*P2_DMDMODCOD*/ +#define R0900_P2_DMDMODCOD 0xf211 +#define F0900_P2_MANUAL_MODCOD 0xf2110080 +#define F0900_P2_DEMOD_MODCOD 0xf211007c +#define F0900_P2_DEMOD_TYPE 0xf2110003 + +/*P2_DSTATUS*/ +#define R0900_P2_DSTATUS 0xf212 +#define F0900_P2_CAR_LOCK 0xf2120080 +#define F0900_P2_TMGLOCK_QUALITY 0xf2120060 +#define F0900_P2_LOCK_DEFINITIF 0xf2120008 +#define F0900_P2_OVADC_DETECT 0xf2120001 + +/*P2_DSTATUS2*/ +#define R0900_P2_DSTATUS2 0xf213 +#define F0900_P2_DEMOD_DELOCK 0xf2130080 +#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008 +#define F0900_P2_AGC2_OVERFLOW 0xf2130004 +#define F0900_P2_CFR_OVERFLOW 0xf2130002 +#define F0900_P2_GAMMA_OVERUNDER 0xf2130001 + +/*P2_DMDCFGMD*/ +#define R0900_P2_DMDCFGMD 0xf214 +#define F0900_P2_DVBS2_ENABLE 0xf2140080 +#define F0900_P2_DVBS1_ENABLE 0xf2140040 +#define F0900_P2_SCAN_ENABLE 0xf2140010 +#define F0900_P2_CFR_AUTOSCAN 0xf2140008 +#define F0900_P2_TUN_RNG 0xf2140003 + +/*P2_DMDCFG2*/ +#define R0900_P2_DMDCFG2 0xf215 +#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040 +#define F0900_P2_INFINITE_RELOCK 0xf2150010 + +/*P2_DMDISTATE*/ +#define R0900_P2_DMDISTATE 0xf216 +#define F0900_P2_I2C_DEMOD_MODE 0xf216001f + +/*P2_DMDT0M*/ +#define R0900_P2_DMDT0M 0xf217 +#define F0900_P2_DMDT0_MIN 0xf21700ff + +/*P2_DMDSTATE*/ +#define R0900_P2_DMDSTATE 0xf21b +#define F0900_P2_HEADER_MODE 0xf21b0060 + +/*P2_DMDFLYW*/ +#define R0900_P2_DMDFLYW 0xf21c +#define F0900_P2_I2C_IRQVAL 0xf21c00f0 +#define F0900_P2_FLYWHEEL_CPT 0xf21c000f + +/*P2_DSTATUS3*/ +#define R0900_P2_DSTATUS3 0xf21d +#define F0900_P2_DEMOD_CFGMODE 0xf21d0060 + +/*P2_DMDCFG3*/ +#define R0900_P2_DMDCFG3 0xf21e +#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008 + +/*P2_DMDCFG4*/ +#define R0900_P2_DMDCFG4 0xf21f +#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008 + +/*P2_CORRELMANT*/ +#define R0900_P2_CORRELMANT 0xf220 +#define F0900_P2_CORREL_MANT 0xf22000ff + +/*P2_CORRELABS*/ +#define R0900_P2_CORRELABS 0xf221 +#define F0900_P2_CORREL_ABS 0xf22100ff + +/*P2_CORRELEXP*/ +#define R0900_P2_CORRELEXP 0xf222 +#define F0900_P2_CORREL_ABSEXP 0xf22200f0 +#define F0900_P2_CORREL_EXP 0xf222000f + +/*P2_PLHMODCOD*/ +#define R0900_P2_PLHMODCOD 0xf224 +#define F0900_P2_SPECINV_DEMOD 0xf2240080 +#define F0900_P2_PLH_MODCOD 0xf224007c +#define F0900_P2_PLH_TYPE 0xf2240003 + +/*P2_DMDREG*/ +#define R0900_P2_DMDREG 0xf225 +#define F0900_P2_DECIM_PLFRAMES 0xf2250001 + +/*P2_AGC2O*/ +#define R0900_P2_AGC2O 0xf22c +#define F0900_P2_AGC2_COEF 0xf22c0007 + +/*P2_AGC2REF*/ +#define R0900_P2_AGC2REF 0xf22d +#define F0900_P2_AGC2_REF 0xf22d00ff + +/*P2_AGC1ADJ*/ +#define R0900_P2_AGC1ADJ 0xf22e +#define F0900_P2_AGC1_ADJUSTED 0xf22e007f + +/*P2_AGC2I1*/ +#define R0900_P2_AGC2I1 0xf236 +#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff + +/*P2_AGC2I0*/ +#define R0900_P2_AGC2I0 0xf237 +#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff + +/*P2_CARCFG*/ +#define R0900_P2_CARCFG 0xf238 +#define F0900_P2_CFRUPLOW_AUTO 0xf2380080 +#define F0900_P2_CFRUPLOW_TEST 0xf2380040 +#define F0900_P2_ROTAON 0xf2380004 +#define F0900_P2_PH_DET_ALGO 0xf2380003 + +/*P2_ACLC*/ +#define R0900_P2_ACLC 0xf239 +#define F0900_P2_CAR_ALPHA_MANT 0xf2390030 +#define F0900_P2_CAR_ALPHA_EXP 0xf239000f + +/*P2_BCLC*/ +#define R0900_P2_BCLC 0xf23a +#define F0900_P2_CAR_BETA_MANT 0xf23a0030 +#define F0900_P2_CAR_BETA_EXP 0xf23a000f + +/*P2_CARFREQ*/ +#define R0900_P2_CARFREQ 0xf23d +#define F0900_P2_KC_COARSE_EXP 0xf23d00f0 +#define F0900_P2_BETA_FREQ 0xf23d000f + +/*P2_CARHDR*/ +#define R0900_P2_CARHDR 0xf23e +#define F0900_P2_K_FREQ_HDR 0xf23e00ff + +/*P2_LDT*/ +#define R0900_P2_LDT 0xf23f +#define F0900_P2_CARLOCK_THRES 0xf23f01ff + +/*P2_LDT2*/ +#define R0900_P2_LDT2 0xf240 +#define F0900_P2_CARLOCK_THRES2 0xf24001ff + +/*P2_CFRICFG*/ +#define R0900_P2_CFRICFG 0xf241 +#define F0900_P2_NEG_CFRSTEP 0xf2410001 + +/*P2_CFRUP1*/ +#define R0900_P2_CFRUP1 0xf242 +#define F0900_P2_CFR_UP1 0xf24201ff + +/*P2_CFRUP0*/ +#define R0900_P2_CFRUP0 0xf243 +#define F0900_P2_CFR_UP0 0xf24300ff + +/*P2_CFRLOW1*/ +#define R0900_P2_CFRLOW1 0xf246 +#define F0900_P2_CFR_LOW1 0xf24601ff + +/*P2_CFRLOW0*/ +#define R0900_P2_CFRLOW0 0xf247 +#define F0900_P2_CFR_LOW0 0xf24700ff + +/*P2_CFRINIT1*/ +#define R0900_P2_CFRINIT1 0xf248 +#define F0900_P2_CFR_INIT1 0xf24801ff + +/*P2_CFRINIT0*/ +#define R0900_P2_CFRINIT0 0xf249 +#define F0900_P2_CFR_INIT0 0xf24900ff + +/*P2_CFRINC1*/ +#define R0900_P2_CFRINC1 0xf24a +#define F0900_P2_MANUAL_CFRINC 0xf24a0080 +#define F0900_P2_CFR_INC1 0xf24a003f + +/*P2_CFRINC0*/ +#define R0900_P2_CFRINC0 0xf24b +#define F0900_P2_CFR_INC0 0xf24b00f8 + +/*P2_CFR2*/ +#define R0900_P2_CFR2 0xf24c +#define F0900_P2_CAR_FREQ2 0xf24c01ff + +/*P2_CFR1*/ +#define R0900_P2_CFR1 0xf24d +#define F0900_P2_CAR_FREQ1 0xf24d00ff + +/*P2_CFR0*/ +#define R0900_P2_CFR0 0xf24e +#define F0900_P2_CAR_FREQ0 0xf24e00ff + +/*P2_LDI*/ +#define R0900_P2_LDI 0xf24f +#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff + +/*P2_TMGCFG*/ +#define R0900_P2_TMGCFG 0xf250 +#define F0900_P2_TMGLOCK_BETA 0xf25000c0 +#define F0900_P2_DO_TIMING_CORR 0xf2500010 +#define F0900_P2_TMG_MINFREQ 0xf2500003 + +/*P2_RTC*/ +#define R0900_P2_RTC 0xf251 +#define F0900_P2_TMGALPHA_EXP 0xf25100f0 +#define F0900_P2_TMGBETA_EXP 0xf251000f + +/*P2_RTCS2*/ +#define R0900_P2_RTCS2 0xf252 +#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0 +#define F0900_P2_TMGBETAS2_EXP 0xf252000f + +/*P2_TMGTHRISE*/ +#define R0900_P2_TMGTHRISE 0xf253 +#define F0900_P2_TMGLOCK_THRISE 0xf25300ff + +/*P2_TMGTHFALL*/ +#define R0900_P2_TMGTHFALL 0xf254 +#define F0900_P2_TMGLOCK_THFALL 0xf25400ff + +/*P2_SFRUPRATIO*/ +#define R0900_P2_SFRUPRATIO 0xf255 +#define F0900_P2_SFR_UPRATIO 0xf25500ff + +/*P2_SFRLOWRATIO*/ +#define R0900_P2_SFRLOWRATIO 0xf256 +#define F0900_P2_SFR_LOWRATIO 0xf25600ff + +/*P2_KREFTMG*/ +#define R0900_P2_KREFTMG 0xf258 +#define F0900_P2_KREF_TMG 0xf25800ff + +/*P2_SFRSTEP*/ +#define R0900_P2_SFRSTEP 0xf259 +#define F0900_P2_SFR_SCANSTEP 0xf25900f0 +#define F0900_P2_SFR_CENTERSTEP 0xf259000f + +/*P2_TMGCFG2*/ +#define R0900_P2_TMGCFG2 0xf25a +#define F0900_P2_SFRRATIO_FINE 0xf25a0001 + +/*P2_KREFTMG2*/ +#define R0900_P2_KREFTMG2 0xf25b +#define F0900_P2_KREF_TMG2 0xf25b00ff + +/*P2_SFRINIT1*/ +#define R0900_P2_SFRINIT1 0xf25e +#define F0900_P2_SFR_INIT1 0xf25e007f + +/*P2_SFRINIT0*/ +#define R0900_P2_SFRINIT0 0xf25f +#define F0900_P2_SFR_INIT0 0xf25f00ff + +/*P2_SFRUP1*/ +#define R0900_P2_SFRUP1 0xf260 +#define F0900_P2_AUTO_GUP 0xf2600080 +#define F0900_P2_SYMB_FREQ_UP1 0xf260007f + +/*P2_SFRUP0*/ +#define R0900_P2_SFRUP0 0xf261 +#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff + +/*P2_SFRLOW1*/ +#define R0900_P2_SFRLOW1 0xf262 +#define F0900_P2_AUTO_GLOW 0xf2620080 +#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f + +/*P2_SFRLOW0*/ +#define R0900_P2_SFRLOW0 0xf263 +#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff + +/*P2_SFR3*/ +#define R0900_P2_SFR3 0xf264 +#define F0900_P2_SYMB_FREQ3 0xf26400ff + +/*P2_SFR2*/ +#define R0900_P2_SFR2 0xf265 +#define F0900_P2_SYMB_FREQ2 0xf26500ff + +/*P2_SFR1*/ +#define R0900_P2_SFR1 0xf266 +#define F0900_P2_SYMB_FREQ1 0xf26600ff + +/*P2_SFR0*/ +#define R0900_P2_SFR0 0xf267 +#define F0900_P2_SYMB_FREQ0 0xf26700ff + +/*P2_TMGREG2*/ +#define R0900_P2_TMGREG2 0xf268 +#define F0900_P2_TMGREG2 0xf26800ff + +/*P2_TMGREG1*/ +#define R0900_P2_TMGREG1 0xf269 +#define F0900_P2_TMGREG1 0xf26900ff + +/*P2_TMGREG0*/ +#define R0900_P2_TMGREG0 0xf26a +#define F0900_P2_TMGREG0 0xf26a00ff + +/*P2_TMGLOCK1*/ +#define R0900_P2_TMGLOCK1 0xf26b +#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff + +/*P2_TMGLOCK0*/ +#define R0900_P2_TMGLOCK0 0xf26c +#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff + +/*P2_TMGOBS*/ +#define R0900_P2_TMGOBS 0xf26d +#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0 + +/*P2_EQUALCFG*/ +#define R0900_P2_EQUALCFG 0xf26f +#define F0900_P2_EQUAL_ON 0xf26f0040 +#define F0900_P2_MU_EQUALDFE 0xf26f0007 + +/*P2_EQUAI1*/ +#define R0900_P2_EQUAI1 0xf270 +#define F0900_P2_EQUA_ACCI1 0xf27001ff + +/*P2_EQUAQ1*/ +#define R0900_P2_EQUAQ1 0xf271 +#define F0900_P2_EQUA_ACCQ1 0xf27101ff + +/*P2_EQUAI2*/ +#define R0900_P2_EQUAI2 0xf272 +#define F0900_P2_EQUA_ACCI2 0xf27201ff + +/*P2_EQUAQ2*/ +#define R0900_P2_EQUAQ2 0xf273 +#define F0900_P2_EQUA_ACCQ2 0xf27301ff + +/*P2_EQUAI3*/ +#define R0900_P2_EQUAI3 0xf274 +#define F0900_P2_EQUA_ACCI3 0xf27401ff + +/*P2_EQUAQ3*/ +#define R0900_P2_EQUAQ3 0xf275 +#define F0900_P2_EQUA_ACCQ3 0xf27501ff + +/*P2_EQUAI4*/ +#define R0900_P2_EQUAI4 0xf276 +#define F0900_P2_EQUA_ACCI4 0xf27601ff + +/*P2_EQUAQ4*/ +#define R0900_P2_EQUAQ4 0xf277 +#define F0900_P2_EQUA_ACCQ4 0xf27701ff + +/*P2_EQUAI5*/ +#define R0900_P2_EQUAI5 0xf278 +#define F0900_P2_EQUA_ACCI5 0xf27801ff + +/*P2_EQUAQ5*/ +#define R0900_P2_EQUAQ5 0xf279 +#define F0900_P2_EQUA_ACCQ5 0xf27901ff + +/*P2_EQUAI6*/ +#define R0900_P2_EQUAI6 0xf27a +#define F0900_P2_EQUA_ACCI6 0xf27a01ff + +/*P2_EQUAQ6*/ +#define R0900_P2_EQUAQ6 0xf27b +#define F0900_P2_EQUA_ACCQ6 0xf27b01ff + +/*P2_EQUAI7*/ +#define R0900_P2_EQUAI7 0xf27c +#define F0900_P2_EQUA_ACCI7 0xf27c01ff + +/*P2_EQUAQ7*/ +#define R0900_P2_EQUAQ7 0xf27d +#define F0900_P2_EQUA_ACCQ7 0xf27d01ff + +/*P2_EQUAI8*/ +#define R0900_P2_EQUAI8 0xf27e +#define F0900_P2_EQUA_ACCI8 0xf27e01ff + +/*P2_EQUAQ8*/ +#define R0900_P2_EQUAQ8 0xf27f +#define F0900_P2_EQUA_ACCQ8 0xf27f01ff + +/*P2_NNOSDATAT1*/ +#define R0900_P2_NNOSDATAT1 0xf280 +#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff + +/*P2_NNOSDATAT0*/ +#define R0900_P2_NNOSDATAT0 0xf281 +#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff + +/*P2_NNOSDATA1*/ +#define R0900_P2_NNOSDATA1 0xf282 +#define F0900_P2_NOSDATA_NORMED1 0xf28200ff + +/*P2_NNOSDATA0*/ +#define R0900_P2_NNOSDATA0 0xf283 +#define F0900_P2_NOSDATA_NORMED0 0xf28300ff + +/*P2_NNOSPLHT1*/ +#define R0900_P2_NNOSPLHT1 0xf284 +#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff + +/*P2_NNOSPLHT0*/ +#define R0900_P2_NNOSPLHT0 0xf285 +#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff + +/*P2_NNOSPLH1*/ +#define R0900_P2_NNOSPLH1 0xf286 +#define F0900_P2_NOSPLH_NORMED1 0xf28600ff + +/*P2_NNOSPLH0*/ +#define R0900_P2_NNOSPLH0 0xf287 +#define F0900_P2_NOSPLH_NORMED0 0xf28700ff + +/*P2_NOSDATAT1*/ +#define R0900_P2_NOSDATAT1 0xf288 +#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff + +/*P2_NOSDATAT0*/ +#define R0900_P2_NOSDATAT0 0xf289 +#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff + +/*P2_NOSDATA1*/ +#define R0900_P2_NOSDATA1 0xf28a +#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff + +/*P2_NOSDATA0*/ +#define R0900_P2_NOSDATA0 0xf28b +#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff + +/*P2_NOSPLHT1*/ +#define R0900_P2_NOSPLHT1 0xf28c +#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff + +/*P2_NOSPLHT0*/ +#define R0900_P2_NOSPLHT0 0xf28d +#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff + +/*P2_NOSPLH1*/ +#define R0900_P2_NOSPLH1 0xf28e +#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff + +/*P2_NOSPLH0*/ +#define R0900_P2_NOSPLH0 0xf28f +#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff + +/*P2_CAR2CFG*/ +#define R0900_P2_CAR2CFG 0xf290 +#define F0900_P2_CARRIER3_DISABLE 0xf2900040 +#define F0900_P2_ROTA2ON 0xf2900004 +#define F0900_P2_PH_DET_ALGO2 0xf2900003 + +/*P2_CFR2CFR1*/ +#define R0900_P2_CFR2CFR1 0xf291 +#define F0900_P2_CFR2TOCFR1_DVBS1 0xf29100c0 +#define F0900_P2_EN_S2CAR2CENTER 0xf2910020 +#define F0900_P2_DIS_BCHERRCFR2 0xf2910010 +#define F0900_P2_CFR2TOCFR1_BETA 0xf2910007 + +/*P2_CFR22*/ +#define R0900_P2_CFR22 0xf293 +#define F0900_P2_CAR2_FREQ2 0xf29301ff + +/*P2_CFR21*/ +#define R0900_P2_CFR21 0xf294 +#define F0900_P2_CAR2_FREQ1 0xf29400ff + +/*P2_CFR20*/ +#define R0900_P2_CFR20 0xf295 +#define F0900_P2_CAR2_FREQ0 0xf29500ff + +/*P2_ACLC2S2Q*/ +#define R0900_P2_ACLC2S2Q 0xf297 +#define F0900_P2_ENAB_SPSKSYMB 0xf2970080 +#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030 +#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f + +/*P2_ACLC2S28*/ +#define R0900_P2_ACLC2S28 0xf298 +#define F0900_P2_OLDI3Q_MODE 0xf2980080 +#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030 +#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f + +/*P2_ACLC2S216A*/ +#define R0900_P2_ACLC2S216A 0xf299 +#define F0900_P2_DIS_C3STOPA2 0xf2990080 +#define F0900_P2_CAR2S2_16ADERAT 0xf2990040 +#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030 +#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f + +/*P2_ACLC2S232A*/ +#define R0900_P2_ACLC2S232A 0xf29a +#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040 +#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030 +#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f + +/*P2_BCLC2S2Q*/ +#define R0900_P2_BCLC2S2Q 0xf29c +#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030 +#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f + +/*P2_BCLC2S28*/ +#define R0900_P2_BCLC2S28 0xf29d +#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030 +#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f + +/*P2_BCLC2S216A*/ +#define R0900_P2_BCLC2S216A 0xf29e + +/*P2_BCLC2S232A*/ +#define R0900_P2_BCLC2S232A 0xf29f + +/*P2_PLROOT2*/ +#define R0900_P2_PLROOT2 0xf2ac +#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c +#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003 + +/*P2_PLROOT1*/ +#define R0900_P2_PLROOT1 0xf2ad +#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff + +/*P2_PLROOT0*/ +#define R0900_P2_PLROOT0 0xf2ae +#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff + +/*P2_MODCODLST0*/ +#define R0900_P2_MODCODLST0 0xf2b0 + +/*P2_MODCODLST1*/ +#define R0900_P2_MODCODLST1 0xf2b1 +#define F0900_P2_DIS_MODCOD29 0xf2b100f0 +#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f + +/*P2_MODCODLST2*/ +#define R0900_P2_MODCODLST2 0xf2b2 +#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0 +#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f + +/*P2_MODCODLST3*/ +#define R0900_P2_MODCODLST3 0xf2b3 +#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0 +#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f + +/*P2_MODCODLST4*/ +#define R0900_P2_MODCODLST4 0xf2b4 +#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0 +#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f + +/*P2_MODCODLST5*/ +#define R0900_P2_MODCODLST5 0xf2b5 +#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0 +#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f + +/*P2_MODCODLST6*/ +#define R0900_P2_MODCODLST6 0xf2b6 +#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0 +#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f + +/*P2_MODCODLST7*/ +#define R0900_P2_MODCODLST7 0xf2b7 +#define F0900_P2_DIS_8P_9_10 0xf2b700f0 +#define F0900_P2_DIS_8P_8_9 0xf2b7000f + +/*P2_MODCODLST8*/ +#define R0900_P2_MODCODLST8 0xf2b8 +#define F0900_P2_DIS_8P_5_6 0xf2b800f0 +#define F0900_P2_DIS_8P_3_4 0xf2b8000f + +/*P2_MODCODLST9*/ +#define R0900_P2_MODCODLST9 0xf2b9 +#define F0900_P2_DIS_8P_2_3 0xf2b900f0 +#define F0900_P2_DIS_8P_3_5 0xf2b9000f + +/*P2_MODCODLSTA*/ +#define R0900_P2_MODCODLSTA 0xf2ba +#define F0900_P2_DIS_QP_9_10 0xf2ba00f0 +#define F0900_P2_DIS_QP_8_9 0xf2ba000f + +/*P2_MODCODLSTB*/ +#define R0900_P2_MODCODLSTB 0xf2bb +#define F0900_P2_DIS_QP_5_6 0xf2bb00f0 +#define F0900_P2_DIS_QP_4_5 0xf2bb000f + +/*P2_MODCODLSTC*/ +#define R0900_P2_MODCODLSTC 0xf2bc +#define F0900_P2_DIS_QP_3_4 0xf2bc00f0 +#define F0900_P2_DIS_QP_2_3 0xf2bc000f + +/*P2_MODCODLSTD*/ +#define R0900_P2_MODCODLSTD 0xf2bd +#define F0900_P2_DIS_QP_3_5 0xf2bd00f0 +#define F0900_P2_DIS_QP_1_2 0xf2bd000f + +/*P2_MODCODLSTE*/ +#define R0900_P2_MODCODLSTE 0xf2be +#define F0900_P2_DIS_QP_2_5 0xf2be00f0 +#define F0900_P2_DIS_QP_1_3 0xf2be000f + +/*P2_MODCODLSTF*/ +#define R0900_P2_MODCODLSTF 0xf2bf +#define F0900_P2_DIS_QP_1_4 0xf2bf00f0 + +/*P2_GAUSSR0*/ +#define R0900_P2_GAUSSR0 0xf2c0 +#define F0900_P2_EN_CCIMODE 0xf2c00080 +#define F0900_P2_R0_GAUSSIEN 0xf2c0007f + +/*P2_CCIR0*/ +#define R0900_P2_CCIR0 0xf2c1 +#define F0900_P2_CCIDETECT_PLHONLY 0xf2c10080 +#define F0900_P2_R0_CCI 0xf2c1007f + +/*P2_CCIQUANT*/ +#define R0900_P2_CCIQUANT 0xf2c2 +#define F0900_P2_CCI_BETA 0xf2c200e0 +#define F0900_P2_CCI_QUANT 0xf2c2001f + +/*P2_CCITHRES*/ +#define R0900_P2_CCITHRES 0xf2c3 +#define F0900_P2_CCI_THRESHOLD 0xf2c300ff + +/*P2_CCIACC*/ +#define R0900_P2_CCIACC 0xf2c4 +#define F0900_P2_CCI_VALUE 0xf2c400ff + +/*P2_DMDRESCFG*/ +#define R0900_P2_DMDRESCFG 0xf2c6 +#define F0900_P2_DMDRES_RESET 0xf2c60080 +#define F0900_P2_DMDRES_STRALL 0xf2c60008 +#define F0900_P2_DMDRES_NEWONLY 0xf2c60004 +#define F0900_P2_DMDRES_NOSTORE 0xf2c60002 + +/*P2_DMDRESADR*/ +#define R0900_P2_DMDRESADR 0xf2c7 +#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040 +#define F0900_P2_DMDRES_MEMFULL 0xf2c70030 +#define F0900_P2_DMDRES_RESNBR 0xf2c7000f + +/*P2_DMDRESDATA7*/ +#define R0900_P2_DMDRESDATA7 0xf2c8 +#define F0900_P2_DMDRES_DATA7 0xf2c800ff + +/*P2_DMDRESDATA6*/ +#define R0900_P2_DMDRESDATA6 0xf2c9 +#define F0900_P2_DMDRES_DATA6 0xf2c900ff + +/*P2_DMDRESDATA5*/ +#define R0900_P2_DMDRESDATA5 0xf2ca +#define F0900_P2_DMDRES_DATA5 0xf2ca00ff + +/*P2_DMDRESDATA4*/ +#define R0900_P2_DMDRESDATA4 0xf2cb +#define F0900_P2_DMDRES_DATA4 0xf2cb00ff + +/*P2_DMDRESDATA3*/ +#define R0900_P2_DMDRESDATA3 0xf2cc +#define F0900_P2_DMDRES_DATA3 0xf2cc00ff + +/*P2_DMDRESDATA2*/ +#define R0900_P2_DMDRESDATA2 0xf2cd +#define F0900_P2_DMDRES_DATA2 0xf2cd00ff + +/*P2_DMDRESDATA1*/ +#define R0900_P2_DMDRESDATA1 0xf2ce +#define F0900_P2_DMDRES_DATA1 0xf2ce00ff + +/*P2_DMDRESDATA0*/ +#define R0900_P2_DMDRESDATA0 0xf2cf +#define F0900_P2_DMDRES_DATA0 0xf2cf00ff + +/*P2_FFEI1*/ +#define R0900_P2_FFEI1 0xf2d0 +#define F0900_P2_FFE_ACCI1 0xf2d001ff + +/*P2_FFEQ1*/ +#define R0900_P2_FFEQ1 0xf2d1 +#define F0900_P2_FFE_ACCQ1 0xf2d101ff + +/*P2_FFEI2*/ +#define R0900_P2_FFEI2 0xf2d2 +#define F0900_P2_FFE_ACCI2 0xf2d201ff + +/*P2_FFEQ2*/ +#define R0900_P2_FFEQ2 0xf2d3 +#define F0900_P2_FFE_ACCQ2 0xf2d301ff + +/*P2_FFEI3*/ +#define R0900_P2_FFEI3 0xf2d4 +#define F0900_P2_FFE_ACCI3 0xf2d401ff + +/*P2_FFEQ3*/ +#define R0900_P2_FFEQ3 0xf2d5 +#define F0900_P2_FFE_ACCQ3 0xf2d501ff + +/*P2_FFEI4*/ +#define R0900_P2_FFEI4 0xf2d6 +#define F0900_P2_FFE_ACCI4 0xf2d601ff + +/*P2_FFEQ4*/ +#define R0900_P2_FFEQ4 0xf2d7 +#define F0900_P2_FFE_ACCQ4 0xf2d701ff + +/*P2_FFECFG*/ +#define R0900_P2_FFECFG 0xf2d8 +#define F0900_P2_EQUALFFE_ON 0xf2d80040 +#define F0900_P2_MU_EQUALFFE 0xf2d80007 + +/*P2_TNRCFG*/ +#define R0900_P2_TNRCFG 0xf2e0 +#define F0900_P2_TUN_ACKFAIL 0xf2e00080 +#define F0900_P2_TUN_TYPE 0xf2e00070 +#define F0900_P2_TUN_SECSTOP 0xf2e00008 +#define F0900_P2_TUN_VCOSRCH 0xf2e00004 +#define F0900_P2_TUN_MADDRESS 0xf2e00003 + +/*P2_TNRCFG2*/ +#define R0900_P2_TNRCFG2 0xf2e1 +#define F0900_P2_TUN_IQSWAP 0xf2e10080 +#define F0900_P2_DIS_BWCALC 0xf2e10004 +#define F0900_P2_SHORT_WAITSTATES 0xf2e10002 + +/*P2_TNRXTAL*/ +#define R0900_P2_TNRXTAL 0xf2e4 +#define F0900_P2_TUN_XTALFREQ 0xf2e4001f + +/*P2_TNRSTEPS*/ +#define R0900_P2_TNRSTEPS 0xf2e7 +#define F0900_P2_TUNER_BW0P125 0xf2e70080 +#define F0900_P2_BWINC_OFFSET 0xf2e70170 +#define F0900_P2_SOFTSTEP_RNG 0xf2e70008 +#define F0900_P2_TUN_BWOFFSET 0xf2e70007 + +/*P2_TNRGAIN*/ +#define R0900_P2_TNRGAIN 0xf2e8 +#define F0900_P2_TUN_KDIVEN 0xf2e800c0 +#define F0900_P2_STB6X00_OCK 0xf2e80030 +#define F0900_P2_TUN_GAIN 0xf2e8000f + +/*P2_TNRRF1*/ +#define R0900_P2_TNRRF1 0xf2e9 +#define F0900_P2_TUN_RFFREQ2 0xf2e900ff + +/*P2_TNRRF0*/ +#define R0900_P2_TNRRF0 0xf2ea +#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff + +/*P2_TNRBW*/ +#define R0900_P2_TNRBW 0xf2eb +#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0 +#define F0900_P2_TUN_BW 0xf2eb003f + +/*P2_TNRADJ*/ +#define R0900_P2_TNRADJ 0xf2ec +#define F0900_P2_STB61X0_CALTIME 0xf2ec0040 + +/*P2_TNRCTL2*/ +#define R0900_P2_TNRCTL2 0xf2ed +#define F0900_P2_STB61X0_RCCKOFF 0xf2ed0080 +#define F0900_P2_STB61X0_ICP_SDOFF 0xf2ed0040 +#define F0900_P2_STB61X0_DCLOOPOFF 0xf2ed0020 +#define F0900_P2_STB61X0_REFOUTSEL 0xf2ed0010 +#define F0900_P2_STB61X0_CALOFF 0xf2ed0008 +#define F0900_P2_STB6XX0_LPT_BEN 0xf2ed0004 +#define F0900_P2_STB6XX0_RX_OSCP 0xf2ed0002 +#define F0900_P2_STB6XX0_SYN 0xf2ed0001 + +/*P2_TNRCFG3*/ +#define R0900_P2_TNRCFG3 0xf2ee +#define F0900_P2_TUN_PLLFREQ 0xf2ee001c +#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003 + +/*P2_TNRLAUNCH*/ +#define R0900_P2_TNRLAUNCH 0xf2f0 + +/*P2_TNRLD*/ +#define R0900_P2_TNRLD 0xf2f0 +#define F0900_P2_TUNLD_VCOING 0xf2f00080 +#define F0900_P2_TUN_REG1FAIL 0xf2f00040 +#define F0900_P2_TUN_REG2FAIL 0xf2f00020 +#define F0900_P2_TUN_REG3FAIL 0xf2f00010 +#define F0900_P2_TUN_REG4FAIL 0xf2f00008 +#define F0900_P2_TUN_REG5FAIL 0xf2f00004 +#define F0900_P2_TUN_BWING 0xf2f00002 +#define F0900_P2_TUN_LOCKED 0xf2f00001 + +/*P2_TNROBSL*/ +#define R0900_P2_TNROBSL 0xf2f6 +#define F0900_P2_TUN_I2CABORTED 0xf2f60080 +#define F0900_P2_TUN_LPEN 0xf2f60040 +#define F0900_P2_TUN_FCCK 0xf2f60020 +#define F0900_P2_TUN_I2CLOCKED 0xf2f60010 +#define F0900_P2_TUN_PROGDONE 0xf2f6000c +#define F0900_P2_TUN_RFRESTE1 0xf2f60003 + +/*P2_TNRRESTE*/ +#define R0900_P2_TNRRESTE 0xf2f7 +#define F0900_P2_TUN_RFRESTE0 0xf2f700ff + +/*P2_SMAPCOEF7*/ +#define R0900_P2_SMAPCOEF7 0xf300 +#define F0900_P2_DIS_QSCALE 0xf3000080 +#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f + +/*P2_SMAPCOEF6*/ +#define R0900_P2_SMAPCOEF6 0xf301 +#define F0900_P2_ADJ_8PSKLLR1 0xf3010004 +#define F0900_P2_OLD_8PSKLLR1 0xf3010002 +#define F0900_P2_DIS_AB8PSK 0xf3010001 + +/*P2_SMAPCOEF5*/ +#define R0900_P2_SMAPCOEF5 0xf302 +#define F0900_P2_DIS_8SCALE 0xf3020080 +#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f + +/*P2_NCO2MAX1*/ +#define R0900_P2_NCO2MAX1 0xf314 +#define F0900_P2_TETA2_MAXVABS1 0xf31400ff + +/*P2_NCO2MAX0*/ +#define R0900_P2_NCO2MAX0 0xf315 +#define F0900_P2_TETA2_MAXVABS0 0xf31500ff + +/*P2_NCO2FR1*/ +#define R0900_P2_NCO2FR1 0xf316 +#define F0900_P2_NCO2FINAL_ANGLE1 0xf31600ff + +/*P2_NCO2FR0*/ +#define R0900_P2_NCO2FR0 0xf317 +#define F0900_P2_NCO2FINAL_ANGLE0 0xf31700ff + +/*P2_CFR2AVRGE1*/ +#define R0900_P2_CFR2AVRGE1 0xf318 +#define F0900_P2_I2C_CFR2AVERAGE1 0xf31800ff + +/*P2_CFR2AVRGE0*/ +#define R0900_P2_CFR2AVRGE0 0xf319 +#define F0900_P2_I2C_CFR2AVERAGE0 0xf31900ff + +/*P2_DMDPLHSTAT*/ +#define R0900_P2_DMDPLHSTAT 0xf320 +#define F0900_P2_PLH_STATISTIC 0xf32000ff + +/*P2_LOCKTIME3*/ +#define R0900_P2_LOCKTIME3 0xf322 +#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff + +/*P2_LOCKTIME2*/ +#define R0900_P2_LOCKTIME2 0xf323 +#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff + +/*P2_LOCKTIME1*/ +#define R0900_P2_LOCKTIME1 0xf324 +#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff + +/*P2_LOCKTIME0*/ +#define R0900_P2_LOCKTIME0 0xf325 +#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff + +/*P2_VITSCALE*/ +#define R0900_P2_VITSCALE 0xf332 +#define F0900_P2_NVTH_NOSRANGE 0xf3320080 +#define F0900_P2_VERROR_MAXMODE 0xf3320040 +#define F0900_P2_NSLOWSN_LOCKED 0xf3320008 +#define F0900_P2_DIS_RSFLOCK 0xf3320002 + +/*P2_FECM*/ +#define R0900_P2_FECM 0xf333 +#define F0900_P2_DSS_DVB 0xf3330080 +#define F0900_P2_DSS_SRCH 0xf3330010 +#define F0900_P2_SYNCVIT 0xf3330002 +#define F0900_P2_IQINV 0xf3330001 + +/*P2_VTH12*/ +#define R0900_P2_VTH12 0xf334 +#define F0900_P2_VTH12 0xf33400ff + +/*P2_VTH23*/ +#define R0900_P2_VTH23 0xf335 +#define F0900_P2_VTH23 0xf33500ff + +/*P2_VTH34*/ +#define R0900_P2_VTH34 0xf336 +#define F0900_P2_VTH34 0xf33600ff + +/*P2_VTH56*/ +#define R0900_P2_VTH56 0xf337 +#define F0900_P2_VTH56 0xf33700ff + +/*P2_VTH67*/ +#define R0900_P2_VTH67 0xf338 +#define F0900_P2_VTH67 0xf33800ff + +/*P2_VTH78*/ +#define R0900_P2_VTH78 0xf339 +#define F0900_P2_VTH78 0xf33900ff + +/*P2_VITCURPUN*/ +#define R0900_P2_VITCURPUN 0xf33a +#define F0900_P2_VIT_CURPUN 0xf33a001f + +/*P2_VERROR*/ +#define R0900_P2_VERROR 0xf33b +#define F0900_P2_REGERR_VIT 0xf33b00ff + +/*P2_PRVIT*/ +#define R0900_P2_PRVIT 0xf33c +#define F0900_P2_DIS_VTHLOCK 0xf33c0040 +#define F0900_P2_E7_8VIT 0xf33c0020 +#define F0900_P2_E6_7VIT 0xf33c0010 +#define F0900_P2_E5_6VIT 0xf33c0008 +#define F0900_P2_E3_4VIT 0xf33c0004 +#define F0900_P2_E2_3VIT 0xf33c0002 +#define F0900_P2_E1_2VIT 0xf33c0001 + +/*P2_VAVSRVIT*/ +#define R0900_P2_VAVSRVIT 0xf33d +#define F0900_P2_AMVIT 0xf33d0080 +#define F0900_P2_FROZENVIT 0xf33d0040 +#define F0900_P2_SNVIT 0xf33d0030 +#define F0900_P2_TOVVIT 0xf33d000c +#define F0900_P2_HYPVIT 0xf33d0003 + +/*P2_VSTATUSVIT*/ +#define R0900_P2_VSTATUSVIT 0xf33e +#define F0900_P2_PRFVIT 0xf33e0010 +#define F0900_P2_LOCKEDVIT 0xf33e0008 + +/*P2_VTHINUSE*/ +#define R0900_P2_VTHINUSE 0xf33f +#define F0900_P2_VIT_INUSE 0xf33f00ff + +/*P2_KDIV12*/ +#define R0900_P2_KDIV12 0xf340 +#define F0900_P2_K_DIVIDER_12 0xf340007f + +/*P2_KDIV23*/ +#define R0900_P2_KDIV23 0xf341 +#define F0900_P2_K_DIVIDER_23 0xf341007f + +/*P2_KDIV34*/ +#define R0900_P2_KDIV34 0xf342 +#define F0900_P2_K_DIVIDER_34 0xf342007f + +/*P2_KDIV56*/ +#define R0900_P2_KDIV56 0xf343 +#define F0900_P2_K_DIVIDER_56 0xf343007f + +/*P2_KDIV67*/ +#define R0900_P2_KDIV67 0xf344 +#define F0900_P2_K_DIVIDER_67 0xf344007f + +/*P2_KDIV78*/ +#define R0900_P2_KDIV78 0xf345 +#define F0900_P2_K_DIVIDER_78 0xf345007f + +/*P2_PDELCTRL1*/ +#define R0900_P2_PDELCTRL1 0xf350 +#define F0900_P2_INV_MISMASK 0xf3500080 +#define F0900_P2_FILTER_EN 0xf3500020 +#define F0900_P2_EN_MIS00 0xf3500002 +#define F0900_P2_ALGOSWRST 0xf3500001 + +/*P2_PDELCTRL2*/ +#define R0900_P2_PDELCTRL2 0xf351 +#define F0900_P2_RESET_UPKO_COUNT 0xf3510040 +#define F0900_P2_FRAME_MODE 0xf3510002 +#define F0900_P2_NOBCHERRFLG_USE 0xf3510001 + +/*P2_HYSTTHRESH*/ +#define R0900_P2_HYSTTHRESH 0xf354 +#define F0900_P2_UNLCK_THRESH 0xf35400f0 +#define F0900_P2_DELIN_LCK_THRESH 0xf354000f + +/*P2_ISIENTRY*/ +#define R0900_P2_ISIENTRY 0xf35e +#define F0900_P2_ISI_ENTRY 0xf35e00ff + +/*P2_ISIBITENA*/ +#define R0900_P2_ISIBITENA 0xf35f +#define F0900_P2_ISI_BIT_EN 0xf35f00ff + +/*P2_MATSTR1*/ +#define R0900_P2_MATSTR1 0xf360 +#define F0900_P2_MATYPE_CURRENT1 0xf36000ff + +/*P2_MATSTR0*/ +#define R0900_P2_MATSTR0 0xf361 +#define F0900_P2_MATYPE_CURRENT0 0xf36100ff + +/*P2_UPLSTR1*/ +#define R0900_P2_UPLSTR1 0xf362 +#define F0900_P2_UPL_CURRENT1 0xf36200ff + +/*P2_UPLSTR0*/ +#define R0900_P2_UPLSTR0 0xf363 +#define F0900_P2_UPL_CURRENT0 0xf36300ff + +/*P2_DFLSTR1*/ +#define R0900_P2_DFLSTR1 0xf364 +#define F0900_P2_DFL_CURRENT1 0xf36400ff + +/*P2_DFLSTR0*/ +#define R0900_P2_DFLSTR0 0xf365 +#define F0900_P2_DFL_CURRENT0 0xf36500ff + +/*P2_SYNCSTR*/ +#define R0900_P2_SYNCSTR 0xf366 +#define F0900_P2_SYNC_CURRENT 0xf36600ff + +/*P2_SYNCDSTR1*/ +#define R0900_P2_SYNCDSTR1 0xf367 +#define F0900_P2_SYNCD_CURRENT1 0xf36700ff + +/*P2_SYNCDSTR0*/ +#define R0900_P2_SYNCDSTR0 0xf368 +#define F0900_P2_SYNCD_CURRENT0 0xf36800ff + +/*P2_PDELSTATUS1*/ +#define R0900_P2_PDELSTATUS1 0xf369 +#define F0900_P2_PKTDELIN_DELOCK 0xf3690080 +#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040 +#define F0900_P2_CONTINUOUS_STREAM 0xf3690020 +#define F0900_P2_UNACCEPTED_STREAM 0xf3690010 +#define F0900_P2_BCH_ERROR_FLAG 0xf3690008 +#define F0900_P2_PKTDELIN_LOCK 0xf3690002 +#define F0900_P2_FIRST_LOCK 0xf3690001 + +/*P2_PDELSTATUS2*/ +#define R0900_P2_PDELSTATUS2 0xf36a +#define F0900_P2_FRAME_MODCOD 0xf36a007c +#define F0900_P2_FRAME_TYPE 0xf36a0003 + +/*P2_BBFCRCKO1*/ +#define R0900_P2_BBFCRCKO1 0xf36b +#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff + +/*P2_BBFCRCKO0*/ +#define R0900_P2_BBFCRCKO0 0xf36c +#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff + +/*P2_UPCRCKO1*/ +#define R0900_P2_UPCRCKO1 0xf36d +#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff + +/*P2_UPCRCKO0*/ +#define R0900_P2_UPCRCKO0 0xf36e +#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff + +/*P2_PDELCTRL3*/ +#define R0900_P2_PDELCTRL3 0xf36f +#define F0900_P2_PKTDEL_CONTFAIL 0xf36f0080 +#define F0900_P2_NOFIFO_BCHERR 0xf36f0020 + +/*P2_TSSTATEM*/ +#define R0900_P2_TSSTATEM 0xf370 +#define F0900_P2_TSDIL_ON 0xf3700080 +#define F0900_P2_TSRS_ON 0xf3700020 +#define F0900_P2_TSDESCRAMB_ON 0xf3700010 +#define F0900_P2_TSFRAME_MODE 0xf3700008 +#define F0900_P2_TS_DISABLE 0xf3700004 +#define F0900_P2_TSOUT_NOSYNC 0xf3700001 + +/*P2_TSCFGH*/ +#define R0900_P2_TSCFGH 0xf372 +#define F0900_P2_TSFIFO_DVBCI 0xf3720080 +#define F0900_P2_TSFIFO_SERIAL 0xf3720040 +#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020 +#define F0900_P2_TSFIFO_DUTY50 0xf3720010 +#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008 +#define F0900_P2_TSFIFO_ERRMODE 0xf3720006 +#define F0900_P2_RST_HWARE 0xf3720001 + +/*P2_TSCFGM*/ +#define R0900_P2_TSCFGM 0xf373 +#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0 +#define F0900_P2_TSFIFO_PERMDATA 0xf3730020 +#define F0900_P2_TSFIFO_DPUNACT 0xf3730002 +#define F0900_P2_TSFIFO_INVDATA 0xf3730001 + +/*P2_TSCFGL*/ +#define R0900_P2_TSCFGL 0xf374 +#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0 +#define F0900_P2_BCHERROR_MODE 0xf3740030 +#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008 +#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004 +#define F0900_P2_TSFIFO_BITSPEED 0xf3740003 + +/*P2_TSINSDELH*/ +#define R0900_P2_TSINSDELH 0xf376 +#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080 +#define F0900_P2_TSDEL_XXHEADER 0xf3760040 +#define F0900_P2_TSDEL_BBHEADER 0xf3760020 +#define F0900_P2_TSDEL_DATAFIELD 0xf3760010 +#define F0900_P2_TSINSDEL_ISCR 0xf3760008 +#define F0900_P2_TSINSDEL_NPD 0xf3760004 +#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002 +#define F0900_P2_TSINSDEL_CRC8 0xf3760001 + +/*P2_TSDIVN*/ +#define R0900_P2_TSDIVN 0xf379 +#define F0900_P2_TSFIFO_SPEEDMODE 0xf37900c0 + +/*P2_TSCFG4*/ +#define R0900_P2_TSCFG4 0xf37a +#define F0900_P2_TSFIFO_TSSPEEDMODE 0xf37a00c0 + +/*P2_TSSPEED*/ +#define R0900_P2_TSSPEED 0xf380 +#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff + +/*P2_TSSTATUS*/ +#define R0900_P2_TSSTATUS 0xf381 +#define F0900_P2_TSFIFO_LINEOK 0xf3810080 +#define F0900_P2_TSFIFO_ERROR 0xf3810040 +#define F0900_P2_DIL_READY 0xf3810001 + +/*P2_TSSTATUS2*/ +#define R0900_P2_TSSTATUS2 0xf382 +#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080 +#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040 +#define F0900_P2_DILXX_RESET 0xf3820020 +#define F0900_P2_TSSERIAL_IMPOS 0xf3820010 +#define F0900_P2_SCRAMBDETECT 0xf3820002 + +/*P2_TSBITRATE1*/ +#define R0900_P2_TSBITRATE1 0xf383 +#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff + +/*P2_TSBITRATE0*/ +#define R0900_P2_TSBITRATE0 0xf384 +#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff + +/*P2_ERRCTRL1*/ +#define R0900_P2_ERRCTRL1 0xf398 +#define F0900_P2_ERR_SOURCE1 0xf39800f0 +#define F0900_P2_NUM_EVENT1 0xf3980007 + +/*P2_ERRCNT12*/ +#define R0900_P2_ERRCNT12 0xf399 +#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080 +#define F0900_P2_ERR_CNT12 0xf399007f + +/*P2_ERRCNT11*/ +#define R0900_P2_ERRCNT11 0xf39a +#define F0900_P2_ERR_CNT11 0xf39a00ff + +/*P2_ERRCNT10*/ +#define R0900_P2_ERRCNT10 0xf39b +#define F0900_P2_ERR_CNT10 0xf39b00ff + +/*P2_ERRCTRL2*/ +#define R0900_P2_ERRCTRL2 0xf39c +#define F0900_P2_ERR_SOURCE2 0xf39c00f0 +#define F0900_P2_NUM_EVENT2 0xf39c0007 + +/*P2_ERRCNT22*/ +#define R0900_P2_ERRCNT22 0xf39d +#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080 +#define F0900_P2_ERR_CNT22 0xf39d007f + +/*P2_ERRCNT21*/ +#define R0900_P2_ERRCNT21 0xf39e +#define F0900_P2_ERR_CNT21 0xf39e00ff + +/*P2_ERRCNT20*/ +#define R0900_P2_ERRCNT20 0xf39f +#define F0900_P2_ERR_CNT20 0xf39f00ff + +/*P2_FECSPY*/ +#define R0900_P2_FECSPY 0xf3a0 +#define F0900_P2_SPY_ENABLE 0xf3a00080 +#define F0900_P2_NO_SYNCBYTE 0xf3a00040 +#define F0900_P2_SERIAL_MODE 0xf3a00020 +#define F0900_P2_UNUSUAL_PACKET 0xf3a00010 +#define F0900_P2_BERMETER_DATAMODE 0xf3a00008 +#define F0900_P2_BERMETER_LMODE 0xf3a00002 +#define F0900_P2_BERMETER_RESET 0xf3a00001 + +/*P2_FSPYCFG*/ +#define R0900_P2_FSPYCFG 0xf3a1 +#define F0900_P2_FECSPY_INPUT 0xf3a100c0 +#define F0900_P2_RST_ON_ERROR 0xf3a10020 +#define F0900_P2_ONE_SHOT 0xf3a10010 +#define F0900_P2_I2C_MODE 0xf3a1000c +#define F0900_P2_SPY_HYSTERESIS 0xf3a10003 + +/*P2_FSPYDATA*/ +#define R0900_P2_FSPYDATA 0xf3a2 +#define F0900_P2_SPY_STUFFING 0xf3a20080 +#define F0900_P2_SPY_CNULLPKT 0xf3a20020 +#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f + +/*P2_FSPYOUT*/ +#define R0900_P2_FSPYOUT 0xf3a3 +#define F0900_P2_FSPY_DIRECT 0xf3a30080 +#define F0900_P2_STUFF_MODE 0xf3a30007 + +/*P2_FSTATUS*/ +#define R0900_P2_FSTATUS 0xf3a4 +#define F0900_P2_SPY_ENDSIM 0xf3a40080 +#define F0900_P2_VALID_SIM 0xf3a40040 +#define F0900_P2_FOUND_SIGNAL 0xf3a40020 +#define F0900_P2_DSS_SYNCBYTE 0xf3a40010 +#define F0900_P2_RESULT_STATE 0xf3a4000f + +/*P2_FBERCPT4*/ +#define R0900_P2_FBERCPT4 0xf3a8 +#define F0900_P2_FBERMETER_CPT4 0xf3a800ff + +/*P2_FBERCPT3*/ +#define R0900_P2_FBERCPT3 0xf3a9 +#define F0900_P2_FBERMETER_CPT3 0xf3a900ff + +/*P2_FBERCPT2*/ +#define R0900_P2_FBERCPT2 0xf3aa +#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff + +/*P2_FBERCPT1*/ +#define R0900_P2_FBERCPT1 0xf3ab +#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff + +/*P2_FBERCPT0*/ +#define R0900_P2_FBERCPT0 0xf3ac +#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff + +/*P2_FBERERR2*/ +#define R0900_P2_FBERERR2 0xf3ad +#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff + +/*P2_FBERERR1*/ +#define R0900_P2_FBERERR1 0xf3ae +#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff + +/*P2_FBERERR0*/ +#define R0900_P2_FBERERR0 0xf3af +#define F0900_P2_FBERMETER_ERR0 0xf3af00ff + +/*P2_FSPYBER*/ +#define R0900_P2_FSPYBER 0xf3b2 +#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010 +#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008 +#define F0900_P2_FSPYBER_CTIME 0xf3b20007 + +/*P1_IQCONST*/ +#define R0900_P1_IQCONST 0xf400 +#define IQCONST REGx(R0900_P1_IQCONST) +#define F0900_P1_CONSTEL_SELECT 0xf4000060 +#define F0900_P1_IQSYMB_SEL 0xf400001f + +/*P1_NOSCFG*/ +#define R0900_P1_NOSCFG 0xf401 +#define NOSCFG REGx(R0900_P1_NOSCFG) +#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020 +#define F0900_P1_NOSPLH_BETA 0xf4010018 +#define F0900_P1_NOSDATA_BETA 0xf4010007 + +/*P1_ISYMB*/ +#define R0900_P1_ISYMB 0xf402 +#define ISYMB REGx(R0900_P1_ISYMB) +#define F0900_P1_I_SYMBOL 0xf40201ff + +/*P1_QSYMB*/ +#define R0900_P1_QSYMB 0xf403 +#define QSYMB REGx(R0900_P1_QSYMB) +#define F0900_P1_Q_SYMBOL 0xf40301ff + +/*P1_AGC1CFG*/ +#define R0900_P1_AGC1CFG 0xf404 +#define AGC1CFG REGx(R0900_P1_AGC1CFG) +#define F0900_P1_DC_FROZEN 0xf4040080 +#define F0900_P1_DC_CORRECT 0xf4040040 +#define F0900_P1_AMM_FROZEN 0xf4040020 +#define F0900_P1_AMM_CORRECT 0xf4040010 +#define F0900_P1_QUAD_FROZEN 0xf4040008 +#define F0900_P1_QUAD_CORRECT 0xf4040004 + +/*P1_AGC1CN*/ +#define R0900_P1_AGC1CN 0xf406 +#define AGC1CN REGx(R0900_P1_AGC1CN) +#define F0900_P1_AGC1_LOCKED 0xf4060080 +#define F0900_P1_AGC1_MINPOWER 0xf4060010 +#define F0900_P1_AGCOUT_FAST 0xf4060008 +#define F0900_P1_AGCIQ_BETA 0xf4060007 + +/*P1_AGC1REF*/ +#define R0900_P1_AGC1REF 0xf407 +#define AGC1REF REGx(R0900_P1_AGC1REF) +#define F0900_P1_AGCIQ_REF 0xf40700ff + +/*P1_IDCCOMP*/ +#define R0900_P1_IDCCOMP 0xf408 +#define IDCCOMP REGx(R0900_P1_IDCCOMP) +#define F0900_P1_IAVERAGE_ADJ 0xf40801ff + +/*P1_QDCCOMP*/ +#define R0900_P1_QDCCOMP 0xf409 +#define QDCCOMP REGx(R0900_P1_QDCCOMP) +#define F0900_P1_QAVERAGE_ADJ 0xf40901ff + +/*P1_POWERI*/ +#define R0900_P1_POWERI 0xf40a +#define POWERI REGx(R0900_P1_POWERI) +#define F0900_P1_POWER_I 0xf40a00ff +#define POWER_I FLDx(F0900_P1_POWER_I) + +/*P1_POWERQ*/ +#define R0900_P1_POWERQ 0xf40b +#define POWERQ REGx(R0900_P1_POWERQ) +#define F0900_P1_POWER_Q 0xf40b00ff +#define POWER_Q FLDx(F0900_P1_POWER_Q) + +/*P1_AGC1AMM*/ +#define R0900_P1_AGC1AMM 0xf40c +#define AGC1AMM REGx(R0900_P1_AGC1AMM) +#define F0900_P1_AMM_VALUE 0xf40c00ff + +/*P1_AGC1QUAD*/ +#define R0900_P1_AGC1QUAD 0xf40d +#define AGC1QUAD REGx(R0900_P1_AGC1QUAD) +#define F0900_P1_QUAD_VALUE 0xf40d01ff + +/*P1_AGCIQIN1*/ +#define R0900_P1_AGCIQIN1 0xf40e +#define AGCIQIN1 REGx(R0900_P1_AGCIQIN1) +#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff +#define AGCIQ_VALUE1 FLDx(F0900_P1_AGCIQ_VALUE1) + +/*P1_AGCIQIN0*/ +#define R0900_P1_AGCIQIN0 0xf40f +#define AGCIQIN0 REGx(R0900_P1_AGCIQIN0) +#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff +#define AGCIQ_VALUE0 FLDx(F0900_P1_AGCIQ_VALUE0) + +/*P1_DEMOD*/ +#define R0900_P1_DEMOD 0xf410 +#define DEMOD REGx(R0900_P1_DEMOD) +#define F0900_P1_MANUALS2_ROLLOFF 0xf4100080 +#define MANUALS2_ROLLOFF FLDx(F0900_P1_MANUALS2_ROLLOFF) + +#define F0900_P1_SPECINV_CONTROL 0xf4100030 +#define SPECINV_CONTROL FLDx(F0900_P1_SPECINV_CONTROL) +#define F0900_P1_FORCE_ENASAMP 0xf4100008 +#define F0900_P1_MANUALSX_ROLLOFF 0xf4100004 +#define MANUALSX_ROLLOFF FLDx(F0900_P1_MANUALSX_ROLLOFF) +#define F0900_P1_ROLLOFF_CONTROL 0xf4100003 +#define ROLLOFF_CONTROL FLDx(F0900_P1_ROLLOFF_CONTROL) + +/*P1_DMDMODCOD*/ +#define R0900_P1_DMDMODCOD 0xf411 +#define DMDMODCOD REGx(R0900_P1_DMDMODCOD) +#define F0900_P1_MANUAL_MODCOD 0xf4110080 +#define F0900_P1_DEMOD_MODCOD 0xf411007c +#define DEMOD_MODCOD FLDx(F0900_P1_DEMOD_MODCOD) +#define F0900_P1_DEMOD_TYPE 0xf4110003 +#define DEMOD_TYPE FLDx(F0900_P1_DEMOD_TYPE) + +/*P1_DSTATUS*/ +#define R0900_P1_DSTATUS 0xf412 +#define DSTATUS REGx(R0900_P1_DSTATUS) +#define F0900_P1_CAR_LOCK 0xf4120080 +#define F0900_P1_TMGLOCK_QUALITY 0xf4120060 +#define TMGLOCK_QUALITY FLDx(F0900_P1_TMGLOCK_QUALITY) +#define F0900_P1_LOCK_DEFINITIF 0xf4120008 +#define LOCK_DEFINITIF FLDx(F0900_P1_LOCK_DEFINITIF) +#define F0900_P1_OVADC_DETECT 0xf4120001 + +/*P1_DSTATUS2*/ +#define R0900_P1_DSTATUS2 0xf413 +#define DSTATUS2 REGx(R0900_P1_DSTATUS2) +#define F0900_P1_DEMOD_DELOCK 0xf4130080 +#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008 +#define F0900_P1_AGC2_OVERFLOW 0xf4130004 +#define F0900_P1_CFR_OVERFLOW 0xf4130002 +#define F0900_P1_GAMMA_OVERUNDER 0xf4130001 + +/*P1_DMDCFGMD*/ +#define R0900_P1_DMDCFGMD 0xf414 +#define DMDCFGMD REGx(R0900_P1_DMDCFGMD) +#define F0900_P1_DVBS2_ENABLE 0xf4140080 +#define DVBS2_ENABLE FLDx(F0900_P1_DVBS2_ENABLE) +#define F0900_P1_DVBS1_ENABLE 0xf4140040 +#define DVBS1_ENABLE FLDx(F0900_P1_DVBS1_ENABLE) +#define F0900_P1_SCAN_ENABLE 0xf4140010 +#define SCAN_ENABLE FLDx(F0900_P1_SCAN_ENABLE) +#define F0900_P1_CFR_AUTOSCAN 0xf4140008 +#define CFR_AUTOSCAN FLDx(F0900_P1_CFR_AUTOSCAN) +#define F0900_P1_TUN_RNG 0xf4140003 + +/*P1_DMDCFG2*/ +#define R0900_P1_DMDCFG2 0xf415 +#define DMDCFG2 REGx(R0900_P1_DMDCFG2) +#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040 +#define S1S2_SEQUENTIAL FLDx(F0900_P1_S1S2_SEQUENTIAL) +#define F0900_P1_INFINITE_RELOCK 0xf4150010 + +/*P1_DMDISTATE*/ +#define R0900_P1_DMDISTATE 0xf416 +#define DMDISTATE REGx(R0900_P1_DMDISTATE) +#define F0900_P1_I2C_DEMOD_MODE 0xf416001f +#define DEMOD_MODE FLDx(F0900_P1_I2C_DEMOD_MODE) + +/*P1_DMDT0M*/ +#define R0900_P1_DMDT0M 0xf417 +#define DMDT0M REGx(R0900_P1_DMDT0M) +#define F0900_P1_DMDT0_MIN 0xf41700ff + +/*P1_DMDSTATE*/ +#define R0900_P1_DMDSTATE 0xf41b +#define DMDSTATE REGx(R0900_P1_DMDSTATE) +#define F0900_P1_HEADER_MODE 0xf41b0060 +#define HEADER_MODE FLDx(F0900_P1_HEADER_MODE) + +/*P1_DMDFLYW*/ +#define R0900_P1_DMDFLYW 0xf41c +#define DMDFLYW REGx(R0900_P1_DMDFLYW) +#define F0900_P1_I2C_IRQVAL 0xf41c00f0 +#define F0900_P1_FLYWHEEL_CPT 0xf41c000f +#define FLYWHEEL_CPT FLDx(F0900_P1_FLYWHEEL_CPT) + +/*P1_DSTATUS3*/ +#define R0900_P1_DSTATUS3 0xf41d +#define DSTATUS3 REGx(R0900_P1_DSTATUS3) +#define F0900_P1_DEMOD_CFGMODE 0xf41d0060 + +/*P1_DMDCFG3*/ +#define R0900_P1_DMDCFG3 0xf41e +#define DMDCFG3 REGx(R0900_P1_DMDCFG3) +#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008 + +/*P1_DMDCFG4*/ +#define R0900_P1_DMDCFG4 0xf41f +#define DMDCFG4 REGx(R0900_P1_DMDCFG4) +#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008 + +/*P1_CORRELMANT*/ +#define R0900_P1_CORRELMANT 0xf420 +#define CORRELMANT REGx(R0900_P1_CORRELMANT) +#define F0900_P1_CORREL_MANT 0xf42000ff + +/*P1_CORRELABS*/ +#define R0900_P1_CORRELABS 0xf421 +#define CORRELABS REGx(R0900_P1_CORRELABS) +#define F0900_P1_CORREL_ABS 0xf42100ff + +/*P1_CORRELEXP*/ +#define R0900_P1_CORRELEXP 0xf422 +#define CORRELEXP REGx(R0900_P1_CORRELEXP) +#define F0900_P1_CORREL_ABSEXP 0xf42200f0 +#define F0900_P1_CORREL_EXP 0xf422000f + +/*P1_PLHMODCOD*/ +#define R0900_P1_PLHMODCOD 0xf424 +#define PLHMODCOD REGx(R0900_P1_PLHMODCOD) +#define F0900_P1_SPECINV_DEMOD 0xf4240080 +#define SPECINV_DEMOD FLDx(F0900_P1_SPECINV_DEMOD) +#define F0900_P1_PLH_MODCOD 0xf424007c +#define F0900_P1_PLH_TYPE 0xf4240003 + +/*P1_DMDREG*/ +#define R0900_P1_DMDREG 0xf425 +#define DMDREG REGx(R0900_P1_DMDREG) +#define F0900_P1_DECIM_PLFRAMES 0xf4250001 + +/*P1_AGC2O*/ +#define R0900_P1_AGC2O 0xf42c +#define AGC2O REGx(R0900_P1_AGC2O) +#define F0900_P1_AGC2_COEF 0xf42c0007 + +/*P1_AGC2REF*/ +#define R0900_P1_AGC2REF 0xf42d +#define AGC2REF REGx(R0900_P1_AGC2REF) +#define F0900_P1_AGC2_REF 0xf42d00ff + +/*P1_AGC1ADJ*/ +#define R0900_P1_AGC1ADJ 0xf42e +#define AGC1ADJ REGx(R0900_P1_AGC1ADJ) +#define F0900_P1_AGC1_ADJUSTED 0xf42e007f + +/*P1_AGC2I1*/ +#define R0900_P1_AGC2I1 0xf436 +#define AGC2I1 REGx(R0900_P1_AGC2I1) +#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff + +/*P1_AGC2I0*/ +#define R0900_P1_AGC2I0 0xf437 +#define AGC2I0 REGx(R0900_P1_AGC2I0) +#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff + +/*P1_CARCFG*/ +#define R0900_P1_CARCFG 0xf438 +#define CARCFG REGx(R0900_P1_CARCFG) +#define F0900_P1_CFRUPLOW_AUTO 0xf4380080 +#define F0900_P1_CFRUPLOW_TEST 0xf4380040 +#define F0900_P1_ROTAON 0xf4380004 +#define F0900_P1_PH_DET_ALGO 0xf4380003 + +/*P1_ACLC*/ +#define R0900_P1_ACLC 0xf439 +#define ACLC REGx(R0900_P1_ACLC) +#define F0900_P1_CAR_ALPHA_MANT 0xf4390030 +#define F0900_P1_CAR_ALPHA_EXP 0xf439000f + +/*P1_BCLC*/ +#define R0900_P1_BCLC 0xf43a +#define BCLC REGx(R0900_P1_BCLC) +#define F0900_P1_CAR_BETA_MANT 0xf43a0030 +#define F0900_P1_CAR_BETA_EXP 0xf43a000f + +/*P1_CARFREQ*/ +#define R0900_P1_CARFREQ 0xf43d +#define CARFREQ REGx(R0900_P1_CARFREQ) +#define F0900_P1_KC_COARSE_EXP 0xf43d00f0 +#define F0900_P1_BETA_FREQ 0xf43d000f + +/*P1_CARHDR*/ +#define R0900_P1_CARHDR 0xf43e +#define CARHDR REGx(R0900_P1_CARHDR) +#define F0900_P1_K_FREQ_HDR 0xf43e00ff + +/*P1_LDT*/ +#define R0900_P1_LDT 0xf43f +#define LDT REGx(R0900_P1_LDT) +#define F0900_P1_CARLOCK_THRES 0xf43f01ff + +/*P1_LDT2*/ +#define R0900_P1_LDT2 0xf440 +#define LDT2 REGx(R0900_P1_LDT2) +#define F0900_P1_CARLOCK_THRES2 0xf44001ff + +/*P1_CFRICFG*/ +#define R0900_P1_CFRICFG 0xf441 +#define CFRICFG REGx(R0900_P1_CFRICFG) +#define F0900_P1_NEG_CFRSTEP 0xf4410001 + +/*P1_CFRUP1*/ +#define R0900_P1_CFRUP1 0xf442 +#define CFRUP1 REGx(R0900_P1_CFRUP1) +#define F0900_P1_CFR_UP1 0xf44201ff +#define CFR_UP1 FLDx(F0900_P1_CFR_UP1) + +/*P1_CFRUP0*/ +#define R0900_P1_CFRUP0 0xf443 +#define CFRUP0 REGx(R0900_P1_CFRUP0) +#define F0900_P1_CFR_UP0 0xf44300ff +#define CFR_UP0 FLDx(F0900_P1_CFR_UP0) + +/*P1_CFRLOW1*/ +#define R0900_P1_CFRLOW1 0xf446 +#define CFRLOW1 REGx(R0900_P1_CFRLOW1) +#define F0900_P1_CFR_LOW1 0xf44601ff +#define CFR_LOW1 FLDx(F0900_P1_CFR_LOW1) + +/*P1_CFRLOW0*/ +#define R0900_P1_CFRLOW0 0xf447 +#define CFRLOW0 REGx(R0900_P1_CFRLOW0) +#define F0900_P1_CFR_LOW0 0xf44700ff +#define CFR_LOW0 FLDx(F0900_P1_CFR_LOW0) + +/*P1_CFRINIT1*/ +#define R0900_P1_CFRINIT1 0xf448 +#define CFRINIT1 REGx(R0900_P1_CFRINIT1) +#define F0900_P1_CFR_INIT1 0xf44801ff +#define CFR_INIT1 FLDx(F0900_P1_CFR_INIT1) + +/*P1_CFRINIT0*/ +#define R0900_P1_CFRINIT0 0xf449 +#define CFRINIT0 REGx(R0900_P1_CFRINIT0) +#define F0900_P1_CFR_INIT0 0xf44900ff +#define CFR_INIT0 FLDx(F0900_P1_CFR_INIT0) + +/*P1_CFRINC1*/ +#define R0900_P1_CFRINC1 0xf44a +#define CFRINC1 REGx(R0900_P1_CFRINC1) +#define F0900_P1_MANUAL_CFRINC 0xf44a0080 +#define F0900_P1_CFR_INC1 0xf44a003f + +/*P1_CFRINC0*/ +#define R0900_P1_CFRINC0 0xf44b +#define CFRINC0 REGx(R0900_P1_CFRINC0) +#define F0900_P1_CFR_INC0 0xf44b00f8 + +/*P1_CFR2*/ +#define R0900_P1_CFR2 0xf44c +#define CFR2 REGx(R0900_P1_CFR2) +#define F0900_P1_CAR_FREQ2 0xf44c01ff +#define CAR_FREQ2 FLDx(F0900_P1_CAR_FREQ2) + +/*P1_CFR1*/ +#define R0900_P1_CFR1 0xf44d +#define CFR1 REGx(R0900_P1_CFR1) +#define F0900_P1_CAR_FREQ1 0xf44d00ff +#define CAR_FREQ1 FLDx(F0900_P1_CAR_FREQ1) + +/*P1_CFR0*/ +#define R0900_P1_CFR0 0xf44e +#define CFR0 REGx(R0900_P1_CFR0) +#define F0900_P1_CAR_FREQ0 0xf44e00ff +#define CAR_FREQ0 FLDx(F0900_P1_CAR_FREQ0) + +/*P1_LDI*/ +#define R0900_P1_LDI 0xf44f +#define LDI REGx(R0900_P1_LDI) +#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff + +/*P1_TMGCFG*/ +#define R0900_P1_TMGCFG 0xf450 +#define TMGCFG REGx(R0900_P1_TMGCFG) +#define F0900_P1_TMGLOCK_BETA 0xf45000c0 +#define F0900_P1_DO_TIMING_CORR 0xf4500010 +#define F0900_P1_TMG_MINFREQ 0xf4500003 + +/*P1_RTC*/ +#define R0900_P1_RTC 0xf451 +#define RTC REGx(R0900_P1_RTC) +#define F0900_P1_TMGALPHA_EXP 0xf45100f0 +#define F0900_P1_TMGBETA_EXP 0xf451000f + +/*P1_RTCS2*/ +#define R0900_P1_RTCS2 0xf452 +#define RTCS2 REGx(R0900_P1_RTCS2) +#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0 +#define F0900_P1_TMGBETAS2_EXP 0xf452000f + +/*P1_TMGTHRISE*/ +#define R0900_P1_TMGTHRISE 0xf453 +#define TMGTHRISE REGx(R0900_P1_TMGTHRISE) +#define F0900_P1_TMGLOCK_THRISE 0xf45300ff + +/*P1_TMGTHFALL*/ +#define R0900_P1_TMGTHFALL 0xf454 +#define TMGTHFALL REGx(R0900_P1_TMGTHFALL) +#define F0900_P1_TMGLOCK_THFALL 0xf45400ff + +/*P1_SFRUPRATIO*/ +#define R0900_P1_SFRUPRATIO 0xf455 +#define SFRUPRATIO REGx(R0900_P1_SFRUPRATIO) +#define F0900_P1_SFR_UPRATIO 0xf45500ff + +/*P1_SFRLOWRATIO*/ +#define R0900_P1_SFRLOWRATIO 0xf456 +#define F0900_P1_SFR_LOWRATIO 0xf45600ff + +/*P1_KREFTMG*/ +#define R0900_P1_KREFTMG 0xf458 +#define KREFTMG REGx(R0900_P1_KREFTMG) +#define F0900_P1_KREF_TMG 0xf45800ff + +/*P1_SFRSTEP*/ +#define R0900_P1_SFRSTEP 0xf459 +#define SFRSTEP REGx(R0900_P1_SFRSTEP) +#define F0900_P1_SFR_SCANSTEP 0xf45900f0 +#define F0900_P1_SFR_CENTERSTEP 0xf459000f + +/*P1_TMGCFG2*/ +#define R0900_P1_TMGCFG2 0xf45a +#define TMGCFG2 REGx(R0900_P1_TMGCFG2) +#define F0900_P1_SFRRATIO_FINE 0xf45a0001 + +/*P1_KREFTMG2*/ +#define R0900_P1_KREFTMG2 0xf45b +#define KREFTMG2 REGx(R0900_P1_KREFTMG2) +#define F0900_P1_KREF_TMG2 0xf45b00ff + +/*P1_SFRINIT1*/ +#define R0900_P1_SFRINIT1 0xf45e +#define SFRINIT1 REGx(R0900_P1_SFRINIT1) +#define F0900_P1_SFR_INIT1 0xf45e007f + +/*P1_SFRINIT0*/ +#define R0900_P1_SFRINIT0 0xf45f +#define SFRINIT0 REGx(R0900_P1_SFRINIT0) +#define F0900_P1_SFR_INIT0 0xf45f00ff + +/*P1_SFRUP1*/ +#define R0900_P1_SFRUP1 0xf460 +#define SFRUP1 REGx(R0900_P1_SFRUP1) +#define F0900_P1_AUTO_GUP 0xf4600080 +#define AUTO_GUP FLDx(F0900_P1_AUTO_GUP) +#define F0900_P1_SYMB_FREQ_UP1 0xf460007f + +/*P1_SFRUP0*/ +#define R0900_P1_SFRUP0 0xf461 +#define SFRUP0 REGx(R0900_P1_SFRUP0) +#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff + +/*P1_SFRLOW1*/ +#define R0900_P1_SFRLOW1 0xf462 +#define SFRLOW1 REGx(R0900_P1_SFRLOW1) +#define F0900_P1_AUTO_GLOW 0xf4620080 +#define AUTO_GLOW FLDx(F0900_P1_AUTO_GLOW) +#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f + +/*P1_SFRLOW0*/ +#define R0900_P1_SFRLOW0 0xf463 +#define SFRLOW0 REGx(R0900_P1_SFRLOW0) +#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff + +/*P1_SFR3*/ +#define R0900_P1_SFR3 0xf464 +#define SFR3 REGx(R0900_P1_SFR3) +#define F0900_P1_SYMB_FREQ3 0xf46400ff +#define SYMB_FREQ3 FLDx(F0900_P1_SYMB_FREQ3) + +/*P1_SFR2*/ +#define R0900_P1_SFR2 0xf465 +#define SFR2 REGx(R0900_P1_SFR2) +#define F0900_P1_SYMB_FREQ2 0xf46500ff +#define SYMB_FREQ2 FLDx(F0900_P1_SYMB_FREQ2) + +/*P1_SFR1*/ +#define R0900_P1_SFR1 0xf466 +#define SFR1 REGx(R0900_P1_SFR1) +#define F0900_P1_SYMB_FREQ1 0xf46600ff +#define SYMB_FREQ1 FLDx(F0900_P1_SYMB_FREQ1) + +/*P1_SFR0*/ +#define R0900_P1_SFR0 0xf467 +#define SFR0 REGx(R0900_P1_SFR0) +#define F0900_P1_SYMB_FREQ0 0xf46700ff +#define SYMB_FREQ0 FLDx(F0900_P1_SYMB_FREQ0) + +/*P1_TMGREG2*/ +#define R0900_P1_TMGREG2 0xf468 +#define TMGREG2 REGx(R0900_P1_TMGREG2) +#define F0900_P1_TMGREG2 0xf46800ff + +/*P1_TMGREG1*/ +#define R0900_P1_TMGREG1 0xf469 +#define TMGREG1 REGx(R0900_P1_TMGREG1) +#define F0900_P1_TMGREG1 0xf46900ff + +/*P1_TMGREG0*/ +#define R0900_P1_TMGREG0 0xf46a +#define TMGREG0 REGx(R0900_P1_TMGREG0) +#define F0900_P1_TMGREG0 0xf46a00ff + +/*P1_TMGLOCK1*/ +#define R0900_P1_TMGLOCK1 0xf46b +#define TMGLOCK1 REGx(R0900_P1_TMGLOCK1) +#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff + +/*P1_TMGLOCK0*/ +#define R0900_P1_TMGLOCK0 0xf46c +#define TMGLOCK0 REGx(R0900_P1_TMGLOCK0) +#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff + +/*P1_TMGOBS*/ +#define R0900_P1_TMGOBS 0xf46d +#define TMGOBS REGx(R0900_P1_TMGOBS) +#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0 +#define ROLLOFF_STATUS FLDx(F0900_P1_ROLLOFF_STATUS) + +/*P1_EQUALCFG*/ +#define R0900_P1_EQUALCFG 0xf46f +#define EQUALCFG REGx(R0900_P1_EQUALCFG) +#define F0900_P1_EQUAL_ON 0xf46f0040 +#define F0900_P1_MU_EQUALDFE 0xf46f0007 + +/*P1_EQUAI1*/ +#define R0900_P1_EQUAI1 0xf470 +#define EQUAI1 REGx(R0900_P1_EQUAI1) +#define F0900_P1_EQUA_ACCI1 0xf47001ff + +/*P1_EQUAQ1*/ +#define R0900_P1_EQUAQ1 0xf471 +#define EQUAQ1 REGx(R0900_P1_EQUAQ1) +#define F0900_P1_EQUA_ACCQ1 0xf47101ff + +/*P1_EQUAI2*/ +#define R0900_P1_EQUAI2 0xf472 +#define EQUAI2 REGx(R0900_P1_EQUAI2) +#define F0900_P1_EQUA_ACCI2 0xf47201ff + +/*P1_EQUAQ2*/ +#define R0900_P1_EQUAQ2 0xf473 +#define EQUAQ2 REGx(R0900_P1_EQUAQ2) +#define F0900_P1_EQUA_ACCQ2 0xf47301ff + +/*P1_EQUAI3*/ +#define R0900_P1_EQUAI3 0xf474 +#define EQUAI3 REGx(R0900_P1_EQUAI3) +#define F0900_P1_EQUA_ACCI3 0xf47401ff + +/*P1_EQUAQ3*/ +#define R0900_P1_EQUAQ3 0xf475 +#define EQUAQ3 REGx(R0900_P1_EQUAQ3) +#define F0900_P1_EQUA_ACCQ3 0xf47501ff + +/*P1_EQUAI4*/ +#define R0900_P1_EQUAI4 0xf476 +#define EQUAI4 REGx(R0900_P1_EQUAI4) +#define F0900_P1_EQUA_ACCI4 0xf47601ff + +/*P1_EQUAQ4*/ +#define R0900_P1_EQUAQ4 0xf477 +#define EQUAQ4 REGx(R0900_P1_EQUAQ4) +#define F0900_P1_EQUA_ACCQ4 0xf47701ff + +/*P1_EQUAI5*/ +#define R0900_P1_EQUAI5 0xf478 +#define EQUAI5 REGx(R0900_P1_EQUAI5) +#define F0900_P1_EQUA_ACCI5 0xf47801ff + +/*P1_EQUAQ5*/ +#define R0900_P1_EQUAQ5 0xf479 +#define EQUAQ5 REGx(R0900_P1_EQUAQ5) +#define F0900_P1_EQUA_ACCQ5 0xf47901ff + +/*P1_EQUAI6*/ +#define R0900_P1_EQUAI6 0xf47a +#define EQUAI6 REGx(R0900_P1_EQUAI6) +#define F0900_P1_EQUA_ACCI6 0xf47a01ff + +/*P1_EQUAQ6*/ +#define R0900_P1_EQUAQ6 0xf47b +#define EQUAQ6 REGx(R0900_P1_EQUAQ6) +#define F0900_P1_EQUA_ACCQ6 0xf47b01ff + +/*P1_EQUAI7*/ +#define R0900_P1_EQUAI7 0xf47c +#define EQUAI7 REGx(R0900_P1_EQUAI7) +#define F0900_P1_EQUA_ACCI7 0xf47c01ff + +/*P1_EQUAQ7*/ +#define R0900_P1_EQUAQ7 0xf47d +#define EQUAQ7 REGx(R0900_P1_EQUAQ7) +#define F0900_P1_EQUA_ACCQ7 0xf47d01ff + +/*P1_EQUAI8*/ +#define R0900_P1_EQUAI8 0xf47e +#define EQUAI8 REGx(R0900_P1_EQUAI8) +#define F0900_P1_EQUA_ACCI8 0xf47e01ff + +/*P1_EQUAQ8*/ +#define R0900_P1_EQUAQ8 0xf47f +#define EQUAQ8 REGx(R0900_P1_EQUAQ8) +#define F0900_P1_EQUA_ACCQ8 0xf47f01ff + +/*P1_NNOSDATAT1*/ +#define R0900_P1_NNOSDATAT1 0xf480 +#define NNOSDATAT1 REGx(R0900_P1_NNOSDATAT1) +#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff +#define NOSDATAT_NORMED1 FLDx(F0900_P1_NOSDATAT_NORMED1) + +/*P1_NNOSDATAT0*/ +#define R0900_P1_NNOSDATAT0 0xf481 +#define NNOSDATAT0 REGx(R0900_P1_NNOSDATAT0) +#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff +#define NOSDATAT_NORMED0 FLDx(F0900_P1_NOSDATAT_NORMED0) + +/*P1_NNOSDATA1*/ +#define R0900_P1_NNOSDATA1 0xf482 +#define NNOSDATA1 REGx(R0900_P1_NNOSDATA1) +#define F0900_P1_NOSDATA_NORMED1 0xf48200ff + +/*P1_NNOSDATA0*/ +#define R0900_P1_NNOSDATA0 0xf483 +#define NNOSDATA0 REGx(R0900_P1_NNOSDATA0) +#define F0900_P1_NOSDATA_NORMED0 0xf48300ff + +/*P1_NNOSPLHT1*/ +#define R0900_P1_NNOSPLHT1 0xf484 +#define NNOSPLHT1 REGx(R0900_P1_NNOSPLHT1) +#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff +#define NOSPLHT_NORMED1 FLDx(F0900_P1_NOSPLHT_NORMED1) + +/*P1_NNOSPLHT0*/ +#define R0900_P1_NNOSPLHT0 0xf485 +#define NNOSPLHT0 REGx(R0900_P1_NNOSPLHT0) +#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff +#define NOSPLHT_NORMED0 FLDx(F0900_P1_NOSPLHT_NORMED0) + +/*P1_NNOSPLH1*/ +#define R0900_P1_NNOSPLH1 0xf486 +#define NNOSPLH1 REGx(R0900_P1_NNOSPLH1) +#define F0900_P1_NOSPLH_NORMED1 0xf48600ff + +/*P1_NNOSPLH0*/ +#define R0900_P1_NNOSPLH0 0xf487 +#define NNOSPLH0 REGx(R0900_P1_NNOSPLH0) +#define F0900_P1_NOSPLH_NORMED0 0xf48700ff + +/*P1_NOSDATAT1*/ +#define R0900_P1_NOSDATAT1 0xf488 +#define NOSDATAT1 REGx(R0900_P1_NOSDATAT1) +#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff + +/*P1_NOSDATAT0*/ +#define R0900_P1_NOSDATAT0 0xf489 +#define NOSDATAT0 REGx(R0900_P1_NOSDATAT0) +#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff + +/*P1_NOSDATA1*/ +#define R0900_P1_NOSDATA1 0xf48a +#define NOSDATA1 REGx(R0900_P1_NOSDATA1) +#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff + +/*P1_NOSDATA0*/ +#define R0900_P1_NOSDATA0 0xf48b +#define NOSDATA0 REGx(R0900_P1_NOSDATA0) +#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff + +/*P1_NOSPLHT1*/ +#define R0900_P1_NOSPLHT1 0xf48c +#define NOSPLHT1 REGx(R0900_P1_NOSPLHT1) +#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff + +/*P1_NOSPLHT0*/ +#define R0900_P1_NOSPLHT0 0xf48d +#define NOSPLHT0 REGx(R0900_P1_NOSPLHT0) +#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff + +/*P1_NOSPLH1*/ +#define R0900_P1_NOSPLH1 0xf48e +#define NOSPLH1 REGx(R0900_P1_NOSPLH1) +#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff + +/*P1_NOSPLH0*/ +#define R0900_P1_NOSPLH0 0xf48f +#define NOSPLH0 REGx(R0900_P1_NOSPLH0) +#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff + +/*P1_CAR2CFG*/ +#define R0900_P1_CAR2CFG 0xf490 +#define CAR2CFG REGx(R0900_P1_CAR2CFG) +#define F0900_P1_CARRIER3_DISABLE 0xf4900040 +#define F0900_P1_ROTA2ON 0xf4900004 +#define F0900_P1_PH_DET_ALGO2 0xf4900003 + +/*P1_CFR2CFR1*/ +#define R0900_P1_CFR2CFR1 0xf491 +#define CFR2CFR1 REGx(R0900_P1_CFR2CFR1) +#define F0900_P1_CFR2TOCFR1_DVBS1 0xf49100c0 +#define F0900_P1_EN_S2CAR2CENTER 0xf4910020 +#define F0900_P1_DIS_BCHERRCFR2 0xf4910010 +#define F0900_P1_CFR2TOCFR1_BETA 0xf4910007 + +/*P1_CFR22*/ +#define R0900_P1_CFR22 0xf493 +#define CFR22 REGx(R0900_P1_CFR22) +#define F0900_P1_CAR2_FREQ2 0xf49301ff + +/*P1_CFR21*/ +#define R0900_P1_CFR21 0xf494 +#define CFR21 REGx(R0900_P1_CFR21) +#define F0900_P1_CAR2_FREQ1 0xf49400ff + +/*P1_CFR20*/ +#define R0900_P1_CFR20 0xf495 +#define CFR20 REGx(R0900_P1_CFR20) +#define F0900_P1_CAR2_FREQ0 0xf49500ff + +/*P1_ACLC2S2Q*/ +#define R0900_P1_ACLC2S2Q 0xf497 +#define ACLC2S2Q REGx(R0900_P1_ACLC2S2Q) +#define F0900_P1_ENAB_SPSKSYMB 0xf4970080 +#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030 +#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f + +/*P1_ACLC2S28*/ +#define R0900_P1_ACLC2S28 0xf498 +#define ACLC2S28 REGx(R0900_P1_ACLC2S28) +#define F0900_P1_OLDI3Q_MODE 0xf4980080 +#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030 +#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f + +/*P1_ACLC2S216A*/ +#define R0900_P1_ACLC2S216A 0xf499 +#define ACLC2S216A REGx(R0900_P1_ACLC2S216A) +#define F0900_P1_DIS_C3STOPA2 0xf4990080 +#define F0900_P1_CAR2S2_16ADERAT 0xf4990040 +#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030 +#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f + +/*P1_ACLC2S232A*/ +#define R0900_P1_ACLC2S232A 0xf49a +#define ACLC2S232A REGx(R0900_P1_ACLC2S232A) +#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040 +#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030 +#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f + +/*P1_BCLC2S2Q*/ +#define R0900_P1_BCLC2S2Q 0xf49c +#define BCLC2S2Q REGx(R0900_P1_BCLC2S2Q) +#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030 +#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f + +/*P1_BCLC2S28*/ +#define R0900_P1_BCLC2S28 0xf49d +#define BCLC2S28 REGx(R0900_P1_BCLC2S28) +#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030 +#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f + +/*P1_BCLC2S216A*/ +#define R0900_P1_BCLC2S216A 0xf49e +#define BCLC2S216A REGx(R0900_P1_BCLC2S216A) + +/*P1_BCLC2S232A*/ +#define R0900_P1_BCLC2S232A 0xf49f +#define BCLC2S232A REGx(R0900_P1_BCLC2S232A) + +/*P1_PLROOT2*/ +#define R0900_P1_PLROOT2 0xf4ac +#define PLROOT2 REGx(R0900_P1_PLROOT2) +#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c +#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003 + +/*P1_PLROOT1*/ +#define R0900_P1_PLROOT1 0xf4ad +#define PLROOT1 REGx(R0900_P1_PLROOT1) +#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff + +/*P1_PLROOT0*/ +#define R0900_P1_PLROOT0 0xf4ae +#define PLROOT0 REGx(R0900_P1_PLROOT0) +#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff + +/*P1_MODCODLST0*/ +#define R0900_P1_MODCODLST0 0xf4b0 +#define MODCODLST0 REGx(R0900_P1_MODCODLST0) + +/*P1_MODCODLST1*/ +#define R0900_P1_MODCODLST1 0xf4b1 +#define MODCODLST1 REGx(R0900_P1_MODCODLST1) +#define F0900_P1_DIS_MODCOD29 0xf4b100f0 +#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f + +/*P1_MODCODLST2*/ +#define R0900_P1_MODCODLST2 0xf4b2 +#define MODCODLST2 REGx(R0900_P1_MODCODLST2) +#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0 +#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f + +/*P1_MODCODLST3*/ +#define R0900_P1_MODCODLST3 0xf4b3 +#define MODCODLST3 REGx(R0900_P1_MODCODLST3) +#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0 +#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f + +/*P1_MODCODLST4*/ +#define R0900_P1_MODCODLST4 0xf4b4 +#define MODCODLST4 REGx(R0900_P1_MODCODLST4) +#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0 +#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f + +/*P1_MODCODLST5*/ +#define R0900_P1_MODCODLST5 0xf4b5 +#define MODCODLST5 REGx(R0900_P1_MODCODLST5) +#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0 +#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f + +/*P1_MODCODLST6*/ +#define R0900_P1_MODCODLST6 0xf4b6 +#define MODCODLST6 REGx(R0900_P1_MODCODLST6) +#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0 +#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f + +/*P1_MODCODLST7*/ +#define R0900_P1_MODCODLST7 0xf4b7 +#define MODCODLST7 REGx(R0900_P1_MODCODLST7) +#define F0900_P1_DIS_8P_9_10 0xf4b700f0 +#define F0900_P1_DIS_8P_8_9 0xf4b7000f + +/*P1_MODCODLST8*/ +#define R0900_P1_MODCODLST8 0xf4b8 +#define MODCODLST8 REGx(R0900_P1_MODCODLST8) +#define F0900_P1_DIS_8P_5_6 0xf4b800f0 +#define F0900_P1_DIS_8P_3_4 0xf4b8000f + +/*P1_MODCODLST9*/ +#define R0900_P1_MODCODLST9 0xf4b9 +#define MODCODLST9 REGx(R0900_P1_MODCODLST9) +#define F0900_P1_DIS_8P_2_3 0xf4b900f0 +#define F0900_P1_DIS_8P_3_5 0xf4b9000f + +/*P1_MODCODLSTA*/ +#define R0900_P1_MODCODLSTA 0xf4ba +#define MODCODLSTA REGx(R0900_P1_MODCODLSTA) +#define F0900_P1_DIS_QP_9_10 0xf4ba00f0 +#define F0900_P1_DIS_QP_8_9 0xf4ba000f + +/*P1_MODCODLSTB*/ +#define R0900_P1_MODCODLSTB 0xf4bb +#define MODCODLSTB REGx(R0900_P1_MODCODLSTB) +#define F0900_P1_DIS_QP_5_6 0xf4bb00f0 +#define F0900_P1_DIS_QP_4_5 0xf4bb000f + +/*P1_MODCODLSTC*/ +#define R0900_P1_MODCODLSTC 0xf4bc +#define MODCODLSTC REGx(R0900_P1_MODCODLSTC) +#define F0900_P1_DIS_QP_3_4 0xf4bc00f0 +#define F0900_P1_DIS_QP_2_3 0xf4bc000f + +/*P1_MODCODLSTD*/ +#define R0900_P1_MODCODLSTD 0xf4bd +#define MODCODLSTD REGx(R0900_P1_MODCODLSTD) +#define F0900_P1_DIS_QP_3_5 0xf4bd00f0 +#define F0900_P1_DIS_QP_1_2 0xf4bd000f + +/*P1_MODCODLSTE*/ +#define R0900_P1_MODCODLSTE 0xf4be +#define MODCODLSTE REGx(R0900_P1_MODCODLSTE) +#define F0900_P1_DIS_QP_2_5 0xf4be00f0 +#define F0900_P1_DIS_QP_1_3 0xf4be000f + +/*P1_MODCODLSTF*/ +#define R0900_P1_MODCODLSTF 0xf4bf +#define MODCODLSTF REGx(R0900_P1_MODCODLSTF) +#define F0900_P1_DIS_QP_1_4 0xf4bf00f0 + +/*P1_GAUSSR0*/ +#define R0900_P1_GAUSSR0 0xf4c0 +#define GAUSSR0 REGx(R0900_P1_GAUSSR0) +#define F0900_P1_EN_CCIMODE 0xf4c00080 +#define F0900_P1_R0_GAUSSIEN 0xf4c0007f + +/*P1_CCIR0*/ +#define R0900_P1_CCIR0 0xf4c1 +#define CCIR0 REGx(R0900_P1_CCIR0) +#define F0900_P1_CCIDETECT_PLHONLY 0xf4c10080 +#define F0900_P1_R0_CCI 0xf4c1007f + +/*P1_CCIQUANT*/ +#define R0900_P1_CCIQUANT 0xf4c2 +#define CCIQUANT REGx(R0900_P1_CCIQUANT) +#define F0900_P1_CCI_BETA 0xf4c200e0 +#define F0900_P1_CCI_QUANT 0xf4c2001f + +/*P1_CCITHRES*/ +#define R0900_P1_CCITHRES 0xf4c3 +#define CCITHRES REGx(R0900_P1_CCITHRES) +#define F0900_P1_CCI_THRESHOLD 0xf4c300ff + +/*P1_CCIACC*/ +#define R0900_P1_CCIACC 0xf4c4 +#define CCIACC REGx(R0900_P1_CCIACC) +#define F0900_P1_CCI_VALUE 0xf4c400ff + +/*P1_DMDRESCFG*/ +#define R0900_P1_DMDRESCFG 0xf4c6 +#define DMDRESCFG REGx(R0900_P1_DMDRESCFG) +#define F0900_P1_DMDRES_RESET 0xf4c60080 +#define F0900_P1_DMDRES_STRALL 0xf4c60008 +#define F0900_P1_DMDRES_NEWONLY 0xf4c60004 +#define F0900_P1_DMDRES_NOSTORE 0xf4c60002 + +/*P1_DMDRESADR*/ +#define R0900_P1_DMDRESADR 0xf4c7 +#define DMDRESADR REGx(R0900_P1_DMDRESADR) +#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040 +#define F0900_P1_DMDRES_MEMFULL 0xf4c70030 +#define F0900_P1_DMDRES_RESNBR 0xf4c7000f + +/*P1_DMDRESDATA7*/ +#define R0900_P1_DMDRESDATA7 0xf4c8 +#define F0900_P1_DMDRES_DATA7 0xf4c800ff + +/*P1_DMDRESDATA6*/ +#define R0900_P1_DMDRESDATA6 0xf4c9 +#define F0900_P1_DMDRES_DATA6 0xf4c900ff + +/*P1_DMDRESDATA5*/ +#define R0900_P1_DMDRESDATA5 0xf4ca +#define F0900_P1_DMDRES_DATA5 0xf4ca00ff + +/*P1_DMDRESDATA4*/ +#define R0900_P1_DMDRESDATA4 0xf4cb +#define F0900_P1_DMDRES_DATA4 0xf4cb00ff + +/*P1_DMDRESDATA3*/ +#define R0900_P1_DMDRESDATA3 0xf4cc +#define F0900_P1_DMDRES_DATA3 0xf4cc00ff + +/*P1_DMDRESDATA2*/ +#define R0900_P1_DMDRESDATA2 0xf4cd +#define F0900_P1_DMDRES_DATA2 0xf4cd00ff + +/*P1_DMDRESDATA1*/ +#define R0900_P1_DMDRESDATA1 0xf4ce +#define F0900_P1_DMDRES_DATA1 0xf4ce00ff + +/*P1_DMDRESDATA0*/ +#define R0900_P1_DMDRESDATA0 0xf4cf +#define F0900_P1_DMDRES_DATA0 0xf4cf00ff + +/*P1_FFEI1*/ +#define R0900_P1_FFEI1 0xf4d0 +#define FFEI1 REGx(R0900_P1_FFEI1) +#define F0900_P1_FFE_ACCI1 0xf4d001ff + +/*P1_FFEQ1*/ +#define R0900_P1_FFEQ1 0xf4d1 +#define FFEQ1 REGx(R0900_P1_FFEQ1) +#define F0900_P1_FFE_ACCQ1 0xf4d101ff + +/*P1_FFEI2*/ +#define R0900_P1_FFEI2 0xf4d2 +#define FFEI2 REGx(R0900_P1_FFEI2) +#define F0900_P1_FFE_ACCI2 0xf4d201ff + +/*P1_FFEQ2*/ +#define R0900_P1_FFEQ2 0xf4d3 +#define FFEQ2 REGx(R0900_P1_FFEQ2) +#define F0900_P1_FFE_ACCQ2 0xf4d301ff + +/*P1_FFEI3*/ +#define R0900_P1_FFEI3 0xf4d4 +#define FFEI3 REGx(R0900_P1_FFEI3) +#define F0900_P1_FFE_ACCI3 0xf4d401ff + +/*P1_FFEQ3*/ +#define R0900_P1_FFEQ3 0xf4d5 +#define FFEQ3 REGx(R0900_P1_FFEQ3) +#define F0900_P1_FFE_ACCQ3 0xf4d501ff + +/*P1_FFEI4*/ +#define R0900_P1_FFEI4 0xf4d6 +#define FFEI4 REGx(R0900_P1_FFEI4) +#define F0900_P1_FFE_ACCI4 0xf4d601ff + +/*P1_FFEQ4*/ +#define R0900_P1_FFEQ4 0xf4d7 +#define FFEQ4 REGx(R0900_P1_FFEQ4) +#define F0900_P1_FFE_ACCQ4 0xf4d701ff + +/*P1_FFECFG*/ +#define R0900_P1_FFECFG 0xf4d8 +#define FFECFG REGx(R0900_P1_FFECFG) +#define F0900_P1_EQUALFFE_ON 0xf4d80040 +#define F0900_P1_MU_EQUALFFE 0xf4d80007 + +/*P1_TNRCFG*/ +#define R0900_P1_TNRCFG 0xf4e0 +#define TNRCFG REGx(R0900_P1_TNRCFG) +#define F0900_P1_TUN_ACKFAIL 0xf4e00080 +#define F0900_P1_TUN_TYPE 0xf4e00070 +#define F0900_P1_TUN_SECSTOP 0xf4e00008 +#define F0900_P1_TUN_VCOSRCH 0xf4e00004 +#define F0900_P1_TUN_MADDRESS 0xf4e00003 + +/*P1_TNRCFG2*/ +#define R0900_P1_TNRCFG2 0xf4e1 +#define TNRCFG2 REGx(R0900_P1_TNRCFG2) +#define F0900_P1_TUN_IQSWAP 0xf4e10080 +#define F0900_P1_DIS_BWCALC 0xf4e10004 +#define F0900_P1_SHORT_WAITSTATES 0xf4e10002 + +/*P1_TNRXTAL*/ +#define R0900_P1_TNRXTAL 0xf4e4 +#define TNRXTAL REGx(R0900_P1_TNRXTAL) +#define F0900_P1_TUN_XTALFREQ 0xf4e4001f + +/*P1_TNRSTEPS*/ +#define R0900_P1_TNRSTEPS 0xf4e7 +#define TNRSTEPS REGx(R0900_P1_TNRSTEPS) +#define F0900_P1_TUNER_BW0P125 0xf4e70080 +#define F0900_P1_BWINC_OFFSET 0xf4e70170 +#define F0900_P1_SOFTSTEP_RNG 0xf4e70008 +#define F0900_P1_TUN_BWOFFSET 0xf4e70007 + +/*P1_TNRGAIN*/ +#define R0900_P1_TNRGAIN 0xf4e8 +#define TNRGAIN REGx(R0900_P1_TNRGAIN) +#define F0900_P1_TUN_KDIVEN 0xf4e800c0 +#define F0900_P1_STB6X00_OCK 0xf4e80030 +#define F0900_P1_TUN_GAIN 0xf4e8000f + +/*P1_TNRRF1*/ +#define R0900_P1_TNRRF1 0xf4e9 +#define TNRRF1 REGx(R0900_P1_TNRRF1) +#define F0900_P1_TUN_RFFREQ2 0xf4e900ff +#define TUN_RFFREQ2 FLDx(F0900_P1_TUN_RFFREQ2) + +/*P1_TNRRF0*/ +#define R0900_P1_TNRRF0 0xf4ea +#define TNRRF0 REGx(R0900_P1_TNRRF0) +#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff +#define TUN_RFFREQ1 FLDx(F0900_P1_TUN_RFFREQ1) + +/*P1_TNRBW*/ +#define R0900_P1_TNRBW 0xf4eb +#define TNRBW REGx(R0900_P1_TNRBW) +#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0 +#define TUN_RFFREQ0 FLDx(F0900_P1_TUN_RFFREQ0) +#define F0900_P1_TUN_BW 0xf4eb003f +#define TUN_BW FLDx(F0900_P1_TUN_BW) + +/*P1_TNRADJ*/ +#define R0900_P1_TNRADJ 0xf4ec +#define TNRADJ REGx(R0900_P1_TNRADJ) +#define F0900_P1_STB61X0_CALTIME 0xf4ec0040 + +/*P1_TNRCTL2*/ +#define R0900_P1_TNRCTL2 0xf4ed +#define TNRCTL2 REGx(R0900_P1_TNRCTL2) +#define F0900_P1_STB61X0_RCCKOFF 0xf4ed0080 +#define F0900_P1_STB61X0_ICP_SDOFF 0xf4ed0040 +#define F0900_P1_STB61X0_DCLOOPOFF 0xf4ed0020 +#define F0900_P1_STB61X0_REFOUTSEL 0xf4ed0010 +#define F0900_P1_STB61X0_CALOFF 0xf4ed0008 +#define F0900_P1_STB6XX0_LPT_BEN 0xf4ed0004 +#define F0900_P1_STB6XX0_RX_OSCP 0xf4ed0002 +#define F0900_P1_STB6XX0_SYN 0xf4ed0001 + +/*P1_TNRCFG3*/ +#define R0900_P1_TNRCFG3 0xf4ee +#define TNRCFG3 REGx(R0900_P1_TNRCFG3) +#define F0900_P1_TUN_PLLFREQ 0xf4ee001c +#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003 + +/*P1_TNRLAUNCH*/ +#define R0900_P1_TNRLAUNCH 0xf4f0 +#define TNRLAUNCH REGx(R0900_P1_TNRLAUNCH) + +/*P1_TNRLD*/ +#define R0900_P1_TNRLD 0xf4f0 +#define TNRLD REGx(R0900_P1_TNRLD) +#define F0900_P1_TUNLD_VCOING 0xf4f00080 +#define F0900_P1_TUN_REG1FAIL 0xf4f00040 +#define F0900_P1_TUN_REG2FAIL 0xf4f00020 +#define F0900_P1_TUN_REG3FAIL 0xf4f00010 +#define F0900_P1_TUN_REG4FAIL 0xf4f00008 +#define F0900_P1_TUN_REG5FAIL 0xf4f00004 +#define F0900_P1_TUN_BWING 0xf4f00002 +#define F0900_P1_TUN_LOCKED 0xf4f00001 + +/*P1_TNROBSL*/ +#define R0900_P1_TNROBSL 0xf4f6 +#define TNROBSL REGx(R0900_P1_TNROBSL) +#define F0900_P1_TUN_I2CABORTED 0xf4f60080 +#define F0900_P1_TUN_LPEN 0xf4f60040 +#define F0900_P1_TUN_FCCK 0xf4f60020 +#define F0900_P1_TUN_I2CLOCKED 0xf4f60010 +#define F0900_P1_TUN_PROGDONE 0xf4f6000c +#define F0900_P1_TUN_RFRESTE1 0xf4f60003 +#define TUN_RFRESTE1 FLDx(F0900_P1_TUN_RFRESTE1) + +/*P1_TNRRESTE*/ +#define R0900_P1_TNRRESTE 0xf4f7 +#define TNRRESTE REGx(R0900_P1_TNRRESTE) +#define F0900_P1_TUN_RFRESTE0 0xf4f700ff +#define TUN_RFRESTE0 FLDx(F0900_P1_TUN_RFRESTE0) + +/*P1_SMAPCOEF7*/ +#define R0900_P1_SMAPCOEF7 0xf500 +#define SMAPCOEF7 REGx(R0900_P1_SMAPCOEF7) +#define F0900_P1_DIS_QSCALE 0xf5000080 +#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f + +/*P1_SMAPCOEF6*/ +#define R0900_P1_SMAPCOEF6 0xf501 +#define SMAPCOEF6 REGx(R0900_P1_SMAPCOEF6) +#define F0900_P1_ADJ_8PSKLLR1 0xf5010004 +#define F0900_P1_OLD_8PSKLLR1 0xf5010002 +#define F0900_P1_DIS_AB8PSK 0xf5010001 + +/*P1_SMAPCOEF5*/ +#define R0900_P1_SMAPCOEF5 0xf502 +#define SMAPCOEF5 REGx(R0900_P1_SMAPCOEF5) +#define F0900_P1_DIS_8SCALE 0xf5020080 +#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f + +/*P1_NCO2MAX1*/ +#define R0900_P1_NCO2MAX1 0xf514 +#define NCO2MAX1 REGx(R0900_P1_NCO2MAX1) +#define F0900_P1_TETA2_MAXVABS1 0xf51400ff + +/*P1_NCO2MAX0*/ +#define R0900_P1_NCO2MAX0 0xf515 +#define NCO2MAX0 REGx(R0900_P1_NCO2MAX0) +#define F0900_P1_TETA2_MAXVABS0 0xf51500ff + +/*P1_NCO2FR1*/ +#define R0900_P1_NCO2FR1 0xf516 +#define NCO2FR1 REGx(R0900_P1_NCO2FR1) +#define F0900_P1_NCO2FINAL_ANGLE1 0xf51600ff + +/*P1_NCO2FR0*/ +#define R0900_P1_NCO2FR0 0xf517 +#define NCO2FR0 REGx(R0900_P1_NCO2FR0) +#define F0900_P1_NCO2FINAL_ANGLE0 0xf51700ff + +/*P1_CFR2AVRGE1*/ +#define R0900_P1_CFR2AVRGE1 0xf518 +#define CFR2AVRGE1 REGx(R0900_P1_CFR2AVRGE1) +#define F0900_P1_I2C_CFR2AVERAGE1 0xf51800ff + +/*P1_CFR2AVRGE0*/ +#define R0900_P1_CFR2AVRGE0 0xf519 +#define CFR2AVRGE0 REGx(R0900_P1_CFR2AVRGE0) +#define F0900_P1_I2C_CFR2AVERAGE0 0xf51900ff + +/*P1_DMDPLHSTAT*/ +#define R0900_P1_DMDPLHSTAT 0xf520 +#define DMDPLHSTAT REGx(R0900_P1_DMDPLHSTAT) +#define F0900_P1_PLH_STATISTIC 0xf52000ff + +/*P1_LOCKTIME3*/ +#define R0900_P1_LOCKTIME3 0xf522 +#define LOCKTIME3 REGx(R0900_P1_LOCKTIME3) +#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff + +/*P1_LOCKTIME2*/ +#define R0900_P1_LOCKTIME2 0xf523 +#define LOCKTIME2 REGx(R0900_P1_LOCKTIME2) +#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff + +/*P1_LOCKTIME1*/ +#define R0900_P1_LOCKTIME1 0xf524 +#define LOCKTIME1 REGx(R0900_P1_LOCKTIME1) +#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff + +/*P1_LOCKTIME0*/ +#define R0900_P1_LOCKTIME0 0xf525 +#define LOCKTIME0 REGx(R0900_P1_LOCKTIME0) +#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff + +/*P1_VITSCALE*/ +#define R0900_P1_VITSCALE 0xf532 +#define VITSCALE REGx(R0900_P1_VITSCALE) +#define F0900_P1_NVTH_NOSRANGE 0xf5320080 +#define F0900_P1_VERROR_MAXMODE 0xf5320040 +#define F0900_P1_NSLOWSN_LOCKED 0xf5320008 +#define F0900_P1_DIS_RSFLOCK 0xf5320002 + +/*P1_FECM*/ +#define R0900_P1_FECM 0xf533 +#define FECM REGx(R0900_P1_FECM) +#define F0900_P1_DSS_DVB 0xf5330080 +#define DSS_DVB FLDx(F0900_P1_DSS_DVB) +#define F0900_P1_DSS_SRCH 0xf5330010 +#define F0900_P1_SYNCVIT 0xf5330002 +#define F0900_P1_IQINV 0xf5330001 +#define IQINV FLDx(F0900_P1_IQINV) + +/*P1_VTH12*/ +#define R0900_P1_VTH12 0xf534 +#define VTH12 REGx(R0900_P1_VTH12) +#define F0900_P1_VTH12 0xf53400ff + +/*P1_VTH23*/ +#define R0900_P1_VTH23 0xf535 +#define VTH23 REGx(R0900_P1_VTH23) +#define F0900_P1_VTH23 0xf53500ff + +/*P1_VTH34*/ +#define R0900_P1_VTH34 0xf536 +#define VTH34 REGx(R0900_P1_VTH34) +#define F0900_P1_VTH34 0xf53600ff + +/*P1_VTH56*/ +#define R0900_P1_VTH56 0xf537 +#define VTH56 REGx(R0900_P1_VTH56) +#define F0900_P1_VTH56 0xf53700ff + +/*P1_VTH67*/ +#define R0900_P1_VTH67 0xf538 +#define VTH67 REGx(R0900_P1_VTH67) +#define F0900_P1_VTH67 0xf53800ff + +/*P1_VTH78*/ +#define R0900_P1_VTH78 0xf539 +#define VTH78 REGx(R0900_P1_VTH78) +#define F0900_P1_VTH78 0xf53900ff + +/*P1_VITCURPUN*/ +#define R0900_P1_VITCURPUN 0xf53a +#define VITCURPUN REGx(R0900_P1_VITCURPUN) +#define F0900_P1_VIT_CURPUN 0xf53a001f +#define VIT_CURPUN FLDx(F0900_P1_VIT_CURPUN) + +/*P1_VERROR*/ +#define R0900_P1_VERROR 0xf53b +#define VERROR REGx(R0900_P1_VERROR) +#define F0900_P1_REGERR_VIT 0xf53b00ff + +/*P1_PRVIT*/ +#define R0900_P1_PRVIT 0xf53c +#define PRVIT REGx(R0900_P1_PRVIT) +#define F0900_P1_DIS_VTHLOCK 0xf53c0040 +#define F0900_P1_E7_8VIT 0xf53c0020 +#define F0900_P1_E6_7VIT 0xf53c0010 +#define F0900_P1_E5_6VIT 0xf53c0008 +#define F0900_P1_E3_4VIT 0xf53c0004 +#define F0900_P1_E2_3VIT 0xf53c0002 +#define F0900_P1_E1_2VIT 0xf53c0001 + +/*P1_VAVSRVIT*/ +#define R0900_P1_VAVSRVIT 0xf53d +#define VAVSRVIT REGx(R0900_P1_VAVSRVIT) +#define F0900_P1_AMVIT 0xf53d0080 +#define F0900_P1_FROZENVIT 0xf53d0040 +#define F0900_P1_SNVIT 0xf53d0030 +#define F0900_P1_TOVVIT 0xf53d000c +#define F0900_P1_HYPVIT 0xf53d0003 + +/*P1_VSTATUSVIT*/ +#define R0900_P1_VSTATUSVIT 0xf53e +#define VSTATUSVIT REGx(R0900_P1_VSTATUSVIT) +#define F0900_P1_PRFVIT 0xf53e0010 +#define PRFVIT FLDx(F0900_P1_PRFVIT) +#define F0900_P1_LOCKEDVIT 0xf53e0008 +#define LOCKEDVIT FLDx(F0900_P1_LOCKEDVIT) + +/*P1_VTHINUSE*/ +#define R0900_P1_VTHINUSE 0xf53f +#define VTHINUSE REGx(R0900_P1_VTHINUSE) +#define F0900_P1_VIT_INUSE 0xf53f00ff + +/*P1_KDIV12*/ +#define R0900_P1_KDIV12 0xf540 +#define KDIV12 REGx(R0900_P1_KDIV12) +#define F0900_P1_K_DIVIDER_12 0xf540007f + +/*P1_KDIV23*/ +#define R0900_P1_KDIV23 0xf541 +#define KDIV23 REGx(R0900_P1_KDIV23) +#define F0900_P1_K_DIVIDER_23 0xf541007f + +/*P1_KDIV34*/ +#define R0900_P1_KDIV34 0xf542 +#define KDIV34 REGx(R0900_P1_KDIV34) +#define F0900_P1_K_DIVIDER_34 0xf542007f + +/*P1_KDIV56*/ +#define R0900_P1_KDIV56 0xf543 +#define KDIV56 REGx(R0900_P1_KDIV56) +#define F0900_P1_K_DIVIDER_56 0xf543007f + +/*P1_KDIV67*/ +#define R0900_P1_KDIV67 0xf544 +#define KDIV67 REGx(R0900_P1_KDIV67) +#define F0900_P1_K_DIVIDER_67 0xf544007f + +/*P1_KDIV78*/ +#define R0900_P1_KDIV78 0xf545 +#define KDIV78 REGx(R0900_P1_KDIV78) +#define F0900_P1_K_DIVIDER_78 0xf545007f + +/*P1_PDELCTRL1*/ +#define R0900_P1_PDELCTRL1 0xf550 +#define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) +#define F0900_P1_INV_MISMASK 0xf5500080 +#define INV_MISMASK FLDx(F0900_P1_INV_MISMASK) +#define F0900_P1_FILTER_EN 0xf5500020 +#define FILTER_EN FLDx(F0900_P1_FILTER_EN) +#define F0900_P1_EN_MIS00 0xf5500002 +#define EN_MIS00 FLDx(F0900_P1_EN_MIS00) +#define F0900_P1_ALGOSWRST 0xf5500001 +#define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) + +/*P1_PDELCTRL2*/ +#define R0900_P1_PDELCTRL2 0xf551 +#define PDELCTRL2 REGx(R0900_P1_PDELCTRL2) +#define F0900_P1_RESET_UPKO_COUNT 0xf5510040 +#define RESET_UPKO_COUNT FLDx(F0900_P1_RESET_UPKO_COUNT) +#define F0900_P1_FRAME_MODE 0xf5510002 +#define F0900_P1_NOBCHERRFLG_USE 0xf5510001 + +/*P1_HYSTTHRESH*/ +#define R0900_P1_HYSTTHRESH 0xf554 +#define HYSTTHRESH REGx(R0900_P1_HYSTTHRESH) +#define F0900_P1_UNLCK_THRESH 0xf55400f0 +#define F0900_P1_DELIN_LCK_THRESH 0xf554000f + +/*P1_ISIENTRY*/ +#define R0900_P1_ISIENTRY 0xf55e +#define ISIENTRY REGx(R0900_P1_ISIENTRY) +#define F0900_P1_ISI_ENTRY 0xf55e00ff + +/*P1_ISIBITENA*/ +#define R0900_P1_ISIBITENA 0xf55f +#define ISIBITENA REGx(R0900_P1_ISIBITENA) +#define F0900_P1_ISI_BIT_EN 0xf55f00ff + +/*P1_MATSTR1*/ +#define R0900_P1_MATSTR1 0xf560 +#define MATSTR1 REGx(R0900_P1_MATSTR1) +#define F0900_P1_MATYPE_CURRENT1 0xf56000ff + +/*P1_MATSTR0*/ +#define R0900_P1_MATSTR0 0xf561 +#define MATSTR0 REGx(R0900_P1_MATSTR0) +#define F0900_P1_MATYPE_CURRENT0 0xf56100ff + +/*P1_UPLSTR1*/ +#define R0900_P1_UPLSTR1 0xf562 +#define UPLSTR1 REGx(R0900_P1_UPLSTR1) +#define F0900_P1_UPL_CURRENT1 0xf56200ff + +/*P1_UPLSTR0*/ +#define R0900_P1_UPLSTR0 0xf563 +#define UPLSTR0 REGx(R0900_P1_UPLSTR0) +#define F0900_P1_UPL_CURRENT0 0xf56300ff + +/*P1_DFLSTR1*/ +#define R0900_P1_DFLSTR1 0xf564 +#define DFLSTR1 REGx(R0900_P1_DFLSTR1) +#define F0900_P1_DFL_CURRENT1 0xf56400ff + +/*P1_DFLSTR0*/ +#define R0900_P1_DFLSTR0 0xf565 +#define DFLSTR0 REGx(R0900_P1_DFLSTR0) +#define F0900_P1_DFL_CURRENT0 0xf56500ff + +/*P1_SYNCSTR*/ +#define R0900_P1_SYNCSTR 0xf566 +#define SYNCSTR REGx(R0900_P1_SYNCSTR) +#define F0900_P1_SYNC_CURRENT 0xf56600ff + +/*P1_SYNCDSTR1*/ +#define R0900_P1_SYNCDSTR1 0xf567 +#define SYNCDSTR1 REGx(R0900_P1_SYNCDSTR1) +#define F0900_P1_SYNCD_CURRENT1 0xf56700ff + +/*P1_SYNCDSTR0*/ +#define R0900_P1_SYNCDSTR0 0xf568 +#define SYNCDSTR0 REGx(R0900_P1_SYNCDSTR0) +#define F0900_P1_SYNCD_CURRENT0 0xf56800ff + +/*P1_PDELSTATUS1*/ +#define R0900_P1_PDELSTATUS1 0xf569 +#define F0900_P1_PKTDELIN_DELOCK 0xf5690080 +#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040 +#define F0900_P1_CONTINUOUS_STREAM 0xf5690020 +#define F0900_P1_UNACCEPTED_STREAM 0xf5690010 +#define F0900_P1_BCH_ERROR_FLAG 0xf5690008 +#define F0900_P1_PKTDELIN_LOCK 0xf5690002 +#define PKTDELIN_LOCK FLDx(F0900_P1_PKTDELIN_LOCK) +#define F0900_P1_FIRST_LOCK 0xf5690001 + +/*P1_PDELSTATUS2*/ +#define R0900_P1_PDELSTATUS2 0xf56a +#define F0900_P1_FRAME_MODCOD 0xf56a007c +#define F0900_P1_FRAME_TYPE 0xf56a0003 + +/*P1_BBFCRCKO1*/ +#define R0900_P1_BBFCRCKO1 0xf56b +#define BBFCRCKO1 REGx(R0900_P1_BBFCRCKO1) +#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff + +/*P1_BBFCRCKO0*/ +#define R0900_P1_BBFCRCKO0 0xf56c +#define BBFCRCKO0 REGx(R0900_P1_BBFCRCKO0) +#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff + +/*P1_UPCRCKO1*/ +#define R0900_P1_UPCRCKO1 0xf56d +#define UPCRCKO1 REGx(R0900_P1_UPCRCKO1) +#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff + +/*P1_UPCRCKO0*/ +#define R0900_P1_UPCRCKO0 0xf56e +#define UPCRCKO0 REGx(R0900_P1_UPCRCKO0) +#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff + +/*P1_PDELCTRL3*/ +#define R0900_P1_PDELCTRL3 0xf56f +#define PDELCTRL3 REGx(R0900_P1_PDELCTRL3) +#define F0900_P1_PKTDEL_CONTFAIL 0xf56f0080 +#define F0900_P1_NOFIFO_BCHERR 0xf56f0020 + +/*P1_TSSTATEM*/ +#define R0900_P1_TSSTATEM 0xf570 +#define TSSTATEM REGx(R0900_P1_TSSTATEM) +#define F0900_P1_TSDIL_ON 0xf5700080 +#define F0900_P1_TSRS_ON 0xf5700020 +#define F0900_P1_TSDESCRAMB_ON 0xf5700010 +#define F0900_P1_TSFRAME_MODE 0xf5700008 +#define F0900_P1_TS_DISABLE 0xf5700004 +#define F0900_P1_TSOUT_NOSYNC 0xf5700001 + +/*P1_TSCFGH*/ +#define R0900_P1_TSCFGH 0xf572 +#define TSCFGH REGx(R0900_P1_TSCFGH) +#define F0900_P1_TSFIFO_DVBCI 0xf5720080 +#define F0900_P1_TSFIFO_SERIAL 0xf5720040 +#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020 +#define F0900_P1_TSFIFO_DUTY50 0xf5720010 +#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008 +#define F0900_P1_TSFIFO_ERRMODE 0xf5720006 +#define F0900_P1_RST_HWARE 0xf5720001 +#define RST_HWARE FLDx(F0900_P1_RST_HWARE) + +/*P1_TSCFGM*/ +#define R0900_P1_TSCFGM 0xf573 +#define TSCFGM REGx(R0900_P1_TSCFGM) +#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0 +#define F0900_P1_TSFIFO_PERMDATA 0xf5730020 +#define F0900_P1_TSFIFO_DPUNACT 0xf5730002 +#define F0900_P1_TSFIFO_INVDATA 0xf5730001 + +/*P1_TSCFGL*/ +#define R0900_P1_TSCFGL 0xf574 +#define TSCFGL REGx(R0900_P1_TSCFGL) +#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0 +#define F0900_P1_BCHERROR_MODE 0xf5740030 +#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008 +#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004 +#define F0900_P1_TSFIFO_BITSPEED 0xf5740003 + +/*P1_TSINSDELH*/ +#define R0900_P1_TSINSDELH 0xf576 +#define TSINSDELH REGx(R0900_P1_TSINSDELH) +#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080 +#define F0900_P1_TSDEL_XXHEADER 0xf5760040 +#define F0900_P1_TSDEL_BBHEADER 0xf5760020 +#define F0900_P1_TSDEL_DATAFIELD 0xf5760010 +#define F0900_P1_TSINSDEL_ISCR 0xf5760008 +#define F0900_P1_TSINSDEL_NPD 0xf5760004 +#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002 +#define F0900_P1_TSINSDEL_CRC8 0xf5760001 + +/*P1_TSDIVN*/ +#define R0900_P1_TSDIVN 0xf579 +#define TSDIVN REGx(R0900_P1_TSDIVN) +#define F0900_P1_TSFIFO_SPEEDMODE 0xf57900c0 + +/*P1_TSCFG4*/ +#define R0900_P1_TSCFG4 0xf57a +#define TSCFG4 REGx(R0900_P1_TSCFG4) +#define F0900_P1_TSFIFO_TSSPEEDMODE 0xf57a00c0 + +/*P1_TSSPEED*/ +#define R0900_P1_TSSPEED 0xf580 +#define TSSPEED REGx(R0900_P1_TSSPEED) +#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff + +/*P1_TSSTATUS*/ +#define R0900_P1_TSSTATUS 0xf581 +#define TSSTATUS REGx(R0900_P1_TSSTATUS) +#define F0900_P1_TSFIFO_LINEOK 0xf5810080 +#define TSFIFO_LINEOK FLDx(F0900_P1_TSFIFO_LINEOK) +#define F0900_P1_TSFIFO_ERROR 0xf5810040 +#define F0900_P1_DIL_READY 0xf5810001 + +/*P1_TSSTATUS2*/ +#define R0900_P1_TSSTATUS2 0xf582 +#define TSSTATUS2 REGx(R0900_P1_TSSTATUS2) +#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080 +#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040 +#define F0900_P1_DILXX_RESET 0xf5820020 +#define F0900_P1_TSSERIAL_IMPOS 0xf5820010 +#define F0900_P1_SCRAMBDETECT 0xf5820002 + +/*P1_TSBITRATE1*/ +#define R0900_P1_TSBITRATE1 0xf583 +#define TSBITRATE1 REGx(R0900_P1_TSBITRATE1) +#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff + +/*P1_TSBITRATE0*/ +#define R0900_P1_TSBITRATE0 0xf584 +#define TSBITRATE0 REGx(R0900_P1_TSBITRATE0) +#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff + +/*P1_ERRCTRL1*/ +#define R0900_P1_ERRCTRL1 0xf598 +#define ERRCTRL1 REGx(R0900_P1_ERRCTRL1) +#define F0900_P1_ERR_SOURCE1 0xf59800f0 +#define F0900_P1_NUM_EVENT1 0xf5980007 + +/*P1_ERRCNT12*/ +#define R0900_P1_ERRCNT12 0xf599 +#define ERRCNT12 REGx(R0900_P1_ERRCNT12) +#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080 +#define F0900_P1_ERR_CNT12 0xf599007f +#define ERR_CNT12 FLDx(F0900_P1_ERR_CNT12) + +/*P1_ERRCNT11*/ +#define R0900_P1_ERRCNT11 0xf59a +#define ERRCNT11 REGx(R0900_P1_ERRCNT11) +#define F0900_P1_ERR_CNT11 0xf59a00ff +#define ERR_CNT11 FLDx(F0900_P1_ERR_CNT11) + +/*P1_ERRCNT10*/ +#define R0900_P1_ERRCNT10 0xf59b +#define ERRCNT10 REGx(R0900_P1_ERRCNT10) +#define F0900_P1_ERR_CNT10 0xf59b00ff +#define ERR_CNT10 FLDx(F0900_P1_ERR_CNT10) + +/*P1_ERRCTRL2*/ +#define R0900_P1_ERRCTRL2 0xf59c +#define ERRCTRL2 REGx(R0900_P1_ERRCTRL2) +#define F0900_P1_ERR_SOURCE2 0xf59c00f0 +#define F0900_P1_NUM_EVENT2 0xf59c0007 + +/*P1_ERRCNT22*/ +#define R0900_P1_ERRCNT22 0xf59d +#define ERRCNT22 REGx(R0900_P1_ERRCNT22) +#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080 +#define F0900_P1_ERR_CNT22 0xf59d007f +#define ERR_CNT22 FLDx(F0900_P1_ERR_CNT22) + +/*P1_ERRCNT21*/ +#define R0900_P1_ERRCNT21 0xf59e +#define ERRCNT21 REGx(R0900_P1_ERRCNT21) +#define F0900_P1_ERR_CNT21 0xf59e00ff +#define ERR_CNT21 FLDx(F0900_P1_ERR_CNT21) + +/*P1_ERRCNT20*/ +#define R0900_P1_ERRCNT20 0xf59f +#define ERRCNT20 REGx(R0900_P1_ERRCNT20) +#define F0900_P1_ERR_CNT20 0xf59f00ff +#define ERR_CNT20 FLDx(F0900_P1_ERR_CNT20) + +/*P1_FECSPY*/ +#define R0900_P1_FECSPY 0xf5a0 +#define FECSPY REGx(R0900_P1_FECSPY) +#define F0900_P1_SPY_ENABLE 0xf5a00080 +#define F0900_P1_NO_SYNCBYTE 0xf5a00040 +#define F0900_P1_SERIAL_MODE 0xf5a00020 +#define F0900_P1_UNUSUAL_PACKET 0xf5a00010 +#define F0900_P1_BERMETER_DATAMODE 0xf5a00008 +#define F0900_P1_BERMETER_LMODE 0xf5a00002 +#define F0900_P1_BERMETER_RESET 0xf5a00001 + +/*P1_FSPYCFG*/ +#define R0900_P1_FSPYCFG 0xf5a1 +#define FSPYCFG REGx(R0900_P1_FSPYCFG) +#define F0900_P1_FECSPY_INPUT 0xf5a100c0 +#define F0900_P1_RST_ON_ERROR 0xf5a10020 +#define F0900_P1_ONE_SHOT 0xf5a10010 +#define F0900_P1_I2C_MODE 0xf5a1000c +#define F0900_P1_SPY_HYSTERESIS 0xf5a10003 + +/*P1_FSPYDATA*/ +#define R0900_P1_FSPYDATA 0xf5a2 +#define FSPYDATA REGx(R0900_P1_FSPYDATA) +#define F0900_P1_SPY_STUFFING 0xf5a20080 +#define F0900_P1_SPY_CNULLPKT 0xf5a20020 +#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f + +/*P1_FSPYOUT*/ +#define R0900_P1_FSPYOUT 0xf5a3 +#define FSPYOUT REGx(R0900_P1_FSPYOUT) +#define F0900_P1_FSPY_DIRECT 0xf5a30080 +#define F0900_P1_STUFF_MODE 0xf5a30007 + +/*P1_FSTATUS*/ +#define R0900_P1_FSTATUS 0xf5a4 +#define FSTATUS REGx(R0900_P1_FSTATUS) +#define F0900_P1_SPY_ENDSIM 0xf5a40080 +#define F0900_P1_VALID_SIM 0xf5a40040 +#define F0900_P1_FOUND_SIGNAL 0xf5a40020 +#define F0900_P1_DSS_SYNCBYTE 0xf5a40010 +#define F0900_P1_RESULT_STATE 0xf5a4000f + +/*P1_FBERCPT4*/ +#define R0900_P1_FBERCPT4 0xf5a8 +#define FBERCPT4 REGx(R0900_P1_FBERCPT4) +#define F0900_P1_FBERMETER_CPT4 0xf5a800ff + +/*P1_FBERCPT3*/ +#define R0900_P1_FBERCPT3 0xf5a9 +#define FBERCPT3 REGx(R0900_P1_FBERCPT3) +#define F0900_P1_FBERMETER_CPT3 0xf5a900ff + +/*P1_FBERCPT2*/ +#define R0900_P1_FBERCPT2 0xf5aa +#define FBERCPT2 REGx(R0900_P1_FBERCPT2) +#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff + +/*P1_FBERCPT1*/ +#define R0900_P1_FBERCPT1 0xf5ab +#define FBERCPT1 REGx(R0900_P1_FBERCPT1) +#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff + +/*P1_FBERCPT0*/ +#define R0900_P1_FBERCPT0 0xf5ac +#define FBERCPT0 REGx(R0900_P1_FBERCPT0) +#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff + +/*P1_FBERERR2*/ +#define R0900_P1_FBERERR2 0xf5ad +#define FBERERR2 REGx(R0900_P1_FBERERR2) +#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff + +/*P1_FBERERR1*/ +#define R0900_P1_FBERERR1 0xf5ae +#define FBERERR1 REGx(R0900_P1_FBERERR1) +#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff + +/*P1_FBERERR0*/ +#define R0900_P1_FBERERR0 0xf5af +#define FBERERR0 REGx(R0900_P1_FBERERR0) +#define F0900_P1_FBERMETER_ERR0 0xf5af00ff + +/*P1_FSPYBER*/ +#define R0900_P1_FSPYBER 0xf5b2 +#define FSPYBER REGx(R0900_P1_FSPYBER) +#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010 +#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008 +#define F0900_P1_FSPYBER_CTIME 0xf5b20007 + +/*RCCFG2*/ +#define R0900_RCCFG2 0xf600 + +/*TSGENERAL*/ +#define R0900_TSGENERAL 0xf630 +#define F0900_TSFIFO_DISTS2PAR 0xf6300040 +#define F0900_MUXSTREAM_OUTMODE 0xf6300008 +#define F0900_TSFIFO_PERMPARAL 0xf6300006 + +/*TSGENERAL1X*/ +#define R0900_TSGENERAL1X 0xf670 + +/*NBITER_NF4*/ +#define R0900_NBITER_NF4 0xfa03 +#define F0900_NBITER_NF_QP_1_2 0xfa0300ff + +/*NBITER_NF5*/ +#define R0900_NBITER_NF5 0xfa04 +#define F0900_NBITER_NF_QP_3_5 0xfa0400ff + +/*NBITER_NF6*/ +#define R0900_NBITER_NF6 0xfa05 +#define F0900_NBITER_NF_QP_2_3 0xfa0500ff + +/*NBITER_NF7*/ +#define R0900_NBITER_NF7 0xfa06 +#define F0900_NBITER_NF_QP_3_4 0xfa0600ff + +/*NBITER_NF8*/ +#define R0900_NBITER_NF8 0xfa07 +#define F0900_NBITER_NF_QP_4_5 0xfa0700ff + +/*NBITER_NF9*/ +#define R0900_NBITER_NF9 0xfa08 +#define F0900_NBITER_NF_QP_5_6 0xfa0800ff + +/*NBITER_NF10*/ +#define R0900_NBITER_NF10 0xfa09 +#define F0900_NBITER_NF_QP_8_9 0xfa0900ff + +/*NBITER_NF11*/ +#define R0900_NBITER_NF11 0xfa0a +#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff + +/*NBITER_NF12*/ +#define R0900_NBITER_NF12 0xfa0b +#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff + +/*NBITER_NF13*/ +#define R0900_NBITER_NF13 0xfa0c +#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff + +/*NBITER_NF14*/ +#define R0900_NBITER_NF14 0xfa0d +#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff + +/*NBITER_NF15*/ +#define R0900_NBITER_NF15 0xfa0e +#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff + +/*NBITER_NF16*/ +#define R0900_NBITER_NF16 0xfa0f +#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff + +/*NBITER_NF17*/ +#define R0900_NBITER_NF17 0xfa10 +#define F0900_NBITER_NF_8P_9_10 0xfa1000ff + +/*NBITERNOERR*/ +#define R0900_NBITERNOERR 0xfa3f +#define F0900_NBITER_STOP_CRIT 0xfa3f000f + +/*GAINLLR_NF4*/ +#define R0900_GAINLLR_NF4 0xfa43 +#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f + +/*GAINLLR_NF5*/ +#define R0900_GAINLLR_NF5 0xfa44 +#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f + +/*GAINLLR_NF6*/ +#define R0900_GAINLLR_NF6 0xfa45 +#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f + +/*GAINLLR_NF7*/ +#define R0900_GAINLLR_NF7 0xfa46 +#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f + +/*GAINLLR_NF8*/ +#define R0900_GAINLLR_NF8 0xfa47 +#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f + +/*GAINLLR_NF9*/ +#define R0900_GAINLLR_NF9 0xfa48 +#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f + +/*GAINLLR_NF10*/ +#define R0900_GAINLLR_NF10 0xfa49 +#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f + +/*GAINLLR_NF11*/ +#define R0900_GAINLLR_NF11 0xfa4a +#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f + +/*GAINLLR_NF12*/ +#define R0900_GAINLLR_NF12 0xfa4b +#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f + +/*GAINLLR_NF13*/ +#define R0900_GAINLLR_NF13 0xfa4c +#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f + +/*GAINLLR_NF14*/ +#define R0900_GAINLLR_NF14 0xfa4d +#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f + +/*GAINLLR_NF15*/ +#define R0900_GAINLLR_NF15 0xfa4e +#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f + +/*GAINLLR_NF16*/ +#define R0900_GAINLLR_NF16 0xfa4f +#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f + +/*GAINLLR_NF17*/ +#define R0900_GAINLLR_NF17 0xfa50 +#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f + +/*CFGEXT*/ +#define R0900_CFGEXT 0xfa80 +#define F0900_STAGMODE 0xfa800080 +#define F0900_BYPBCH 0xfa800040 +#define F0900_BYPLDPC 0xfa800020 +#define F0900_LDPCMODE 0xfa800010 +#define F0900_INVLLRSIGN 0xfa800008 +#define F0900_SHORTMULT 0xfa800004 +#define F0900_EXTERNTX 0xfa800001 + +/*GENCFG*/ +#define R0900_GENCFG 0xfa86 +#define F0900_BROADCAST 0xfa860010 +#define F0900_PRIORITY 0xfa860002 +#define F0900_DDEMOD 0xfa860001 + +/*LDPCERR1*/ +#define R0900_LDPCERR1 0xfa96 +#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff + +/*LDPCERR0*/ +#define R0900_LDPCERR0 0xfa97 +#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff + +/*BCHERR*/ +#define R0900_BCHERR 0xfa98 +#define F0900_ERRORFLAG 0xfa980010 +#define F0900_BCH_ERRORS_COUNTER 0xfa98000f + +/*TSTRES0*/ +#define R0900_TSTRES0 0xff11 +#define F0900_FRESFEC 0xff110080 + +/*P2_TCTL4*/ +#define R0900_P2_TCTL4 0xff28 +#define F0900_P2_PN4_SELECT 0xff280020 + +/*P1_TCTL4*/ +#define R0900_P1_TCTL4 0xff48 +#define TCTL4 shiftx(R0900_P1_TCTL4, demod, 0x20) +#define F0900_P1_PN4_SELECT 0xff480020 + +/*P2_TSTDISRX*/ +#define R0900_P2_TSTDISRX 0xff65 +#define F0900_P2_PIN_SELECT1 0xff650008 + +/*P1_TSTDISRX*/ +#define R0900_P1_TSTDISRX 0xff67 +#define TSTDISRX shiftx(R0900_P1_TSTDISRX, demod, 2) +#define F0900_P1_PIN_SELECT1 0xff670008 +#define PIN_SELECT1 shiftx(F0900_P1_PIN_SELECT1, demod, 0x20000) + +#define STV0900_NBREGS 723 +#define STV0900_NBFIELDS 1420 + +#endif + diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c new file mode 100644 index 0000000000..3ca52ba411 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0900_sw.c @@ -0,0 +1,2015 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * stv0900_sw.c + * + * Driver for ST STV0900 satellite demodulator IC. + * + * Copyright (C) ST Microelectronics. + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#include "stv0900.h" +#include "stv0900_reg.h" +#include "stv0900_priv.h" + +s32 shiftx(s32 x, int demod, s32 shift) +{ + if (demod == 1) + return x - shift; + + return x; +} + +int stv0900_check_signal_presence(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + s32 carr_offset, + agc2_integr, + max_carrier; + + int no_signal = FALSE; + + carr_offset = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); + carr_offset = ge2comp(carr_offset, 16); + agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); + max_carrier = intp->srch_range[demod] / 1000; + + max_carrier += (max_carrier / 10); + max_carrier = 65536 * (max_carrier / 2); + max_carrier /= intp->mclk / 1000; + if (max_carrier > 0x4000) + max_carrier = 0x4000; + + if ((agc2_integr > 0x2000) + || (carr_offset > (2 * max_carrier)) + || (carr_offset < (-2 * max_carrier))) + no_signal = TRUE; + + return no_signal; +} + +static void stv0900_get_sw_loop_params(struct stv0900_internal *intp, + s32 *frequency_inc, s32 *sw_timeout, + s32 *steps, + enum fe_stv0900_demod_num demod) +{ + s32 timeout, freq_inc, max_steps, srate, max_carrier; + + enum fe_stv0900_search_standard standard; + + srate = intp->symbol_rate[demod]; + max_carrier = intp->srch_range[demod] / 1000; + max_carrier += max_carrier / 10; + standard = intp->srch_standard[demod]; + + max_carrier = 65536 * (max_carrier / 2); + max_carrier /= intp->mclk / 1000; + + if (max_carrier > 0x4000) + max_carrier = 0x4000; + + freq_inc = srate; + freq_inc /= intp->mclk >> 10; + freq_inc = freq_inc << 6; + + switch (standard) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + freq_inc *= 3; + timeout = 20; + break; + case STV0900_SEARCH_DVBS2: + freq_inc *= 4; + timeout = 25; + break; + case STV0900_AUTO_SEARCH: + default: + freq_inc *= 3; + timeout = 25; + break; + } + + freq_inc /= 100; + + if ((freq_inc > max_carrier) || (freq_inc < 0)) + freq_inc = max_carrier / 2; + + timeout *= 27500; + + if (srate > 0) + timeout /= srate / 1000; + + if ((timeout > 100) || (timeout < 0)) + timeout = 100; + + max_steps = (max_carrier / freq_inc) + 1; + + if ((max_steps > 100) || (max_steps < 0)) { + max_steps = 100; + freq_inc = max_carrier / max_steps; + } + + *frequency_inc = freq_inc; + *sw_timeout = timeout; + *steps = max_steps; + +} + +static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp, + s32 FreqIncr, s32 Timeout, int zigzag, + s32 MaxStep, enum fe_stv0900_demod_num demod) +{ + int no_signal, + lock = FALSE; + s32 stepCpt, + freqOffset, + max_carrier; + + max_carrier = intp->srch_range[demod] / 1000; + max_carrier += (max_carrier / 10); + + max_carrier = 65536 * (max_carrier / 2); + max_carrier /= intp->mclk / 1000; + + if (max_carrier > 0x4000) + max_carrier = 0x4000; + + if (zigzag == TRUE) + freqOffset = 0; + else + freqOffset = -max_carrier + FreqIncr; + + stepCpt = 0; + + do { + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff); + stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x18); + stv0900_write_bits(intp, ALGOSWRST, 1); + + if (intp->chip_id == 0x12) { + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); + } + + if (zigzag == TRUE) { + if (freqOffset >= 0) + freqOffset = -freqOffset - 2 * FreqIncr; + else + freqOffset = -freqOffset; + } else + freqOffset += + 2 * FreqIncr; + + stepCpt++; + lock = stv0900_get_demod_lock(intp, demod, Timeout); + no_signal = stv0900_check_signal_presence(intp, demod); + + } while ((lock == FALSE) + && (no_signal == FALSE) + && ((freqOffset - FreqIncr) < max_carrier) + && ((freqOffset + FreqIncr) > -max_carrier) + && (stepCpt < MaxStep)); + + stv0900_write_bits(intp, ALGOSWRST, 0); + + return lock; +} + +static int stv0900_sw_algo(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + int lock = FALSE, + no_signal, + zigzag; + s32 s2fw, + fqc_inc, + sft_stp_tout, + trial_cntr, + max_steps; + + stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout, + &max_steps, demod); + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x3b); + else + stv0900_write_reg(intp, CARFREQ, 0xef); + + stv0900_write_reg(intp, DMDCFGMD, 0x49); + zigzag = FALSE; + break; + case STV0900_SEARCH_DVBS2: + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CORRELABS, 0x79); + else + stv0900_write_reg(intp, CORRELABS, 0x68); + + stv0900_write_reg(intp, DMDCFGMD, 0x89); + + zigzag = TRUE; + break; + case STV0900_AUTO_SEARCH: + default: + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x3b); + stv0900_write_reg(intp, CORRELABS, 0x79); + } else { + stv0900_write_reg(intp, CARFREQ, 0xef); + stv0900_write_reg(intp, CORRELABS, 0x68); + } + + stv0900_write_reg(intp, DMDCFGMD, 0xc9); + zigzag = FALSE; + break; + } + + trial_cntr = 0; + do { + lock = stv0900_search_carr_sw_loop(intp, + fqc_inc, + sft_stp_tout, + zigzag, + max_steps, + demod); + no_signal = stv0900_check_signal_presence(intp, demod); + trial_cntr++; + if ((lock == TRUE) + || (no_signal == TRUE) + || (trial_cntr == 2)) { + + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x49); + stv0900_write_reg(intp, CORRELABS, 0x9e); + } else { + stv0900_write_reg(intp, CARFREQ, 0xed); + stv0900_write_reg(intp, CORRELABS, 0x88); + } + + if ((stv0900_get_bits(intp, HEADER_MODE) == + STV0900_DVBS2_FOUND) && + (lock == TRUE)) { + msleep(sft_stp_tout); + s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT); + + if (s2fw < 0xd) { + msleep(sft_stp_tout); + s2fw = stv0900_get_bits(intp, + FLYWHEEL_CPT); + } + + if (s2fw < 0xd) { + lock = FALSE; + + if (trial_cntr < 2) { + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, + CORRELABS, + 0x79); + else + stv0900_write_reg(intp, + CORRELABS, + 0x68); + + stv0900_write_reg(intp, + DMDCFGMD, + 0x89); + } + } + } + } + + } while ((lock == FALSE) + && (trial_cntr < 2) + && (no_signal == FALSE)); + + return lock; +} + +static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp, + u32 mclk, + enum fe_stv0900_demod_num demod) +{ + s32 rem1, rem2, intval1, intval2, srate; + + srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) + + (stv0900_get_bits(intp, SYMB_FREQ2) << 16) + + (stv0900_get_bits(intp, SYMB_FREQ1) << 8) + + (stv0900_get_bits(intp, SYMB_FREQ0)); + dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n", + srate, stv0900_get_bits(intp, SYMB_FREQ0), + stv0900_get_bits(intp, SYMB_FREQ1), + stv0900_get_bits(intp, SYMB_FREQ2), + stv0900_get_bits(intp, SYMB_FREQ3)); + + intval1 = (mclk) >> 16; + intval2 = (srate) >> 16; + + rem1 = (mclk) % 0x10000; + rem2 = (srate) % 0x10000; + srate = (intval1 * intval2) + + ((intval1 * rem2) >> 16) + + ((intval2 * rem1) >> 16); + + return srate; +} + +static void stv0900_set_symbol_rate(struct stv0900_internal *intp, + u32 mclk, u32 srate, + enum fe_stv0900_demod_num demod) +{ + u32 symb; + + dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, + srate, demod); + + if (srate > 60000000) { + symb = srate << 4; + symb /= (mclk >> 12); + } else if (srate > 6000000) { + symb = srate << 6; + symb /= (mclk >> 10); + } else { + symb = srate << 9; + symb /= (mclk >> 7); + } + + stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f); + stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff)); +} + +static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp, + u32 mclk, u32 srate, + enum fe_stv0900_demod_num demod) +{ + u32 symb; + + srate = 105 * (srate / 100); + + if (srate > 60000000) { + symb = srate << 4; + symb /= (mclk >> 12); + } else if (srate > 6000000) { + symb = srate << 6; + symb /= (mclk >> 10); + } else { + symb = srate << 9; + symb /= (mclk >> 7); + } + + if (symb < 0x7fff) { + stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f); + stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff)); + } else { + stv0900_write_reg(intp, SFRUP1, 0x7f); + stv0900_write_reg(intp, SFRUP1 + 1, 0xff); + } +} + +static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp, + u32 mclk, u32 srate, + enum fe_stv0900_demod_num demod) +{ + u32 symb; + + srate = 95 * (srate / 100); + if (srate > 60000000) { + symb = srate << 4; + symb /= (mclk >> 12); + + } else if (srate > 6000000) { + symb = srate << 6; + symb /= (mclk >> 10); + + } else { + symb = srate << 9; + symb /= (mclk >> 7); + } + + stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff); + stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff)); +} + +static s32 stv0900_get_timing_offst(struct stv0900_internal *intp, + u32 srate, + enum fe_stv0900_demod_num demod) +{ + s32 timingoffset; + + + timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) + + (stv0900_read_reg(intp, TMGREG2 + 1) << 8) + + (stv0900_read_reg(intp, TMGREG2 + 2)); + + timingoffset = ge2comp(timingoffset, 24); + + + if (timingoffset == 0) + timingoffset = 1; + + timingoffset = ((s32)srate * 10) / ((s32)0x1000000 / timingoffset); + timingoffset /= 320; + + return timingoffset; +} + +static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + s32 rolloff; + + if (intp->chip_id == 0x10) { + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03; + stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff); + } else if (intp->chip_id <= 0x20) + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0); + else /* cut 3.0 */ + stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0); +} + +static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) +{ + u32 rolloff; + + switch (ro) { + case STV0900_20: + rolloff = 20; + break; + case STV0900_25: + rolloff = 25; + break; + case STV0900_35: + default: + rolloff = 35; + break; + } + + return srate + (srate * rolloff) / 100; +} + +static int stv0900_check_timing_lock(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + int timingLock = FALSE; + s32 i, + timingcpt = 0; + u8 car_freq, + tmg_th_high, + tmg_th_low; + + car_freq = stv0900_read_reg(intp, CARFREQ); + tmg_th_high = stv0900_read_reg(intp, TMGTHRISE); + tmg_th_low = stv0900_read_reg(intp, TMGTHFALL); + stv0900_write_reg(intp, TMGTHRISE, 0x20); + stv0900_write_reg(intp, TMGTHFALL, 0x0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, RTC, 0x80); + stv0900_write_reg(intp, RTCS2, 0x40); + stv0900_write_reg(intp, CARFREQ, 0x0); + stv0900_write_reg(intp, CFRINIT1, 0x0); + stv0900_write_reg(intp, CFRINIT0, 0x0); + stv0900_write_reg(intp, AGC2REF, 0x65); + stv0900_write_reg(intp, DMDISTATE, 0x18); + msleep(7); + + for (i = 0; i < 10; i++) { + if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) + timingcpt++; + + msleep(1); + } + + if (timingcpt >= 3) + timingLock = TRUE; + + stv0900_write_reg(intp, AGC2REF, 0x38); + stv0900_write_reg(intp, RTC, 0x88); + stv0900_write_reg(intp, RTCS2, 0x68); + stv0900_write_reg(intp, CARFREQ, car_freq); + stv0900_write_reg(intp, TMGTHRISE, tmg_th_high); + stv0900_write_reg(intp, TMGTHFALL, tmg_th_low); + + return timingLock; +} + +static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe, + s32 demod_timeout) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + int lock = FALSE, + d = demod; + s32 srate, + search_range, + locktimeout, + currier_step, + nb_steps, + current_step, + direction, + tuner_freq, + timeout, + freq; + + srate = intp->symbol_rate[d]; + search_range = intp->srch_range[d]; + + if (srate >= 10000000) + locktimeout = demod_timeout / 3; + else + locktimeout = demod_timeout / 2; + + lock = stv0900_get_demod_lock(intp, d, locktimeout); + + if (lock != FALSE) + return lock; + + if (srate >= 10000000) { + if (stv0900_check_timing_lock(intp, d) == TRUE) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + lock = stv0900_get_demod_lock(intp, d, demod_timeout); + } else + lock = FALSE; + + return lock; + } + + if (intp->chip_id <= 0x20) { + if (srate <= 1000000) + currier_step = 500; + else if (srate <= 4000000) + currier_step = 1000; + else if (srate <= 7000000) + currier_step = 2000; + else if (srate <= 10000000) + currier_step = 3000; + else + currier_step = 5000; + + if (srate >= 2000000) { + timeout = (demod_timeout / 3); + if (timeout > 1000) + timeout = 1000; + } else + timeout = (demod_timeout / 2); + } else { + /*cut 3.0 */ + currier_step = srate / 4000; + timeout = (demod_timeout * 3) / 4; + } + + nb_steps = ((search_range / 1000) / currier_step); + + if ((nb_steps % 2) != 0) + nb_steps += 1; + + if (nb_steps <= 0) + nb_steps = 2; + else if (nb_steps > 12) + nb_steps = 12; + + current_step = 1; + direction = 1; + + if (intp->chip_id <= 0x20) { + tuner_freq = intp->freq[d]; + intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d], + intp->rolloff) + intp->symbol_rate[d]; + } else + tuner_freq = 0; + + while ((current_step <= nb_steps) && (lock == FALSE)) { + if (direction > 0) + tuner_freq += (current_step * currier_step); + else + tuner_freq -= (current_step * currier_step); + + if (intp->chip_id <= 0x20) { + if (intp->tuner_type[d] == 3) + stv0900_set_tuner_auto(intp, tuner_freq, + intp->bw[d], demod); + else + stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); + + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, 0); + stv0900_write_reg(intp, CFRINIT0, 0); + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + } else { + stv0900_write_reg(intp, DMDISTATE, 0x1c); + freq = (tuner_freq * 65536) / (intp->mclk / 1000); + stv0900_write_bits(intp, CFR_INIT1, MSB(freq)); + stv0900_write_bits(intp, CFR_INIT0, LSB(freq)); + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x05); + } + + lock = stv0900_get_demod_lock(intp, d, timeout); + direction *= -1; + current_step++; + } + + return lock; +} + +static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, + s32 srate, + enum fe_stv0900_search_algo algo) +{ + switch (algo) { + case STV0900_BLIND_SEARCH: + if (srate <= 1500000) { + (*demod_timeout) = 1500; + (*fec_timeout) = 400; + } else if (srate <= 5000000) { + (*demod_timeout) = 1000; + (*fec_timeout) = 300; + } else { + (*demod_timeout) = 700; + (*fec_timeout) = 100; + } + + break; + case STV0900_COLD_START: + case STV0900_WARM_START: + default: + if (srate <= 1000000) { + (*demod_timeout) = 3000; + (*fec_timeout) = 1700; + } else if (srate <= 2000000) { + (*demod_timeout) = 2500; + (*fec_timeout) = 1100; + } else if (srate <= 5000000) { + (*demod_timeout) = 1000; + (*fec_timeout) = 550; + } else if (srate <= 10000000) { + (*demod_timeout) = 700; + (*fec_timeout) = 250; + } else if (srate <= 20000000) { + (*demod_timeout) = 400; + (*fec_timeout) = 130; + } else { + (*demod_timeout) = 300; + (*fec_timeout) = 100; + } + + break; + + } + + if (algo == STV0900_WARM_START) + (*demod_timeout) /= 2; +} + +static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + + s32 vth_reg = VTH12; + + dprintk("%s\n", __func__); + + stv0900_write_reg(intp, vth_reg++, 0xd0); + stv0900_write_reg(intp, vth_reg++, 0x7d); + stv0900_write_reg(intp, vth_reg++, 0x53); + stv0900_write_reg(intp, vth_reg++, 0x2f); + stv0900_write_reg(intp, vth_reg++, 0x24); + stv0900_write_reg(intp, vth_reg++, 0x1f); +} + +static void stv0900_set_viterbi_standard(struct stv0900_internal *intp, + enum fe_stv0900_search_standard standard, + enum fe_stv0900_fec fec, + enum fe_stv0900_demod_num demod) +{ + dprintk("%s: ViterbiStandard = ", __func__); + + switch (standard) { + case STV0900_AUTO_SEARCH: + dprintk("Auto\n"); + stv0900_write_reg(intp, FECM, 0x10); + stv0900_write_reg(intp, PRVIT, 0x3f); + break; + case STV0900_SEARCH_DVBS1: + dprintk("DVBS1\n"); + stv0900_write_reg(intp, FECM, 0x00); + switch (fec) { + case STV0900_FEC_UNKNOWN: + default: + stv0900_write_reg(intp, PRVIT, 0x2f); + break; + case STV0900_FEC_1_2: + stv0900_write_reg(intp, PRVIT, 0x01); + break; + case STV0900_FEC_2_3: + stv0900_write_reg(intp, PRVIT, 0x02); + break; + case STV0900_FEC_3_4: + stv0900_write_reg(intp, PRVIT, 0x04); + break; + case STV0900_FEC_5_6: + stv0900_write_reg(intp, PRVIT, 0x08); + break; + case STV0900_FEC_7_8: + stv0900_write_reg(intp, PRVIT, 0x20); + break; + } + + break; + case STV0900_SEARCH_DSS: + dprintk("DSS\n"); + stv0900_write_reg(intp, FECM, 0x80); + switch (fec) { + case STV0900_FEC_UNKNOWN: + default: + stv0900_write_reg(intp, PRVIT, 0x13); + break; + case STV0900_FEC_1_2: + stv0900_write_reg(intp, PRVIT, 0x01); + break; + case STV0900_FEC_2_3: + stv0900_write_reg(intp, PRVIT, 0x02); + break; + case STV0900_FEC_6_7: + stv0900_write_reg(intp, PRVIT, 0x10); + break; + } + break; + default: + break; + } +} + +static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + enum fe_stv0900_fec prate; + s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN); + + switch (rate_fld) { + case 13: + prate = STV0900_FEC_1_2; + break; + case 18: + prate = STV0900_FEC_2_3; + break; + case 21: + prate = STV0900_FEC_3_4; + break; + case 24: + prate = STV0900_FEC_5_6; + break; + case 25: + prate = STV0900_FEC_6_7; + break; + case 26: + prate = STV0900_FEC_7_8; + break; + default: + prate = STV0900_FEC_UNKNOWN; + break; + } + + return prate; +} + +static void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, + u32 srate) +{ + if (intp->chip_id >= 0x30) { + if (srate >= 15000000) { + stv0900_write_reg(intp, ACLC, 0x2b); + stv0900_write_reg(intp, BCLC, 0x1a); + } else if ((srate >= 7000000) && (15000000 > srate)) { + stv0900_write_reg(intp, ACLC, 0x0c); + stv0900_write_reg(intp, BCLC, 0x1b); + } else if (srate < 7000000) { + stv0900_write_reg(intp, ACLC, 0x2c); + stv0900_write_reg(intp, BCLC, 0x1c); + } + + } else { /*cut 2.0 and 1.x*/ + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + } + +} + +static void stv0900_track_optimization(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + s32 srate, + pilots, + aclc, + freq1, + freq0, + i = 0, + timed, + timef, + blind_tun_sw = 0, + modulation; + + enum fe_stv0900_modcode foundModcod; + + dprintk("%s\n", __func__); + + srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + srate += stv0900_get_timing_offst(intp, srate, demod); + + switch (intp->result[demod].standard) { + case STV0900_DVBS1_STANDARD: + case STV0900_DSS_STANDARD: + dprintk("%s: found DVB-S or DSS\n", __func__); + if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) { + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 0); + } + + stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff); + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + + if (intp->chip_id < 0x30) { + stv0900_write_reg(intp, ERRCTRL1, 0x75); + break; + } + + if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) { + stv0900_write_reg(intp, GAUSSR0, 0x98); + stv0900_write_reg(intp, CCIR0, 0x18); + } else { + stv0900_write_reg(intp, GAUSSR0, 0x18); + stv0900_write_reg(intp, CCIR0, 0x18); + } + + stv0900_write_reg(intp, ERRCTRL1, 0x75); + break; + case STV0900_DVBS2_STANDARD: + dprintk("%s: found DVB-S2\n", __func__); + stv0900_write_bits(intp, DVBS1_ENABLE, 0); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_reg(intp, ACLC, 0); + stv0900_write_reg(intp, BCLC, 0); + if (intp->result[demod].frame_len == STV0900_LONG_FRAME) { + foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD); + pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; + aclc = stv0900_get_optim_carr_loop(srate, + foundModcod, + pilots, + intp->chip_id); + if (foundModcod <= STV0900_QPSK_910) + stv0900_write_reg(intp, ACLC2S2Q, aclc); + else if (foundModcod <= STV0900_8PSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S28, aclc); + } + + if ((intp->demod_mode == STV0900_SINGLE) && + (foundModcod > STV0900_8PSK_910)) { + if (foundModcod <= STV0900_16APSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S216A, + aclc); + } else if (foundModcod <= STV0900_32APSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S232A, + aclc); + } + } + + } else { + modulation = intp->result[demod].modulation; + aclc = stv0900_get_optim_short_carr_loop(srate, + modulation, intp->chip_id); + if (modulation == STV0900_QPSK) + stv0900_write_reg(intp, ACLC2S2Q, aclc); + else if (modulation == STV0900_8PSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S28, aclc); + } else if (modulation == STV0900_16APSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S216A, aclc); + } else if (modulation == STV0900_32APSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S232A, aclc); + } + + } + + if (intp->chip_id <= 0x11) { + if (intp->demod_mode != STV0900_SINGLE) + stv0900_activate_s2_modcod(intp, demod); + + } + + stv0900_write_reg(intp, ERRCTRL1, 0x67); + break; + case STV0900_UNKNOWN_STANDARD: + default: + dprintk("%s: found unknown standard\n", __func__); + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + break; + } + + freq1 = stv0900_read_reg(intp, CFR2); + freq0 = stv0900_read_reg(intp, CFR1); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { + stv0900_write_reg(intp, SFRSTEP, 0x00); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, TMGCFG2, 0xc1); + stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); + blind_tun_sw = 1; + if (intp->result[demod].standard != STV0900_DVBS2_STANDARD) + stv0900_set_dvbs1_track_car_loop(intp, demod, srate); + + } + + if (intp->chip_id >= 0x20) { + if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || + (intp->srch_standard[demod] == + STV0900_SEARCH_DSS) || + (intp->srch_standard[demod] == + STV0900_AUTO_SEARCH)) { + stv0900_write_reg(intp, VAVSRVIT, 0x0a); + stv0900_write_reg(intp, VITSCALE, 0x0); + } + } + + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x08); + + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0x0a); + + stv0900_write_reg(intp, AGC2REF, 0x38); + + if ((intp->chip_id >= 0x20) || + (blind_tun_sw == 1) || + (intp->symbol_rate[demod] < 10000000)) { + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + intp->bw[demod] = stv0900_carrier_width(srate, + intp->rolloff) + 10000000; + + if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) { + if (intp->srch_algo[demod] != STV0900_WARM_START) { + if (intp->tuner_type[demod] == 3) + stv0900_set_tuner_auto(intp, + intp->freq[demod], + intp->bw[demod], + demod); + else + stv0900_set_bandwidth(fe, + intp->bw[demod]); + } + } + + if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) || + (intp->symbol_rate[demod] < 10000000)) + msleep(50); + else + msleep(5); + + stv0900_get_lock_timeout(&timed, &timef, srate, + STV0900_WARM_START); + + if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + i = 0; + while ((stv0900_get_demod_lock(intp, + demod, + timed / 2) == FALSE) && + (i <= 2)) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + i++; + } + } + + } + + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x49); + + if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) || + (intp->result[demod].standard == STV0900_DSS_STANDARD)) + stv0900_set_viterbi_tracq(intp, demod); + +} + +static int stv0900_get_fec_lock(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, s32 time_out) +{ + s32 timer = 0, lock = 0; + + enum fe_stv0900_search_state dmd_state; + + dprintk("%s\n", __func__); + + dmd_state = stv0900_get_bits(intp, HEADER_MODE); + + while ((timer < time_out) && (lock == 0)) { + switch (dmd_state) { + case STV0900_SEARCH: + case STV0900_PLH_DETECTED: + default: + lock = 0; + break; + case STV0900_DVBS2_FOUND: + lock = stv0900_get_bits(intp, PKTDELIN_LOCK); + break; + case STV0900_DVBS_FOUND: + lock = stv0900_get_bits(intp, LOCKEDVIT); + break; + } + + if (lock == 0) { + msleep(10); + timer += 10; + } + } + + if (lock) + dprintk("%s: DEMOD FEC LOCK OK\n", __func__); + else + dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__); + + return lock; +} + +static int stv0900_wait_for_lock(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, + s32 dmd_timeout, s32 fec_timeout) +{ + + s32 timer = 0, lock = 0; + + dprintk("%s\n", __func__); + + lock = stv0900_get_demod_lock(intp, demod, dmd_timeout); + + if (lock) + lock = stv0900_get_fec_lock(intp, demod, fec_timeout); + + if (lock) { + lock = 0; + + dprintk("%s: Timer = %d, time_out = %d\n", + __func__, timer, fec_timeout); + + while ((timer < fec_timeout) && (lock == 0)) { + lock = stv0900_get_bits(intp, TSFIFO_LINEOK); + msleep(1); + timer++; + } + } + + if (lock) + dprintk("%s: DEMOD LOCK OK\n", __func__); + else + dprintk("%s: DEMOD LOCK FAIL\n", __func__); + + if (lock) + return TRUE; + else + return FALSE; +} + +enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, + enum fe_stv0900_demod_num demod) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_tracking_standard fnd_standard; + + int hdr_mode = stv0900_get_bits(intp, HEADER_MODE); + + switch (hdr_mode) { + case 2: + fnd_standard = STV0900_DVBS2_STANDARD; + break; + case 3: + if (stv0900_get_bits(intp, DSS_DVB) == 1) + fnd_standard = STV0900_DSS_STANDARD; + else + fnd_standard = STV0900_DVBS1_STANDARD; + + break; + default: + fnd_standard = STV0900_UNKNOWN_STANDARD; + } + + dprintk("%s: standard %d\n", __func__, fnd_standard); + + return fnd_standard; +} + +static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk, + enum fe_stv0900_demod_num demod) +{ + s32 derot, + rem1, + rem2, + intval1, + intval2; + + derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) + + (stv0900_get_bits(intp, CAR_FREQ1) << 8) + + (stv0900_get_bits(intp, CAR_FREQ0)); + + derot = ge2comp(derot, 24); + intval1 = mclk >> 12; + intval2 = derot >> 12; + rem1 = mclk % 0x1000; + rem2 = derot % 0x1000; + derot = (intval1 * intval2) + + ((intval1 * rem2) >> 12) + + ((intval2 * rem1) >> 12); + + return derot; +} + +static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + u32 freq = 0; + + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; + + if (tuner_ops->get_frequency) { + if ((tuner_ops->get_frequency(fe, &freq)) < 0) + dprintk("%s: Invalid parameter\n", __func__); + else + dprintk("%s: Frequency=%d\n", __func__, freq); + + } + + return freq; +} + +static enum +fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE; + struct stv0900_signal_info *result = &intp->result[demod]; + s32 offsetFreq, + srate_offset; + int i = 0, + d = demod; + + u8 timing; + + msleep(5); + if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) { + timing = stv0900_read_reg(intp, TMGREG2); + i = 0; + stv0900_write_reg(intp, SFRSTEP, 0x5c); + + while ((i <= 50) && (timing != 0) && (timing != 0xff)) { + timing = stv0900_read_reg(intp, TMGREG2); + msleep(5); + i += 5; + } + } + + result->standard = stv0900_get_standard(fe, d); + if (intp->tuner_type[demod] == 3) + result->frequency = stv0900_get_freq_auto(intp, d); + else + result->frequency = stv0900_get_tuner_freq(fe); + + offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000; + result->frequency += offsetFreq; + result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d); + srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d); + result->symbol_rate += srate_offset; + result->fec = stv0900_get_vit_fec(intp, d); + result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD); + result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; + result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1; + result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); + + dprintk("%s: modcode=0x%x \n", __func__, result->modcode); + + switch (result->standard) { + case STV0900_DVBS2_STANDARD: + result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD); + if (result->modcode <= STV0900_QPSK_910) + result->modulation = STV0900_QPSK; + else if (result->modcode <= STV0900_8PSK_910) + result->modulation = STV0900_8PSK; + else if (result->modcode <= STV0900_16APSK_910) + result->modulation = STV0900_16APSK; + else if (result->modcode <= STV0900_32APSK_910) + result->modulation = STV0900_32APSK; + else + result->modulation = STV0900_UNKNOWN; + break; + case STV0900_DVBS1_STANDARD: + case STV0900_DSS_STANDARD: + result->spectrum = stv0900_get_bits(intp, IQINV); + result->modulation = STV0900_QPSK; + break; + default: + break; + } + + if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) || + (intp->symbol_rate[d] < 10000000)) { + offsetFreq = result->frequency - intp->freq[d]; + if (intp->tuner_type[demod] == 3) + intp->freq[d] = stv0900_get_freq_auto(intp, d); + else + intp->freq[d] = stv0900_get_tuner_freq(fe); + + if (abs(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + range = STV0900_RANGEOK; + else if (abs(offsetFreq) <= + (stv0900_carrier_width(result->symbol_rate, + result->rolloff) / 2000)) + range = STV0900_RANGEOK; + + } else if (abs(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + range = STV0900_RANGEOK; + + dprintk("%s: range %d\n", __func__, range); + + return range; +} + +static enum +fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + enum fe_stv0900_signal_type signal_type = STV0900_NODATA; + + s32 srate, + demod_timeout, + fec_timeout, + freq1, + freq0; + + intp->result[demod].locked = FALSE; + + if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) { + srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + srate += stv0900_get_timing_offst(intp, srate, demod); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) + stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); + + stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, + srate, STV0900_WARM_START); + freq1 = stv0900_read_reg(intp, CFR2); + freq0 = stv0900_read_reg(intp, CFR1); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_bits(intp, SPECINV_CONTROL, + STV0900_IQ_FORCE_SWAPPED); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + if (stv0900_wait_for_lock(intp, demod, + demod_timeout, fec_timeout) == TRUE) { + intp->result[demod].locked = TRUE; + signal_type = stv0900_get_signal_params(fe); + stv0900_track_optimization(fe); + } else { + stv0900_write_bits(intp, SPECINV_CONTROL, + STV0900_IQ_FORCE_NORMAL); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + if (stv0900_wait_for_lock(intp, demod, + demod_timeout, fec_timeout) == TRUE) { + intp->result[demod].locked = TRUE; + signal_type = stv0900_get_signal_params(fe); + stv0900_track_optimization(fe); + } + + } + + } else + intp->result[demod].locked = FALSE; + + return signal_type; +} + +static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + u32 minagc2level = 0xffff, + agc2level, + init_freq, freq_step; + + s32 i, j, nb_steps, direction; + + dprintk("%s\n", __func__); + + stv0900_write_reg(intp, AGC2REF, 0x38); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + + stv0900_write_bits(intp, AUTO_GUP, 1); + stv0900_write_bits(intp, AUTO_GLOW, 1); + + stv0900_write_reg(intp, DMDT0M, 0x0); + + stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); + nb_steps = -1 + (intp->srch_range[demod] / 1000000); + nb_steps /= 2; + nb_steps = (2 * nb_steps) + 1; + + if (nb_steps < 0) + nb_steps = 1; + + direction = 1; + + freq_step = (1000000 << 8) / (intp->mclk >> 8); + + init_freq = 0; + + for (i = 0; i < nb_steps; i++) { + if (direction > 0) + init_freq = init_freq + (freq_step * i); + else + init_freq = init_freq - (freq_step * i); + + direction *= -1; + stv0900_write_reg(intp, DMDISTATE, 0x5C); + stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff); + stv0900_write_reg(intp, CFRINIT0, init_freq & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x58); + msleep(10); + agc2level = 0; + + for (j = 0; j < 10; j++) + agc2level += (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); + + agc2level /= 10; + + if (agc2level < minagc2level) + minagc2level = agc2level; + + } + + return (u16)minagc2level; +} + +static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + int timing_lck = FALSE; + s32 i, timingcpt = 0, + direction = 1, + nb_steps, + current_step = 0, + tuner_freq; + u32 agc2_th, + coarse_srate = 0, + agc2_integr = 0, + currier_step = 1200; + + if (intp->chip_id >= 0x30) + agc2_th = 0x2e00; + else + agc2_th = 0x1f00; + + stv0900_write_bits(intp, DEMOD_MODE, 0x1f); + stv0900_write_reg(intp, TMGCFG, 0x12); + stv0900_write_reg(intp, TMGTHRISE, 0xf0); + stv0900_write_reg(intp, TMGTHFALL, 0xe0); + stv0900_write_bits(intp, SCAN_ENABLE, 1); + stv0900_write_bits(intp, CFR_AUTOSCAN, 1); + stv0900_write_reg(intp, SFRUP1, 0x83); + stv0900_write_reg(intp, SFRUP0, 0xc0); + stv0900_write_reg(intp, SFRLOW1, 0x82); + stv0900_write_reg(intp, SFRLOW0, 0xa0); + stv0900_write_reg(intp, DMDT0M, 0x0); + stv0900_write_reg(intp, AGC2REF, 0x50); + + if (intp->chip_id >= 0x30) { + stv0900_write_reg(intp, CARFREQ, 0x99); + stv0900_write_reg(intp, SFRSTEP, 0x98); + } else if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x6a); + stv0900_write_reg(intp, SFRSTEP, 0x95); + } else { + stv0900_write_reg(intp, CARFREQ, 0xed); + stv0900_write_reg(intp, SFRSTEP, 0x73); + } + + if (intp->symbol_rate[demod] <= 2000000) + currier_step = 1000; + else if (intp->symbol_rate[demod] <= 5000000) + currier_step = 2000; + else if (intp->symbol_rate[demod] <= 12000000) + currier_step = 3000; + else + currier_step = 5000; + + nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step); + nb_steps /= 2; + nb_steps = (2 * nb_steps) + 1; + + if (nb_steps < 0) + nb_steps = 1; + else if (nb_steps > 10) { + nb_steps = 11; + currier_step = (intp->srch_range[demod] / 1000) / 10; + } + + current_step = 0; + direction = 1; + + tuner_freq = intp->freq[demod]; + + while ((timing_lck == FALSE) && (current_step < nb_steps)) { + stv0900_write_reg(intp, DMDISTATE, 0x5f); + stv0900_write_bits(intp, DEMOD_MODE, 0); + + msleep(50); + + for (i = 0; i < 10; i++) { + if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) + timingcpt++; + + agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) | + stv0900_read_reg(intp, AGC2I0); + } + + agc2_integr /= 10; + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + current_step++; + direction *= -1; + + dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started. tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", + tuner_freq, agc2_integr, coarse_srate, timingcpt); + + if ((timingcpt >= 5) && + (agc2_integr < agc2_th) && + (coarse_srate < 55000000) && + (coarse_srate > 850000)) + timing_lck = TRUE; + else if (current_step < nb_steps) { + if (direction > 0) + tuner_freq += (current_step * currier_step); + else + tuner_freq -= (current_step * currier_step); + + if (intp->tuner_type[demod] == 3) + stv0900_set_tuner_auto(intp, tuner_freq, + intp->bw[demod], demod); + else + stv0900_set_tuner(fe, tuner_freq, + intp->bw[demod]); + } + } + + if (timing_lck == FALSE) + coarse_srate = 0; + else + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + + return coarse_srate; +} + +static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + u32 coarse_srate, + coarse_freq, + symb, + symbmax, + symbmin, + symbcomp; + + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + + if (coarse_srate > 3000000) { + symbmax = 13 * (coarse_srate / 10); + symbmax = (symbmax / 1000) * 65536; + symbmax /= (intp->mclk / 1000); + + symbmin = 10 * (coarse_srate / 13); + symbmin = (symbmin / 1000)*65536; + symbmin /= (intp->mclk / 1000); + + symb = (coarse_srate / 1000) * 65536; + symb /= (intp->mclk / 1000); + } else { + symbmax = 13 * (coarse_srate / 10); + symbmax = (symbmax / 100) * 65536; + symbmax /= (intp->mclk / 100); + + symbmin = 10 * (coarse_srate / 14); + symbmin = (symbmin / 100) * 65536; + symbmin /= (intp->mclk / 100); + + symb = (coarse_srate / 100) * 65536; + symb /= (intp->mclk / 100); + } + + symbcomp = 13 * (coarse_srate / 10); + coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); + + if (symbcomp < intp->symbol_rate[demod]) + coarse_srate = 0; + else { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, TMGCFG2, 0xc1); + stv0900_write_reg(intp, TMGTHRISE, 0x20); + stv0900_write_reg(intp, TMGTHFALL, 0x00); + stv0900_write_reg(intp, TMGCFG, 0xd2); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, AGC2REF, 0x38); + + if (intp->chip_id >= 0x30) + stv0900_write_reg(intp, CARFREQ, 0x79); + else if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x49); + else + stv0900_write_reg(intp, CARFREQ, 0xed); + + stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f); + stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff)); + + stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f); + stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff)); + + stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff); + stv0900_write_reg(intp, SFRINIT0, (symb & 0xff)); + + stv0900_write_reg(intp, DMDT0M, 0x20); + stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff); + stv0900_write_reg(intp, CFRINIT0, coarse_freq & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x15); + } + + return coarse_srate; +} + +static int stv0900_blind_search_algo(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + u8 k_ref_tmg, + k_ref_tmg_max, + k_ref_tmg_min; + u32 coarse_srate, + agc2_th; + int lock = FALSE, + coarse_fail = FALSE; + s32 demod_timeout = 500, + fec_timeout = 50, + fail_cpt, + i, + agc2_overflow; + u16 agc2_int; + u8 dstatus2; + + dprintk("%s\n", __func__); + + if (intp->chip_id < 0x20) { + k_ref_tmg_max = 233; + k_ref_tmg_min = 143; + } else { + k_ref_tmg_max = 110; + k_ref_tmg_min = 10; + } + + if (intp->chip_id <= 0x20) + agc2_th = STV0900_BLIND_SEARCH_AGC2_TH; + else + agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30; + + agc2_int = stv0900_blind_check_agc2_min_level(intp, demod); + + dprintk("%s agc2_int=%d agc2_th=%d \n", __func__, agc2_int, agc2_th); + if (agc2_int > agc2_th) + return FALSE; + + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0xaa); + + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x55); + else + stv0900_write_reg(intp, CARHDR, 0x20); + + if (intp->chip_id <= 0x20) + stv0900_write_reg(intp, CARCFG, 0xc4); + else + stv0900_write_reg(intp, CARCFG, 0x6); + + stv0900_write_reg(intp, RTCS2, 0x44); + + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, EQUALCFG, 0x41); + stv0900_write_reg(intp, FFECFG, 0x41); + stv0900_write_reg(intp, VITSCALE, 0x82); + stv0900_write_reg(intp, VAVSRVIT, 0x0); + } + + k_ref_tmg = k_ref_tmg_max; + + do { + stv0900_write_reg(intp, KREFTMG, k_ref_tmg); + if (stv0900_search_srate_coarse(fe) != 0) { + coarse_srate = stv0900_search_srate_fine(fe); + + if (coarse_srate != 0) { + stv0900_get_lock_timeout(&demod_timeout, + &fec_timeout, + coarse_srate, + STV0900_BLIND_SEARCH); + lock = stv0900_get_demod_lock(intp, + demod, + demod_timeout); + } else + lock = FALSE; + } else { + fail_cpt = 0; + agc2_overflow = 0; + + for (i = 0; i < 10; i++) { + agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); + + if (agc2_int >= 0xff00) + agc2_overflow++; + + dstatus2 = stv0900_read_reg(intp, DSTATUS2); + + if (((dstatus2 & 0x1) == 0x1) && + ((dstatus2 >> 7) == 1)) + fail_cpt++; + } + + if ((fail_cpt > 7) || (agc2_overflow > 7)) + coarse_fail = TRUE; + + lock = FALSE; + } + k_ref_tmg -= 30; + } while ((k_ref_tmg >= k_ref_tmg_min) && + (lock == FALSE) && + (coarse_fail == FALSE)); + + return lock; +} + +static void stv0900_set_viterbi_acq(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + s32 vth_reg = VTH12; + + dprintk("%s\n", __func__); + + stv0900_write_reg(intp, vth_reg++, 0x96); + stv0900_write_reg(intp, vth_reg++, 0x64); + stv0900_write_reg(intp, vth_reg++, 0x36); + stv0900_write_reg(intp, vth_reg++, 0x23); + stv0900_write_reg(intp, vth_reg++, 0x1e); + stv0900_write_reg(intp, vth_reg++, 0x19); +} + +static void stv0900_set_search_standard(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) +{ + + dprintk("%s\n", __func__); + + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + dprintk("Search Standard = DVBS1\n"); + break; + case STV0900_SEARCH_DSS: + dprintk("Search Standard = DSS\n"); + break; + case STV0900_SEARCH_DVBS2: + dprintk("Search Standard = DVBS2\n"); + break; + case STV0900_AUTO_SEARCH: + default: + dprintk("Search Standard = AUTO\n"); + break; + } + + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 0); + stv0900_write_bits(intp, STOP_CLKVIT, 0); + stv0900_set_dvbs1_track_car_loop(intp, + demod, + intp->symbol_rate[demod]); + stv0900_write_reg(intp, CAR2CFG, 0x22); + + stv0900_set_viterbi_acq(intp, demod); + stv0900_set_viterbi_standard(intp, + intp->srch_standard[demod], + intp->fec[demod], demod); + + break; + case STV0900_SEARCH_DVBS2: + stv0900_write_bits(intp, DVBS1_ENABLE, 0); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_bits(intp, STOP_CLKVIT, 1); + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ + stv0900_write_reg(intp, CAR2CFG, 0x26); + else + stv0900_write_reg(intp, CAR2CFG, 0x66); + + if (intp->demod_mode != STV0900_SINGLE) { + if (intp->chip_id <= 0x11) + stv0900_stop_all_s2_modcod(intp, demod); + else + stv0900_activate_s2_modcod(intp, demod); + + } else + stv0900_activate_s2_modcod_single(intp, demod); + + stv0900_set_viterbi_tracq(intp, demod); + + break; + case STV0900_AUTO_SEARCH: + default: + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_bits(intp, STOP_CLKVIT, 0); + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + stv0900_set_dvbs1_track_car_loop(intp, + demod, + intp->symbol_rate[demod]); + if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ + stv0900_write_reg(intp, CAR2CFG, 0x26); + else + stv0900_write_reg(intp, CAR2CFG, 0x66); + + if (intp->demod_mode != STV0900_SINGLE) { + if (intp->chip_id <= 0x11) + stv0900_stop_all_s2_modcod(intp, demod); + else + stv0900_activate_s2_modcod(intp, demod); + + } else + stv0900_activate_s2_modcod_single(intp, demod); + + stv0900_set_viterbi_tracq(intp, demod); + stv0900_set_viterbi_standard(intp, + intp->srch_standard[demod], + intp->fec[demod], demod); + + break; + } +} + +enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + + s32 demod_timeout = 500, fec_timeout = 50; + s32 aq_power, agc1_power, i; + + int lock = FALSE, low_sr = FALSE; + + enum fe_stv0900_signal_type signal_type = STV0900_NOCARRIER; + enum fe_stv0900_search_algo algo; + int no_signal = FALSE; + + dprintk("%s\n", __func__); + + algo = intp->srch_algo[demod]; + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_reg(intp, DMDISTATE, 0x5c); + if (intp->chip_id >= 0x20) { + if (intp->symbol_rate[demod] > 5000000) + stv0900_write_reg(intp, CORRELABS, 0x9e); + else + stv0900_write_reg(intp, CORRELABS, 0x82); + } else + stv0900_write_reg(intp, CORRELABS, 0x88); + + stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, + intp->symbol_rate[demod], + intp->srch_algo[demod]); + + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { + intp->bw[demod] = 2 * 36000000; + + stv0900_write_reg(intp, TMGCFG2, 0xc0); + stv0900_write_reg(intp, CORRELMANT, 0x70); + + stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); + } else { + stv0900_write_reg(intp, DMDT0M, 0x20); + stv0900_write_reg(intp, TMGCFG, 0xd2); + + if (intp->symbol_rate[demod] < 2000000) + stv0900_write_reg(intp, CORRELMANT, 0x63); + else + stv0900_write_reg(intp, CORRELMANT, 0x70); + + stv0900_write_reg(intp, AGC2REF, 0x38); + + intp->bw[demod] = + stv0900_carrier_width(intp->symbol_rate[demod], + intp->rolloff); + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, KREFTMG, 0x5a); + + if (intp->srch_algo[demod] == STV0900_COLD_START) { + intp->bw[demod] += 10000000; + intp->bw[demod] *= 15; + intp->bw[demod] /= 10; + } else if (intp->srch_algo[demod] == STV0900_WARM_START) + intp->bw[demod] += 10000000; + + } else { + stv0900_write_reg(intp, KREFTMG, 0xc1); + intp->bw[demod] += 10000000; + intp->bw[demod] *= 15; + intp->bw[demod] /= 10; + } + + stv0900_write_reg(intp, TMGCFG2, 0xc1); + + stv0900_set_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + stv0900_set_max_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + stv0900_set_min_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + if (intp->symbol_rate[demod] >= 10000000) + low_sr = FALSE; + else + low_sr = TRUE; + + } + + if (intp->tuner_type[demod] == 3) + stv0900_set_tuner_auto(intp, intp->freq[demod], + intp->bw[demod], demod); + else + stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); + + agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), + stv0900_get_bits(intp, AGCIQ_VALUE0)); + + aq_power = 0; + + if (agc1_power == 0) { + for (i = 0; i < 5; i++) + aq_power += (stv0900_get_bits(intp, POWER_I) + + stv0900_get_bits(intp, POWER_Q)) / 2; + + aq_power /= 5; + } + + if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) { + intp->result[demod].locked = FALSE; + signal_type = STV0900_NOAGC1; + dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__); + } else { + stv0900_write_bits(intp, SPECINV_CONTROL, + intp->srch_iq_inv[demod]); + if (intp->chip_id <= 0x20) /*cut 2.0*/ + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + else /*cut 3.0*/ + stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1); + + stv0900_set_search_standard(intp, demod); + + if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH) + stv0900_start_search(intp, demod); + } + + if (signal_type == STV0900_NOAGC1) + return signal_type; + + if (intp->chip_id == 0x12) { + stv0900_write_bits(intp, RST_HWARE, 0); + msleep(3); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); + } + + if (algo == STV0900_BLIND_SEARCH) + lock = stv0900_blind_search_algo(fe); + else if (algo == STV0900_COLD_START) + lock = stv0900_get_demod_cold_lock(fe, demod_timeout); + else if (algo == STV0900_WARM_START) + lock = stv0900_get_demod_lock(intp, demod, demod_timeout); + + if ((lock == FALSE) && (algo == STV0900_COLD_START)) { + if (low_sr == FALSE) { + if (stv0900_check_timing_lock(intp, demod) == TRUE) + lock = stv0900_sw_algo(intp, demod); + } + } + + if (lock == TRUE) + signal_type = stv0900_get_signal_params(fe); + + if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) { + stv0900_track_optimization(fe); + if (intp->chip_id <= 0x11) { + if ((stv0900_get_standard(fe, 0) == + STV0900_DVBS1_STANDARD) && + (stv0900_get_standard(fe, 1) == + STV0900_DVBS1_STANDARD)) { + msleep(20); + stv0900_write_bits(intp, RST_HWARE, 0); + } else { + stv0900_write_bits(intp, RST_HWARE, 0); + msleep(3); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); + } + + } else if (intp->chip_id >= 0x20) { + stv0900_write_bits(intp, RST_HWARE, 0); + msleep(3); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); + } + + if (stv0900_wait_for_lock(intp, demod, + fec_timeout, fec_timeout) == TRUE) { + lock = TRUE; + intp->result[demod].locked = TRUE; + if (intp->result[demod].standard == + STV0900_DVBS2_STANDARD) { + stv0900_set_dvbs2_rolloff(intp, demod); + stv0900_write_bits(intp, RESET_UPKO_COUNT, 1); + stv0900_write_bits(intp, RESET_UPKO_COUNT, 0); + stv0900_write_reg(intp, ERRCTRL1, 0x67); + } else { + stv0900_write_reg(intp, ERRCTRL1, 0x75); + } + + stv0900_write_reg(intp, FBERCPT4, 0); + stv0900_write_reg(intp, ERRCTRL2, 0xc1); + } else { + lock = FALSE; + signal_type = STV0900_NODATA; + no_signal = stv0900_check_signal_presence(intp, demod); + + intp->result[demod].locked = FALSE; + } + } + + if ((signal_type != STV0900_NODATA) || (no_signal != FALSE)) + return signal_type; + + if (intp->chip_id > 0x11) { + intp->result[demod].locked = FALSE; + return signal_type; + } + + if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) && + (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST)) + signal_type = stv0900_dvbs1_acq_workaround(fe); + + return signal_type; +} + diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c new file mode 100644 index 0000000000..cc45139057 --- /dev/null +++ b/drivers/media/dvb-frontends/stv090x.c @@ -0,0 +1,5097 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + STV0900/0903 Multistandard Broadcast Frontend driver + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/mutex.h> + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +#include "stv6110x.h" /* for demodulator internal modes */ + +#include "stv090x_reg.h" +#include "stv090x.h" +#include "stv090x_priv.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +static unsigned int verbose; +module_param(verbose, int, 0644); + +/* internal params node */ +struct stv090x_dev { + /* pointer for internal params, one for each pair of demods */ + struct stv090x_internal *internal; + struct stv090x_dev *next_dev; +}; + +/* first internal params */ +static struct stv090x_dev *stv090x_first_dev; + +/* find chip by i2c adapter and i2c address */ +static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap, + u8 i2c_addr) +{ + struct stv090x_dev *temp_dev = stv090x_first_dev; + + /* + Search of the last stv0900 chip or + find it by i2c adapter and i2c address */ + while ((temp_dev != NULL) && + ((temp_dev->internal->i2c_adap != i2c_adap) || + (temp_dev->internal->i2c_addr != i2c_addr))) { + + temp_dev = temp_dev->next_dev; + } + + return temp_dev; +} + +/* deallocating chip */ +static void remove_dev(struct stv090x_internal *internal) +{ + struct stv090x_dev *prev_dev = stv090x_first_dev; + struct stv090x_dev *del_dev = find_dev(internal->i2c_adap, + internal->i2c_addr); + + if (del_dev != NULL) { + if (del_dev == stv090x_first_dev) { + stv090x_first_dev = del_dev->next_dev; + } else { + while (prev_dev->next_dev != del_dev) + prev_dev = prev_dev->next_dev; + + prev_dev->next_dev = del_dev->next_dev; + } + + kfree(del_dev); + } +} + +/* allocating new chip */ +static struct stv090x_dev *append_internal(struct stv090x_internal *internal) +{ + struct stv090x_dev *new_dev; + struct stv090x_dev *temp_dev; + + new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL); + if (new_dev != NULL) { + new_dev->internal = internal; + new_dev->next_dev = NULL; + + /* append to list */ + if (stv090x_first_dev == NULL) { + stv090x_first_dev = new_dev; + } else { + temp_dev = stv090x_first_dev; + while (temp_dev->next_dev != NULL) + temp_dev = temp_dev->next_dev; + + temp_dev->next_dev = new_dev; + } + } + + return new_dev; +} + + +/* DVBS1 and DSS C/N Lookup table */ +static const struct stv090x_tab stv090x_s1cn_tab[] = { + { 0, 8917 }, /* 0.0dB */ + { 5, 8801 }, /* 0.5dB */ + { 10, 8667 }, /* 1.0dB */ + { 15, 8522 }, /* 1.5dB */ + { 20, 8355 }, /* 2.0dB */ + { 25, 8175 }, /* 2.5dB */ + { 30, 7979 }, /* 3.0dB */ + { 35, 7763 }, /* 3.5dB */ + { 40, 7530 }, /* 4.0dB */ + { 45, 7282 }, /* 4.5dB */ + { 50, 7026 }, /* 5.0dB */ + { 55, 6781 }, /* 5.5dB */ + { 60, 6514 }, /* 6.0dB */ + { 65, 6241 }, /* 6.5dB */ + { 70, 5965 }, /* 7.0dB */ + { 75, 5690 }, /* 7.5dB */ + { 80, 5424 }, /* 8.0dB */ + { 85, 5161 }, /* 8.5dB */ + { 90, 4902 }, /* 9.0dB */ + { 95, 4654 }, /* 9.5dB */ + { 100, 4417 }, /* 10.0dB */ + { 105, 4186 }, /* 10.5dB */ + { 110, 3968 }, /* 11.0dB */ + { 115, 3757 }, /* 11.5dB */ + { 120, 3558 }, /* 12.0dB */ + { 125, 3366 }, /* 12.5dB */ + { 130, 3185 }, /* 13.0dB */ + { 135, 3012 }, /* 13.5dB */ + { 140, 2850 }, /* 14.0dB */ + { 145, 2698 }, /* 14.5dB */ + { 150, 2550 }, /* 15.0dB */ + { 160, 2283 }, /* 16.0dB */ + { 170, 2042 }, /* 17.0dB */ + { 180, 1827 }, /* 18.0dB */ + { 190, 1636 }, /* 19.0dB */ + { 200, 1466 }, /* 20.0dB */ + { 210, 1315 }, /* 21.0dB */ + { 220, 1181 }, /* 22.0dB */ + { 230, 1064 }, /* 23.0dB */ + { 240, 960 }, /* 24.0dB */ + { 250, 869 }, /* 25.0dB */ + { 260, 792 }, /* 26.0dB */ + { 270, 724 }, /* 27.0dB */ + { 280, 665 }, /* 28.0dB */ + { 290, 616 }, /* 29.0dB */ + { 300, 573 }, /* 30.0dB */ + { 310, 537 }, /* 31.0dB */ + { 320, 507 }, /* 32.0dB */ + { 330, 483 }, /* 33.0dB */ + { 400, 398 }, /* 40.0dB */ + { 450, 381 }, /* 45.0dB */ + { 500, 377 } /* 50.0dB */ +}; + +/* DVBS2 C/N Lookup table */ +static const struct stv090x_tab stv090x_s2cn_tab[] = { + { -30, 13348 }, /* -3.0dB */ + { -20, 12640 }, /* -2d.0B */ + { -10, 11883 }, /* -1.0dB */ + { 0, 11101 }, /* -0.0dB */ + { 5, 10718 }, /* 0.5dB */ + { 10, 10339 }, /* 1.0dB */ + { 15, 9947 }, /* 1.5dB */ + { 20, 9552 }, /* 2.0dB */ + { 25, 9183 }, /* 2.5dB */ + { 30, 8799 }, /* 3.0dB */ + { 35, 8422 }, /* 3.5dB */ + { 40, 8062 }, /* 4.0dB */ + { 45, 7707 }, /* 4.5dB */ + { 50, 7353 }, /* 5.0dB */ + { 55, 7025 }, /* 5.5dB */ + { 60, 6684 }, /* 6.0dB */ + { 65, 6331 }, /* 6.5dB */ + { 70, 6036 }, /* 7.0dB */ + { 75, 5727 }, /* 7.5dB */ + { 80, 5437 }, /* 8.0dB */ + { 85, 5164 }, /* 8.5dB */ + { 90, 4902 }, /* 9.0dB */ + { 95, 4653 }, /* 9.5dB */ + { 100, 4408 }, /* 10.0dB */ + { 105, 4187 }, /* 10.5dB */ + { 110, 3961 }, /* 11.0dB */ + { 115, 3751 }, /* 11.5dB */ + { 120, 3558 }, /* 12.0dB */ + { 125, 3368 }, /* 12.5dB */ + { 130, 3191 }, /* 13.0dB */ + { 135, 3017 }, /* 13.5dB */ + { 140, 2862 }, /* 14.0dB */ + { 145, 2710 }, /* 14.5dB */ + { 150, 2565 }, /* 15.0dB */ + { 160, 2300 }, /* 16.0dB */ + { 170, 2058 }, /* 17.0dB */ + { 180, 1849 }, /* 18.0dB */ + { 190, 1663 }, /* 19.0dB */ + { 200, 1495 }, /* 20.0dB */ + { 210, 1349 }, /* 21.0dB */ + { 220, 1222 }, /* 22.0dB */ + { 230, 1110 }, /* 23.0dB */ + { 240, 1011 }, /* 24.0dB */ + { 250, 925 }, /* 25.0dB */ + { 260, 853 }, /* 26.0dB */ + { 270, 789 }, /* 27.0dB */ + { 280, 734 }, /* 28.0dB */ + { 290, 690 }, /* 29.0dB */ + { 300, 650 }, /* 30.0dB */ + { 310, 619 }, /* 31.0dB */ + { 320, 593 }, /* 32.0dB */ + { 330, 571 }, /* 33.0dB */ + { 400, 498 }, /* 40.0dB */ + { 450, 484 }, /* 45.0dB */ + { 500, 481 } /* 50.0dB */ +}; + +/* RF level C/N lookup table */ +static const struct stv090x_tab stv090x_rf_tab[] = { + { -5, 0xcaa1 }, /* -5dBm */ + { -10, 0xc229 }, /* -10dBm */ + { -15, 0xbb08 }, /* -15dBm */ + { -20, 0xb4bc }, /* -20dBm */ + { -25, 0xad5a }, /* -25dBm */ + { -30, 0xa298 }, /* -30dBm */ + { -35, 0x98a8 }, /* -35dBm */ + { -40, 0x8389 }, /* -40dBm */ + { -45, 0x59be }, /* -45dBm */ + { -50, 0x3a14 }, /* -50dBm */ + { -55, 0x2d11 }, /* -55dBm */ + { -60, 0x210d }, /* -60dBm */ + { -65, 0xa14f }, /* -65dBm */ + { -70, 0x07aa } /* -70dBm */ +}; + + +static struct stv090x_reg stv0900_initval[] = { + + { STV090x_OUTCFG, 0x00 }, + { STV090x_MODECFG, 0xff }, + { STV090x_AGCRF1CFG, 0x11 }, + { STV090x_AGCRF2CFG, 0x13 }, + { STV090x_TSGENERAL1X, 0x14 }, + { STV090x_TSTTNR2, 0x21 }, + { STV090x_TSTTNR4, 0x21 }, + { STV090x_P2_DISTXCTL, 0x22 }, + { STV090x_P2_F22TX, 0xc0 }, + { STV090x_P2_F22RX, 0xc0 }, + { STV090x_P2_DISRXCTL, 0x00 }, + { STV090x_P2_DMDCFGMD, 0xF9 }, + { STV090x_P2_DEMOD, 0x08 }, + { STV090x_P2_DMDCFG3, 0xc4 }, + { STV090x_P2_CARFREQ, 0xed }, + { STV090x_P2_LDT, 0xd0 }, + { STV090x_P2_LDT2, 0xb8 }, + { STV090x_P2_TMGCFG, 0xd2 }, + { STV090x_P2_TMGTHRISE, 0x20 }, + { STV090x_P1_TMGCFG, 0xd2 }, + + { STV090x_P2_TMGTHFALL, 0x00 }, + { STV090x_P2_FECSPY, 0x88 }, + { STV090x_P2_FSPYDATA, 0x3a }, + { STV090x_P2_FBERCPT4, 0x00 }, + { STV090x_P2_FSPYBER, 0x10 }, + { STV090x_P2_ERRCTRL1, 0x35 }, + { STV090x_P2_ERRCTRL2, 0xc1 }, + { STV090x_P2_CFRICFG, 0xf8 }, + { STV090x_P2_NOSCFG, 0x1c }, + { STV090x_P2_DMDTOM, 0x20 }, + { STV090x_P2_CORRELMANT, 0x70 }, + { STV090x_P2_CORRELABS, 0x88 }, + { STV090x_P2_AGC2O, 0x5b }, + { STV090x_P2_AGC2REF, 0x38 }, + { STV090x_P2_CARCFG, 0xe4 }, + { STV090x_P2_ACLC, 0x1A }, + { STV090x_P2_BCLC, 0x09 }, + { STV090x_P2_CARHDR, 0x08 }, + { STV090x_P2_KREFTMG, 0xc1 }, + { STV090x_P2_SFRUPRATIO, 0xf0 }, + { STV090x_P2_SFRLOWRATIO, 0x70 }, + { STV090x_P2_SFRSTEP, 0x58 }, + { STV090x_P2_TMGCFG2, 0x01 }, + { STV090x_P2_CAR2CFG, 0x26 }, + { STV090x_P2_BCLC2S2Q, 0x86 }, + { STV090x_P2_BCLC2S28, 0x86 }, + { STV090x_P2_SMAPCOEF7, 0x77 }, + { STV090x_P2_SMAPCOEF6, 0x85 }, + { STV090x_P2_SMAPCOEF5, 0x77 }, + { STV090x_P2_TSCFGL, 0x20 }, + { STV090x_P2_DMDCFG2, 0x3b }, + { STV090x_P2_MODCODLST0, 0xff }, + { STV090x_P2_MODCODLST1, 0xff }, + { STV090x_P2_MODCODLST2, 0xff }, + { STV090x_P2_MODCODLST3, 0xff }, + { STV090x_P2_MODCODLST4, 0xff }, + { STV090x_P2_MODCODLST5, 0xff }, + { STV090x_P2_MODCODLST6, 0xff }, + { STV090x_P2_MODCODLST7, 0xcc }, + { STV090x_P2_MODCODLST8, 0xcc }, + { STV090x_P2_MODCODLST9, 0xcc }, + { STV090x_P2_MODCODLSTA, 0xcc }, + { STV090x_P2_MODCODLSTB, 0xcc }, + { STV090x_P2_MODCODLSTC, 0xcc }, + { STV090x_P2_MODCODLSTD, 0xcc }, + { STV090x_P2_MODCODLSTE, 0xcc }, + { STV090x_P2_MODCODLSTF, 0xcf }, + { STV090x_P1_DISTXCTL, 0x22 }, + { STV090x_P1_F22TX, 0xc0 }, + { STV090x_P1_F22RX, 0xc0 }, + { STV090x_P1_DISRXCTL, 0x00 }, + { STV090x_P1_DMDCFGMD, 0xf9 }, + { STV090x_P1_DEMOD, 0x08 }, + { STV090x_P1_DMDCFG3, 0xc4 }, + { STV090x_P1_DMDTOM, 0x20 }, + { STV090x_P1_CARFREQ, 0xed }, + { STV090x_P1_LDT, 0xd0 }, + { STV090x_P1_LDT2, 0xb8 }, + { STV090x_P1_TMGCFG, 0xd2 }, + { STV090x_P1_TMGTHRISE, 0x20 }, + { STV090x_P1_TMGTHFALL, 0x00 }, + { STV090x_P1_SFRUPRATIO, 0xf0 }, + { STV090x_P1_SFRLOWRATIO, 0x70 }, + { STV090x_P1_TSCFGL, 0x20 }, + { STV090x_P1_FECSPY, 0x88 }, + { STV090x_P1_FSPYDATA, 0x3a }, + { STV090x_P1_FBERCPT4, 0x00 }, + { STV090x_P1_FSPYBER, 0x10 }, + { STV090x_P1_ERRCTRL1, 0x35 }, + { STV090x_P1_ERRCTRL2, 0xc1 }, + { STV090x_P1_CFRICFG, 0xf8 }, + { STV090x_P1_NOSCFG, 0x1c }, + { STV090x_P1_CORRELMANT, 0x70 }, + { STV090x_P1_CORRELABS, 0x88 }, + { STV090x_P1_AGC2O, 0x5b }, + { STV090x_P1_AGC2REF, 0x38 }, + { STV090x_P1_CARCFG, 0xe4 }, + { STV090x_P1_ACLC, 0x1A }, + { STV090x_P1_BCLC, 0x09 }, + { STV090x_P1_CARHDR, 0x08 }, + { STV090x_P1_KREFTMG, 0xc1 }, + { STV090x_P1_SFRSTEP, 0x58 }, + { STV090x_P1_TMGCFG2, 0x01 }, + { STV090x_P1_CAR2CFG, 0x26 }, + { STV090x_P1_BCLC2S2Q, 0x86 }, + { STV090x_P1_BCLC2S28, 0x86 }, + { STV090x_P1_SMAPCOEF7, 0x77 }, + { STV090x_P1_SMAPCOEF6, 0x85 }, + { STV090x_P1_SMAPCOEF5, 0x77 }, + { STV090x_P1_DMDCFG2, 0x3b }, + { STV090x_P1_MODCODLST0, 0xff }, + { STV090x_P1_MODCODLST1, 0xff }, + { STV090x_P1_MODCODLST2, 0xff }, + { STV090x_P1_MODCODLST3, 0xff }, + { STV090x_P1_MODCODLST4, 0xff }, + { STV090x_P1_MODCODLST5, 0xff }, + { STV090x_P1_MODCODLST6, 0xff }, + { STV090x_P1_MODCODLST7, 0xcc }, + { STV090x_P1_MODCODLST8, 0xcc }, + { STV090x_P1_MODCODLST9, 0xcc }, + { STV090x_P1_MODCODLSTA, 0xcc }, + { STV090x_P1_MODCODLSTB, 0xcc }, + { STV090x_P1_MODCODLSTC, 0xcc }, + { STV090x_P1_MODCODLSTD, 0xcc }, + { STV090x_P1_MODCODLSTE, 0xcc }, + { STV090x_P1_MODCODLSTF, 0xcf }, + { STV090x_GENCFG, 0x1d }, + { STV090x_NBITER_NF4, 0x37 }, + { STV090x_NBITER_NF5, 0x29 }, + { STV090x_NBITER_NF6, 0x37 }, + { STV090x_NBITER_NF7, 0x33 }, + { STV090x_NBITER_NF8, 0x31 }, + { STV090x_NBITER_NF9, 0x2f }, + { STV090x_NBITER_NF10, 0x39 }, + { STV090x_NBITER_NF11, 0x3a }, + { STV090x_NBITER_NF12, 0x29 }, + { STV090x_NBITER_NF13, 0x37 }, + { STV090x_NBITER_NF14, 0x33 }, + { STV090x_NBITER_NF15, 0x2f }, + { STV090x_NBITER_NF16, 0x39 }, + { STV090x_NBITER_NF17, 0x3a }, + { STV090x_NBITERNOERR, 0x04 }, + { STV090x_GAINLLR_NF4, 0x0C }, + { STV090x_GAINLLR_NF5, 0x0F }, + { STV090x_GAINLLR_NF6, 0x11 }, + { STV090x_GAINLLR_NF7, 0x14 }, + { STV090x_GAINLLR_NF8, 0x17 }, + { STV090x_GAINLLR_NF9, 0x19 }, + { STV090x_GAINLLR_NF10, 0x20 }, + { STV090x_GAINLLR_NF11, 0x21 }, + { STV090x_GAINLLR_NF12, 0x0D }, + { STV090x_GAINLLR_NF13, 0x0F }, + { STV090x_GAINLLR_NF14, 0x13 }, + { STV090x_GAINLLR_NF15, 0x1A }, + { STV090x_GAINLLR_NF16, 0x1F }, + { STV090x_GAINLLR_NF17, 0x21 }, + { STV090x_RCCFGH, 0x20 }, + { STV090x_P1_FECM, 0x01 }, /* disable DSS modes */ + { STV090x_P2_FECM, 0x01 }, /* disable DSS modes */ + { STV090x_P1_PRVIT, 0x2F }, /* disable PR 6/7 */ + { STV090x_P2_PRVIT, 0x2F }, /* disable PR 6/7 */ +}; + +static struct stv090x_reg stv0903_initval[] = { + { STV090x_OUTCFG, 0x00 }, + { STV090x_AGCRF1CFG, 0x11 }, + { STV090x_STOPCLK1, 0x48 }, + { STV090x_STOPCLK2, 0x14 }, + { STV090x_TSTTNR1, 0x27 }, + { STV090x_TSTTNR2, 0x21 }, + { STV090x_P1_DISTXCTL, 0x22 }, + { STV090x_P1_F22TX, 0xc0 }, + { STV090x_P1_F22RX, 0xc0 }, + { STV090x_P1_DISRXCTL, 0x00 }, + { STV090x_P1_DMDCFGMD, 0xF9 }, + { STV090x_P1_DEMOD, 0x08 }, + { STV090x_P1_DMDCFG3, 0xc4 }, + { STV090x_P1_CARFREQ, 0xed }, + { STV090x_P1_TNRCFG2, 0x82 }, + { STV090x_P1_LDT, 0xd0 }, + { STV090x_P1_LDT2, 0xb8 }, + { STV090x_P1_TMGCFG, 0xd2 }, + { STV090x_P1_TMGTHRISE, 0x20 }, + { STV090x_P1_TMGTHFALL, 0x00 }, + { STV090x_P1_SFRUPRATIO, 0xf0 }, + { STV090x_P1_SFRLOWRATIO, 0x70 }, + { STV090x_P1_TSCFGL, 0x20 }, + { STV090x_P1_FECSPY, 0x88 }, + { STV090x_P1_FSPYDATA, 0x3a }, + { STV090x_P1_FBERCPT4, 0x00 }, + { STV090x_P1_FSPYBER, 0x10 }, + { STV090x_P1_ERRCTRL1, 0x35 }, + { STV090x_P1_ERRCTRL2, 0xc1 }, + { STV090x_P1_CFRICFG, 0xf8 }, + { STV090x_P1_NOSCFG, 0x1c }, + { STV090x_P1_DMDTOM, 0x20 }, + { STV090x_P1_CORRELMANT, 0x70 }, + { STV090x_P1_CORRELABS, 0x88 }, + { STV090x_P1_AGC2O, 0x5b }, + { STV090x_P1_AGC2REF, 0x38 }, + { STV090x_P1_CARCFG, 0xe4 }, + { STV090x_P1_ACLC, 0x1A }, + { STV090x_P1_BCLC, 0x09 }, + { STV090x_P1_CARHDR, 0x08 }, + { STV090x_P1_KREFTMG, 0xc1 }, + { STV090x_P1_SFRSTEP, 0x58 }, + { STV090x_P1_TMGCFG2, 0x01 }, + { STV090x_P1_CAR2CFG, 0x26 }, + { STV090x_P1_BCLC2S2Q, 0x86 }, + { STV090x_P1_BCLC2S28, 0x86 }, + { STV090x_P1_SMAPCOEF7, 0x77 }, + { STV090x_P1_SMAPCOEF6, 0x85 }, + { STV090x_P1_SMAPCOEF5, 0x77 }, + { STV090x_P1_DMDCFG2, 0x3b }, + { STV090x_P1_MODCODLST0, 0xff }, + { STV090x_P1_MODCODLST1, 0xff }, + { STV090x_P1_MODCODLST2, 0xff }, + { STV090x_P1_MODCODLST3, 0xff }, + { STV090x_P1_MODCODLST4, 0xff }, + { STV090x_P1_MODCODLST5, 0xff }, + { STV090x_P1_MODCODLST6, 0xff }, + { STV090x_P1_MODCODLST7, 0xcc }, + { STV090x_P1_MODCODLST8, 0xcc }, + { STV090x_P1_MODCODLST9, 0xcc }, + { STV090x_P1_MODCODLSTA, 0xcc }, + { STV090x_P1_MODCODLSTB, 0xcc }, + { STV090x_P1_MODCODLSTC, 0xcc }, + { STV090x_P1_MODCODLSTD, 0xcc }, + { STV090x_P1_MODCODLSTE, 0xcc }, + { STV090x_P1_MODCODLSTF, 0xcf }, + { STV090x_GENCFG, 0x1c }, + { STV090x_NBITER_NF4, 0x37 }, + { STV090x_NBITER_NF5, 0x29 }, + { STV090x_NBITER_NF6, 0x37 }, + { STV090x_NBITER_NF7, 0x33 }, + { STV090x_NBITER_NF8, 0x31 }, + { STV090x_NBITER_NF9, 0x2f }, + { STV090x_NBITER_NF10, 0x39 }, + { STV090x_NBITER_NF11, 0x3a }, + { STV090x_NBITER_NF12, 0x29 }, + { STV090x_NBITER_NF13, 0x37 }, + { STV090x_NBITER_NF14, 0x33 }, + { STV090x_NBITER_NF15, 0x2f }, + { STV090x_NBITER_NF16, 0x39 }, + { STV090x_NBITER_NF17, 0x3a }, + { STV090x_NBITERNOERR, 0x04 }, + { STV090x_GAINLLR_NF4, 0x0C }, + { STV090x_GAINLLR_NF5, 0x0F }, + { STV090x_GAINLLR_NF6, 0x11 }, + { STV090x_GAINLLR_NF7, 0x14 }, + { STV090x_GAINLLR_NF8, 0x17 }, + { STV090x_GAINLLR_NF9, 0x19 }, + { STV090x_GAINLLR_NF10, 0x20 }, + { STV090x_GAINLLR_NF11, 0x21 }, + { STV090x_GAINLLR_NF12, 0x0D }, + { STV090x_GAINLLR_NF13, 0x0F }, + { STV090x_GAINLLR_NF14, 0x13 }, + { STV090x_GAINLLR_NF15, 0x1A }, + { STV090x_GAINLLR_NF16, 0x1F }, + { STV090x_GAINLLR_NF17, 0x21 }, + { STV090x_RCCFGH, 0x20 }, + { STV090x_P1_FECM, 0x01 }, /*disable the DSS mode */ + { STV090x_P1_PRVIT, 0x2f } /*disable puncture rate 6/7*/ +}; + +static struct stv090x_reg stv0900_cut20_val[] = { + + { STV090x_P2_DMDCFG3, 0xe8 }, + { STV090x_P2_DMDCFG4, 0x10 }, + { STV090x_P2_CARFREQ, 0x38 }, + { STV090x_P2_CARHDR, 0x20 }, + { STV090x_P2_KREFTMG, 0x5a }, + { STV090x_P2_SMAPCOEF7, 0x06 }, + { STV090x_P2_SMAPCOEF6, 0x00 }, + { STV090x_P2_SMAPCOEF5, 0x04 }, + { STV090x_P2_NOSCFG, 0x0c }, + { STV090x_P1_DMDCFG3, 0xe8 }, + { STV090x_P1_DMDCFG4, 0x10 }, + { STV090x_P1_CARFREQ, 0x38 }, + { STV090x_P1_CARHDR, 0x20 }, + { STV090x_P1_KREFTMG, 0x5a }, + { STV090x_P1_SMAPCOEF7, 0x06 }, + { STV090x_P1_SMAPCOEF6, 0x00 }, + { STV090x_P1_SMAPCOEF5, 0x04 }, + { STV090x_P1_NOSCFG, 0x0c }, + { STV090x_GAINLLR_NF4, 0x21 }, + { STV090x_GAINLLR_NF5, 0x21 }, + { STV090x_GAINLLR_NF6, 0x20 }, + { STV090x_GAINLLR_NF7, 0x1F }, + { STV090x_GAINLLR_NF8, 0x1E }, + { STV090x_GAINLLR_NF9, 0x1E }, + { STV090x_GAINLLR_NF10, 0x1D }, + { STV090x_GAINLLR_NF11, 0x1B }, + { STV090x_GAINLLR_NF12, 0x20 }, + { STV090x_GAINLLR_NF13, 0x20 }, + { STV090x_GAINLLR_NF14, 0x20 }, + { STV090x_GAINLLR_NF15, 0x20 }, + { STV090x_GAINLLR_NF16, 0x20 }, + { STV090x_GAINLLR_NF17, 0x21 }, +}; + +static struct stv090x_reg stv0903_cut20_val[] = { + { STV090x_P1_DMDCFG3, 0xe8 }, + { STV090x_P1_DMDCFG4, 0x10 }, + { STV090x_P1_CARFREQ, 0x38 }, + { STV090x_P1_CARHDR, 0x20 }, + { STV090x_P1_KREFTMG, 0x5a }, + { STV090x_P1_SMAPCOEF7, 0x06 }, + { STV090x_P1_SMAPCOEF6, 0x00 }, + { STV090x_P1_SMAPCOEF5, 0x04 }, + { STV090x_P1_NOSCFG, 0x0c }, + { STV090x_GAINLLR_NF4, 0x21 }, + { STV090x_GAINLLR_NF5, 0x21 }, + { STV090x_GAINLLR_NF6, 0x20 }, + { STV090x_GAINLLR_NF7, 0x1F }, + { STV090x_GAINLLR_NF8, 0x1E }, + { STV090x_GAINLLR_NF9, 0x1E }, + { STV090x_GAINLLR_NF10, 0x1D }, + { STV090x_GAINLLR_NF11, 0x1B }, + { STV090x_GAINLLR_NF12, 0x20 }, + { STV090x_GAINLLR_NF13, 0x20 }, + { STV090x_GAINLLR_NF14, 0x20 }, + { STV090x_GAINLLR_NF15, 0x20 }, + { STV090x_GAINLLR_NF16, 0x20 }, + { STV090x_GAINLLR_NF17, 0x21 } +}; + +/* Cut 2.0 Long Frame Tracking CR loop */ +static struct stv090x_long_frame_crloop stv090x_s2_crl_cut20[] = { + /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV090x_QPSK_12, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x1e }, + { STV090x_QPSK_35, 0x2f, 0x3f, 0x2e, 0x2f, 0x3d, 0x0f, 0x0e, 0x2e, 0x3d, 0x0e }, + { STV090x_QPSK_23, 0x2f, 0x3f, 0x2e, 0x2f, 0x0e, 0x0f, 0x0e, 0x1e, 0x3d, 0x3d }, + { STV090x_QPSK_34, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, + { STV090x_QPSK_45, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, + { STV090x_QPSK_56, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, + { STV090x_QPSK_89, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, + { STV090x_QPSK_910, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, + { STV090x_8PSK_35, 0x3c, 0x3e, 0x1c, 0x2e, 0x0c, 0x1e, 0x2b, 0x2d, 0x1b, 0x1d }, + { STV090x_8PSK_23, 0x1d, 0x3e, 0x3c, 0x2e, 0x2c, 0x1e, 0x0c, 0x2d, 0x2b, 0x1d }, + { STV090x_8PSK_34, 0x0e, 0x3e, 0x3d, 0x2e, 0x0d, 0x1e, 0x2c, 0x2d, 0x0c, 0x1d }, + { STV090x_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d }, + { STV090x_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d }, + { STV090x_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d } +}; + +/* Cut 3.0 Long Frame Tracking CR loop */ +static struct stv090x_long_frame_crloop stv090x_s2_crl_cut30[] = { + /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV090x_QPSK_12, 0x3c, 0x2c, 0x0c, 0x2c, 0x1b, 0x2c, 0x1b, 0x1c, 0x0b, 0x3b }, + { STV090x_QPSK_35, 0x0d, 0x0d, 0x0c, 0x0d, 0x1b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, + { STV090x_QPSK_23, 0x1d, 0x0d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, + { STV090x_QPSK_34, 0x1d, 0x1d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, + { STV090x_QPSK_45, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, + { STV090x_QPSK_56, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, + { STV090x_QPSK_89, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, + { STV090x_QPSK_910, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, + { STV090x_8PSK_35, 0x39, 0x29, 0x39, 0x19, 0x19, 0x19, 0x19, 0x19, 0x09, 0x19 }, + { STV090x_8PSK_23, 0x2a, 0x39, 0x1a, 0x0a, 0x39, 0x0a, 0x29, 0x39, 0x29, 0x0a }, + { STV090x_8PSK_34, 0x2b, 0x3a, 0x1b, 0x1b, 0x3a, 0x1b, 0x1a, 0x0b, 0x1a, 0x3a }, + { STV090x_8PSK_56, 0x0c, 0x1b, 0x3b, 0x3b, 0x1b, 0x3b, 0x3a, 0x3b, 0x3a, 0x1b }, + { STV090x_8PSK_89, 0x0d, 0x3c, 0x2c, 0x2c, 0x2b, 0x0c, 0x0b, 0x3b, 0x0b, 0x1b }, + { STV090x_8PSK_910, 0x0d, 0x0d, 0x2c, 0x3c, 0x3b, 0x1c, 0x0b, 0x3b, 0x0b, 0x1b } +}; + +/* Cut 2.0 Long Frame Tracking CR Loop */ +static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut20[] = { + /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV090x_16APSK_23, 0x0c, 0x0c, 0x0c, 0x0c, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x0c }, + { STV090x_16APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0c, 0x2d, 0x0c, 0x1d, 0x0c }, + { STV090x_16APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c }, + { STV090x_16APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c }, + { STV090x_16APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c }, + { STV090x_16APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c }, + { STV090x_32APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, + { STV090x_32APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, + { STV090x_32APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, + { STV090x_32APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, + { STV090x_32APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c } +}; + +/* Cut 3.0 Long Frame Tracking CR Loop */ +static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut30[] = { + /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV090x_16APSK_23, 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x0a, 0x3a, 0x0a, 0x2a, 0x0a }, + { STV090x_16APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0a, 0x3b, 0x0a, 0x1b, 0x0a }, + { STV090x_16APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a }, + { STV090x_16APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a }, + { STV090x_16APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a }, + { STV090x_16APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a }, + { STV090x_32APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, + { STV090x_32APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, + { STV090x_32APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, + { STV090x_32APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, + { STV090x_32APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } +}; + +static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut20[] = { + /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV090x_QPSK_14, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x2d, 0x1f, 0x3d, 0x3e }, + { STV090x_QPSK_13, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x3d, 0x0f, 0x3d, 0x2e }, + { STV090x_QPSK_25, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x2e } +}; + +static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut30[] = { + /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ + { STV090x_QPSK_14, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x2a, 0x1c, 0x3a, 0x3b }, + { STV090x_QPSK_13, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x3a, 0x0c, 0x3a, 0x2b }, + { STV090x_QPSK_25, 0x1c, 0x3c, 0x1b, 0x3c, 0x3a, 0x1c, 0x3a, 0x3b, 0x3a, 0x2b } +}; + +/* Cut 2.0 Short Frame Tracking CR Loop */ +static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut20[] = { + /* MODCOD 2M 5M 10M 20M 30M */ + { STV090x_QPSK, 0x2f, 0x2e, 0x0e, 0x0e, 0x3d }, + { STV090x_8PSK, 0x3e, 0x0e, 0x2d, 0x0d, 0x3c }, + { STV090x_16APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d }, + { STV090x_32APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d } +}; + +/* Cut 3.0 Short Frame Tracking CR Loop */ +static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut30[] = { + /* MODCOD 2M 5M 10M 20M 30M */ + { STV090x_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A }, + { STV090x_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 }, + { STV090x_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, + { STV090x_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A } +}; + +static inline s32 comp2(s32 __x, s32 __width) +{ + if (__width == 32) + return __x; + else + return (__x >= (1 << (__width - 1))) ? (__x - (1 << __width)) : __x; +} + +static int stv090x_read_reg(struct stv090x_state *state, unsigned int reg) +{ + const struct stv090x_config *config = state->config; + int ret; + + u8 b0[] = { reg >> 8, reg & 0xff }; + u8 buf; + + struct i2c_msg msg[] = { + { .addr = config->address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = config->address, .flags = I2C_M_RD, .buf = &buf, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + if (ret != -ERESTARTSYS) + dprintk(FE_ERROR, 1, + "Read error, Reg=[0x%02x], Status=%d", + reg, ret); + + return ret < 0 ? ret : -EREMOTEIO; + } + if (unlikely(*state->verbose >= FE_DEBUGREG)) + dprintk(FE_ERROR, 1, "Reg=[0x%02x], data=%02x", + reg, buf); + + return (unsigned int) buf; +} + +static int stv090x_write_regs(struct stv090x_state *state, unsigned int reg, u8 *data, u32 count) +{ + const struct stv090x_config *config = state->config; + int ret; + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg i2c_msg = { .addr = config->address, .flags = 0, .buf = buf, .len = 2 + count }; + + if (2 + count > sizeof(buf)) { + printk(KERN_WARNING + "%s: i2c wr reg=%04x: len=%d is too big!\n", + KBUILD_MODNAME, reg, count); + return -EINVAL; + } + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + memcpy(&buf[2], data, count); + + dprintk(FE_DEBUGREG, 1, "%s [0x%04x]: %*ph", + __func__, reg, count, data); + + ret = i2c_transfer(state->i2c, &i2c_msg, 1); + if (ret != 1) { + if (ret != -ERESTARTSYS) + dprintk(FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", + reg, data[0], count, ret); + return ret < 0 ? ret : -EREMOTEIO; + } + + return 0; +} + +static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 data) +{ + u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return stv090x_write_regs(state, reg, &tmp, 1); +} + +static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) +{ + u32 reg; + + /* + * NOTE! A lock is used as a FSM to control the state in which + * access is serialized between two tuners on the same demod. + * This has nothing to do with a lock to protect a critical section + * which may in some other cases be confused with protecting I/O + * access to the demodulator gate. + * In case of any error, the lock is unlocked and exit within the + * relevant operations themselves. + */ + if (enable) { + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 1); + else + mutex_lock(&state->internal->tuner_lock); + } + + reg = STV090x_READ_DEMOD(state, I2CRPT); + if (enable) { + dprintk(FE_DEBUG, 1, "Enable Gate"); + STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0) + goto err; + + } else { + dprintk(FE_DEBUG, 1, "Disable Gate"); + STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 0); + if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0) + goto err; + } + + if (!enable) { + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); + } + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); + return -1; +} + +static void stv090x_get_lock_tmg(struct stv090x_state *state) +{ + switch (state->algo) { + case STV090x_BLIND_SEARCH: + dprintk(FE_DEBUG, 1, "Blind Search"); + if (state->srate <= 1500000) { /*10Msps< SR <=15Msps*/ + state->DemodTimeout = 1500; + state->FecTimeout = 400; + } else if (state->srate <= 5000000) { /*10Msps< SR <=15Msps*/ + state->DemodTimeout = 1000; + state->FecTimeout = 300; + } else { /*SR >20Msps*/ + state->DemodTimeout = 700; + state->FecTimeout = 100; + } + break; + + case STV090x_COLD_SEARCH: + case STV090x_WARM_SEARCH: + default: + dprintk(FE_DEBUG, 1, "Normal Search"); + if (state->srate <= 1000000) { /*SR <=1Msps*/ + state->DemodTimeout = 4500; + state->FecTimeout = 1700; + } else if (state->srate <= 2000000) { /*1Msps < SR <= 2Msps */ + state->DemodTimeout = 2500; + state->FecTimeout = 1100; + } else if (state->srate <= 5000000) { /*2Msps < SR <= 5Msps */ + state->DemodTimeout = 1000; + state->FecTimeout = 550; + } else if (state->srate <= 10000000) { /*5Msps < SR <= 10Msps */ + state->DemodTimeout = 700; + state->FecTimeout = 250; + } else if (state->srate <= 20000000) { /*10Msps < SR <= 20Msps */ + state->DemodTimeout = 400; + state->FecTimeout = 130; + } else { /*SR >20Msps*/ + state->DemodTimeout = 300; + state->FecTimeout = 100; + } + break; + } + + if (state->algo == STV090x_WARM_SEARCH) + state->DemodTimeout /= 2; +} + +static int stv090x_set_srate(struct stv090x_state *state, u32 srate) +{ + u32 sym; + + if (srate > 60000000) { + sym = (srate << 4); /* SR * 2^16 / master_clk */ + sym /= (state->internal->mclk >> 12); + } else if (srate > 6000000) { + sym = (srate << 6); + sym /= (state->internal->mclk >> 10); + } else { + sym = (srate << 9); + sym /= (state->internal->mclk >> 7); + } + + if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */ + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT0, (sym & 0xff)) < 0) /* LSB */ + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate) +{ + u32 sym; + + srate = 105 * (srate / 100); + if (srate > 60000000) { + sym = (srate << 4); /* SR * 2^16 / master_clk */ + sym /= (state->internal->mclk >> 12); + } else if (srate > 6000000) { + sym = (srate << 6); + sym /= (state->internal->mclk >> 10); + } else { + sym = (srate << 9); + sym /= (state->internal->mclk >> 7); + } + + if (sym < 0x7fff) { + if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) /* MSB */ + goto err; + if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) /* LSB */ + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x7f) < 0) /* MSB */ + goto err; + if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xff) < 0) /* LSB */ + goto err; + } + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate) +{ + u32 sym; + + srate = 95 * (srate / 100); + if (srate > 60000000) { + sym = (srate << 4); /* SR * 2^16 / master_clk */ + sym /= (state->internal->mclk >> 12); + } else if (srate > 6000000) { + sym = (srate << 6); + sym /= (state->internal->mclk >> 10); + } else { + sym = (srate << 9); + sym /= (state->internal->mclk >> 7); + } + + if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */ + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */ + goto err; + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static u32 stv090x_car_width(u32 srate, enum stv090x_rolloff rolloff) +{ + u32 ro; + + switch (rolloff) { + case STV090x_RO_20: + ro = 20; + break; + case STV090x_RO_25: + ro = 25; + break; + case STV090x_RO_35: + default: + ro = 35; + break; + } + + return srate + (srate * ro) / 100; +} + +static int stv090x_set_vit_thacq(struct stv090x_state *state) +{ + if (STV090x_WRITE_DEMOD(state, VTH12, 0x96) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH23, 0x64) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH34, 0x36) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH56, 0x23) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH67, 0x1e) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH78, 0x19) < 0) + goto err; + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_vit_thtracq(struct stv090x_state *state) +{ + if (STV090x_WRITE_DEMOD(state, VTH12, 0xd0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH23, 0x7d) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH34, 0x53) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH56, 0x2f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH67, 0x24) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VTH78, 0x1f) < 0) + goto err; + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_viterbi(struct stv090x_state *state) +{ + switch (state->search_mode) { + case STV090x_SEARCH_AUTO: + if (STV090x_WRITE_DEMOD(state, FECM, 0x10) < 0) /* DVB-S and DVB-S2 */ + goto err; + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x3f) < 0) /* all puncture rate */ + goto err; + break; + case STV090x_SEARCH_DVBS1: + if (STV090x_WRITE_DEMOD(state, FECM, 0x00) < 0) /* disable DSS */ + goto err; + switch (state->fec) { + case STV090x_PR12: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0) + goto err; + break; + + case STV090x_PR23: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0) + goto err; + break; + + case STV090x_PR34: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x04) < 0) + goto err; + break; + + case STV090x_PR56: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x08) < 0) + goto err; + break; + + case STV090x_PR78: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x20) < 0) + goto err; + break; + + default: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x2f) < 0) /* all */ + goto err; + break; + } + break; + case STV090x_SEARCH_DSS: + if (STV090x_WRITE_DEMOD(state, FECM, 0x80) < 0) + goto err; + switch (state->fec) { + case STV090x_PR12: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0) + goto err; + break; + + case STV090x_PR23: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0) + goto err; + break; + + case STV090x_PR67: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x10) < 0) + goto err; + break; + + default: + if (STV090x_WRITE_DEMOD(state, PRVIT, 0x13) < 0) /* 1/2, 2/3, 6/7 */ + goto err; + break; + } + break; + default: + break; + } + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_stop_modcod(struct stv090x_state *state) +{ + if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xff) < 0) + goto err; + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_activate_modcod(struct stv090x_state *state) +{ + if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xfc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_activate_modcod_single(struct stv090x_state *state) +{ + + if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xf0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0x0f) < 0) + goto err; + + return 0; + +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable) +{ + u32 reg; + + switch (state->demod) { + case STV090x_DEMODULATOR_0: + mutex_lock(&state->internal->demod_lock); + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + mutex_unlock(&state->internal->demod_lock); + break; + + case STV090x_DEMODULATOR_1: + mutex_lock(&state->internal->demod_lock); + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + mutex_unlock(&state->internal->demod_lock); + break; + + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + return 0; +err: + mutex_unlock(&state->internal->demod_lock); + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_dvbs_track_crl(struct stv090x_state *state) +{ + if (state->internal->dev_ver >= 0x30) { + /* Set ACLC BCLC optimised value vs SR */ + if (state->srate >= 15000000) { + if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0x1a) < 0) + goto err; + } else if ((state->srate >= 7000000) && (15000000 > state->srate)) { + if (STV090x_WRITE_DEMOD(state, ACLC, 0x0c) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0x1b) < 0) + goto err; + } else if (state->srate < 7000000) { + if (STV090x_WRITE_DEMOD(state, ACLC, 0x2c) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0x1c) < 0) + goto err; + } + + } else { + /* Cut 2.0 */ + if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) + goto err; + } + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_delivery_search(struct stv090x_state *state) +{ + u32 reg; + + switch (state->search_mode) { + case STV090x_SEARCH_DVBS1: + case STV090x_SEARCH_DSS: + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + + /* Activate Viterbi decoder in legacy search, + * do not use FRESVIT1, might impact VITERBI2 + */ + if (stv090x_vitclk_ctl(state, 0) < 0) + goto err; + + if (stv090x_dvbs_track_crl(state) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x22) < 0) /* disable DVB-S2 */ + goto err; + + if (stv090x_set_vit_thacq(state) < 0) + goto err; + if (stv090x_set_viterbi(state) < 0) + goto err; + break; + + case STV090x_SEARCH_DVBS2: + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + + if (stv090x_vitclk_ctl(state, 1) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) /* stop DVB-S CR loop */ + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) + goto err; + + if (state->internal->dev_ver <= 0x20) { + /* enable S2 carrier loop */ + if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) + goto err; + } else { + /* > Cut 3: Stop carrier 3 */ + if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0) + goto err; + } + + if (state->demod_mode != STV090x_SINGLE) { + /* Cut 2: enable link during search */ + if (stv090x_activate_modcod(state) < 0) + goto err; + } else { + /* Single demodulator + * Authorize SHORT and LONG frames, + * QPSK, 8PSK, 16APSK and 32APSK + */ + if (stv090x_activate_modcod_single(state) < 0) + goto err; + } + + if (stv090x_set_vit_thtracq(state) < 0) + goto err; + break; + + case STV090x_SEARCH_AUTO: + default: + /* enable DVB-S2 and DVB-S2 in Auto MODE */ + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + + if (stv090x_vitclk_ctl(state, 0) < 0) + goto err; + + if (stv090x_dvbs_track_crl(state) < 0) + goto err; + + if (state->internal->dev_ver <= 0x20) { + /* enable S2 carrier loop */ + if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) + goto err; + } else { + /* > Cut 3: Stop carrier 3 */ + if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0) + goto err; + } + + if (state->demod_mode != STV090x_SINGLE) { + /* Cut 2: enable link during search */ + if (stv090x_activate_modcod(state) < 0) + goto err; + } else { + /* Single demodulator + * Authorize SHORT and LONG frames, + * QPSK, 8PSK, 16APSK and 32APSK + */ + if (stv090x_activate_modcod_single(state) < 0) + goto err; + } + + if (stv090x_set_vit_thacq(state) < 0) + goto err; + + if (stv090x_set_viterbi(state) < 0) + goto err; + break; + } + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_start_search(struct stv090x_state *state) +{ + u32 reg, freq_abs; + s16 freq; + + /* Reset demodulator */ + reg = STV090x_READ_DEMOD(state, DMDISTATE); + STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); + if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) + goto err; + + if (state->internal->dev_ver <= 0x20) { + if (state->srate <= 5000000) { + if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRLOW0, 0x00) < 0) + goto err; + + /*enlarge the timing bandwidth for Low SR*/ + if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) + goto err; + } else { + /* If the symbol rate is >5 Msps + Set The carrier search up and low to auto mode */ + if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) + goto err; + /*reduce the timing bandwidth for high SR*/ + if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) + goto err; + } + } else { + /* >= Cut 3 */ + if (state->srate <= 5000000) { + /* enlarge the timing bandwidth for Low SR */ + STV090x_WRITE_DEMOD(state, RTCS2, 0x68); + } else { + /* reduce timing bandwidth for high SR */ + STV090x_WRITE_DEMOD(state, RTCS2, 0x44); + } + + /* Set CFR min and max to manual mode */ + STV090x_WRITE_DEMOD(state, CARCFG, 0x46); + + if (state->algo == STV090x_WARM_SEARCH) { + /* WARM Start + * CFR min = -1MHz, + * CFR max = +1MHz + */ + freq_abs = 1000 << 16; + freq_abs /= (state->internal->mclk / 1000); + freq = (s16) freq_abs; + } else { + /* COLD Start + * CFR min =- (SearchRange / 2 + 600KHz) + * CFR max = +(SearchRange / 2 + 600KHz) + * (600KHz for the tuner step size) + */ + freq_abs = (state->search_range / 2000) + 600; + freq_abs = freq_abs << 16; + freq_abs /= (state->internal->mclk / 1000); + freq = (s16) freq_abs; + } + + if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0) + goto err; + + freq *= -1; + + if (STV090x_WRITE_DEMOD(state, CFRLOW1, MSB(freq)) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRLOW0, LSB(freq)) < 0) + goto err; + + } + + if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0) + goto err; + + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) + goto err; + + if ((state->search_mode == STV090x_SEARCH_DVBS1) || + (state->search_mode == STV090x_SEARCH_DSS) || + (state->search_mode == STV090x_SEARCH_AUTO)) { + + if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) + goto err; + } + } + + if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xe0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xc0) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + reg = STV090x_READ_DEMOD(state, DMDCFG2); + STV090x_SETFIELD_Px(reg, S1S2_SEQUENTIAL_FIELD, 0x0); + if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) + goto err; + + if (state->internal->dev_ver >= 0x20) { + /*Frequency offset detector setting*/ + if (state->srate < 2000000) { + if (state->internal->dev_ver <= 0x20) { + /* Cut 2 */ + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0) + goto err; + } else { + /* Cut 3 */ + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0) + goto err; + } + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0) + goto err; + } else if (state->srate < 10000000) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) + goto err; + } + } else { + if (state->srate < 10000000) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xef) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xed) < 0) + goto err; + } + } + + switch (state->algo) { + case STV090x_WARM_SEARCH: + /* The symbol rate and the exact + * carrier Frequency are known + */ + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) + goto err; + break; + + case STV090x_COLD_SEARCH: + /* The symbol rate is known */ + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) + goto err; + break; + + default: + break; + } + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_get_agc2_min_level(struct stv090x_state *state) +{ + u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg; + s32 i, j, steps, dir; + + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) /* SR = 65 Msps Max */ + goto err; + if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) /* SR= 400 ksps Min */ + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) /* stop acq @ coarse carrier state */ + goto err; + if (stv090x_set_srate(state, 1000000) < 0) + goto err; + + steps = state->search_range / 1000000; + if (steps <= 0) + steps = 1; + + dir = 1; + freq_step = (1000000 * 256) / (state->internal->mclk / 256); + freq_init = 0; + + for (i = 0; i < steps; i++) { + if (dir > 0) + freq_init = freq_init + (freq_step * i); + else + freq_init = freq_init - (freq_step * i); + + dir *= -1; + + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */ + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_init >> 8) & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_init & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */ + goto err; + msleep(10); + + agc2 = 0; + for (j = 0; j < 10; j++) { + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); + } + agc2 /= 10; + if (agc2 < agc2_min) + agc2_min = agc2; + } + + return agc2_min; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static u32 stv090x_get_srate(struct stv090x_state *state, u32 clk) +{ + u8 r3, r2, r1, r0; + s32 srate, int_1, int_2, tmp_1, tmp_2; + + r3 = STV090x_READ_DEMOD(state, SFR3); + r2 = STV090x_READ_DEMOD(state, SFR2); + r1 = STV090x_READ_DEMOD(state, SFR1); + r0 = STV090x_READ_DEMOD(state, SFR0); + + srate = ((r3 << 24) | (r2 << 16) | (r1 << 8) | r0); + + int_1 = clk >> 16; + int_2 = srate >> 16; + + tmp_1 = clk % 0x10000; + tmp_2 = srate % 0x10000; + + srate = (int_1 * int_2) + + ((int_1 * tmp_2) >> 16) + + ((int_2 * tmp_1) >> 16); + + return srate; +} + +static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) +{ + struct dvb_frontend *fe = &state->frontend; + + int tmg_lock = 0, i; + s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq; + u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg; + u32 agc2th; + + if (state->internal->dev_ver >= 0x30) + agc2th = 0x2e00; + else + agc2th = 0x1f00; + + reg = STV090x_READ_DEMOD(state, DMDISTATE); + STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */ + if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0) + goto err; + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0) + goto err; + + if (state->internal->dev_ver >= 0x30) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0) + goto err; + + } else if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0) + goto err; + } + + if (state->srate <= 2000000) + car_step = 1000; + else if (state->srate <= 5000000) + car_step = 2000; + else if (state->srate <= 12000000) + car_step = 3000; + else + car_step = 5000; + + steps = -1 + ((state->search_range / 1000) / car_step); + steps /= 2; + steps = (2 * steps) + 1; + if (steps < 0) + steps = 1; + else if (steps > 10) { + steps = 11; + car_step = (state->search_range / 1000) / 10; + } + cur_step = 0; + dir = 1; + freq = state->frequency; + + while ((!tmg_lock) && (cur_step < steps)) { + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */ + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0) + goto err; + /* trigger acquisition */ + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0) + goto err; + msleep(50); + for (i = 0; i < 10; i++) { + reg = STV090x_READ_DEMOD(state, DSTATUS); + if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) + tmg_cpt++; + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); + } + agc2 /= 10; + srate_coarse = stv090x_get_srate(state, state->internal->mclk); + cur_step++; + dir *= -1; + if ((tmg_cpt >= 5) && (agc2 < agc2th) && + (srate_coarse < 50000000) && (srate_coarse > 850000)) + tmg_lock = 1; + else if (cur_step < steps) { + if (dir > 0) + freq += cur_step * car_step; + else + freq -= cur_step * car_step; + + /* Setup tuner */ + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_set_frequency) { + if (state->config->tuner_set_frequency(fe, freq) < 0) + goto err_gateoff; + } + + if (state->config->tuner_set_bandwidth) { + if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + msleep(50); + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_get_status) { + if (state->config->tuner_get_status(fe, ®) < 0) + goto err_gateoff; + } + + if (reg) + dprintk(FE_DEBUG, 1, "Tuner phase locked"); + else + dprintk(FE_DEBUG, 1, "Tuner unlocked"); + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + } + } + if (!tmg_lock) + srate_coarse = 0; + else + srate_coarse = stv090x_get_srate(state, state->internal->mclk); + + return srate_coarse; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static u32 stv090x_srate_srch_fine(struct stv090x_state *state) +{ + u32 srate_coarse, freq_coarse, sym, reg; + + srate_coarse = stv090x_get_srate(state, state->internal->mclk); + freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8; + freq_coarse |= STV090x_READ_DEMOD(state, CFR1); + sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ + + if (sym < state->srate) + srate_coarse = 0; + else { + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */ + goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0) + goto err; + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + + if (state->internal->dev_ver >= 0x30) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0) + goto err; + } else if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) + goto err; + } + + if (srate_coarse > 3000000) { + sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ + sym = (sym / 1000) * 65536; + sym /= (state->internal->mclk / 1000); + if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) + goto err; + sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */ + sym = (sym / 1000) * 65536; + sym /= (state->internal->mclk / 1000); + if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) + goto err; + sym = (srate_coarse / 1000) * 65536; + sym /= (state->internal->mclk / 1000); + if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) + goto err; + } else { + sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ + sym = (sym / 100) * 65536; + sym /= (state->internal->mclk / 100); + if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) + goto err; + sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */ + sym = (sym / 100) * 65536; + sym /= (state->internal->mclk / 100); + if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) + goto err; + sym = (srate_coarse / 100) * 65536; + sym /= (state->internal->mclk / 100); + if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) + goto err; + } + if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_coarse >> 8) & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_coarse & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) /* trigger acquisition */ + goto err; + } + + return srate_coarse; + +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout) +{ + s32 timer = 0, lock = 0; + u32 reg; + u8 stat; + + while ((timer < timeout) && (!lock)) { + reg = STV090x_READ_DEMOD(state, DMDSTATE); + stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); + + switch (stat) { + case 0: /* searching */ + case 1: /* first PLH detected */ + default: + dprintk(FE_DEBUG, 1, "Demodulator searching .."); + lock = 0; + break; + case 2: /* DVB-S2 mode */ + case 3: /* DVB-S1/legacy mode */ + reg = STV090x_READ_DEMOD(state, DSTATUS); + lock = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); + break; + } + + if (!lock) + msleep(10); + else + dprintk(FE_DEBUG, 1, "Demodulator acquired LOCK"); + + timer += 10; + } + return lock; +} + +static int stv090x_blind_search(struct stv090x_state *state) +{ + u32 agc2, reg, srate_coarse; + s32 cpt_fail, agc2_ovflw, i; + u8 k_ref, k_max, k_min; + int coarse_fail = 0; + int lock; + + k_max = 110; + k_min = 10; + + agc2 = stv090x_get_agc2_min_level(state); + + if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) { + lock = 0; + } else { + + if (state->internal->dev_ver <= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) + goto err; + } else { + /* > Cut 3 */ + if (STV090x_WRITE_DEMOD(state, CARCFG, 0x06) < 0) + goto err; + } + + if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) + goto err; + + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) /* set viterbi hysteresis */ + goto err; + } + + k_ref = k_max; + do { + if (STV090x_WRITE_DEMOD(state, KREFTMG, k_ref) < 0) + goto err; + if (stv090x_srate_srch_coarse(state) != 0) { + srate_coarse = stv090x_srate_srch_fine(state); + if (srate_coarse != 0) { + stv090x_get_lock_tmg(state); + lock = stv090x_get_dmdlock(state, + state->DemodTimeout); + } else { + lock = 0; + } + } else { + cpt_fail = 0; + agc2_ovflw = 0; + for (i = 0; i < 10; i++) { + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); + if (agc2 >= 0xff00) + agc2_ovflw++; + reg = STV090x_READ_DEMOD(state, DSTATUS2); + if ((STV090x_GETFIELD_Px(reg, CFR_OVERFLOW_FIELD) == 0x01) && + (STV090x_GETFIELD_Px(reg, DEMOD_DELOCK_FIELD) == 0x01)) + + cpt_fail++; + } + if ((cpt_fail > 7) || (agc2_ovflw > 7)) + coarse_fail = 1; + + lock = 0; + } + k_ref -= 20; + } while ((k_ref >= k_min) && (!lock) && (!coarse_fail)); + } + + return lock; + +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_chk_tmg(struct stv090x_state *state) +{ + u32 reg; + s32 tmg_cpt = 0, i; + u8 freq, tmg_thh, tmg_thl; + int tmg_lock = 0; + + freq = STV090x_READ_DEMOD(state, CARFREQ); + tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE); + tmg_thl = STV090x_READ_DEMOD(state, TMGTHFALL); + if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); /* stop carrier offset search */ + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, RTC, 0x80) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, RTCS2, 0x40) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x00) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) /* set car ofset to 0 */ + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x65) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) /* trigger acquisition */ + goto err; + msleep(10); + + for (i = 0; i < 10; i++) { + reg = STV090x_READ_DEMOD(state, DSTATUS); + if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) + tmg_cpt++; + msleep(1); + } + if (tmg_cpt >= 3) + tmg_lock = 1; + + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) /* DVB-S1 timing */ + goto err; + if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) /* DVB-S2 timing */ + goto err; + + if (STV090x_WRITE_DEMOD(state, CARFREQ, freq) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHRISE, tmg_thh) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGTHFALL, tmg_thl) < 0) + goto err; + + return tmg_lock; + +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) +{ + struct dvb_frontend *fe = &state->frontend; + + u32 reg; + s32 car_step, steps, cur_step, dir, freq, timeout_lock; + int lock; + + if (state->srate >= 10000000) + timeout_lock = timeout_dmd / 3; + else + timeout_lock = timeout_dmd / 2; + + lock = stv090x_get_dmdlock(state, timeout_lock); /* cold start wait */ + if (lock) + return lock; + + if (state->srate >= 10000000) { + if (stv090x_chk_tmg(state)) { + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) + goto err; + return stv090x_get_dmdlock(state, timeout_dmd); + } + return 0; + } + + if (state->srate <= 4000000) + car_step = 1000; + else if (state->srate <= 7000000) + car_step = 2000; + else if (state->srate <= 10000000) + car_step = 3000; + else + car_step = 5000; + + steps = (state->search_range / 1000) / car_step; + steps /= 2; + steps = 2 * (steps + 1); + if (steps < 0) + steps = 2; + else if (steps > 12) + steps = 12; + + cur_step = 1; + dir = 1; + + freq = state->frequency; + state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate; + while ((cur_step <= steps) && (!lock)) { + if (dir > 0) + freq += cur_step * car_step; + else + freq -= cur_step * car_step; + + /* Setup tuner */ + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_set_frequency) { + if (state->config->tuner_set_frequency(fe, freq) < 0) + goto err_gateoff; + } + + if (state->config->tuner_set_bandwidth) { + if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + msleep(50); + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_get_status) { + if (state->config->tuner_get_status(fe, ®) < 0) + goto err_gateoff; + if (reg) + dprintk(FE_DEBUG, 1, "Tuner phase locked"); + else + dprintk(FE_DEBUG, 1, "Tuner unlocked"); + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c); + if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) + goto err; + lock = stv090x_get_dmdlock(state, (timeout_dmd / 3)); + + dir *= -1; + cur_step++; + } + + return lock; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s32 *timeout_sw, s32 *steps) +{ + s32 timeout, inc, steps_max, srate, car_max; + + srate = state->srate; + car_max = state->search_range / 1000; + car_max += car_max / 10; + car_max = 65536 * (car_max / 2); + car_max /= (state->internal->mclk / 1000); + + if (car_max > 0x4000) + car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */ + + inc = srate; + inc /= state->internal->mclk / 1000; + inc *= 256; + inc *= 256; + inc /= 1000; + + switch (state->search_mode) { + case STV090x_SEARCH_DVBS1: + case STV090x_SEARCH_DSS: + inc *= 3; /* freq step = 3% of srate */ + timeout = 20; + break; + + case STV090x_SEARCH_DVBS2: + inc *= 4; + timeout = 25; + break; + + case STV090x_SEARCH_AUTO: + default: + inc *= 3; + timeout = 25; + break; + } + inc /= 100; + if ((inc > car_max) || (inc < 0)) + inc = car_max / 2; /* increment <= 1/8 Mclk */ + + timeout *= 27500; /* 27.5 Msps reference */ + if (srate > 0) + timeout /= (srate / 1000); + + if ((timeout > 100) || (timeout < 0)) + timeout = 100; + + steps_max = (car_max / inc) + 1; /* min steps = 3 */ + if ((steps_max > 100) || (steps_max < 0)) { + steps_max = 100; /* max steps <= 100 */ + inc = car_max / steps_max; + } + *freq_inc = inc; + *timeout_sw = timeout; + *steps = steps_max; + + return 0; +} + +static int stv090x_chk_signal(struct stv090x_state *state) +{ + s32 offst_car, agc2, car_max; + int no_signal; + + offst_car = STV090x_READ_DEMOD(state, CFR2) << 8; + offst_car |= STV090x_READ_DEMOD(state, CFR1); + offst_car = comp2(offst_car, 16); + + agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8; + agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + car_max = state->search_range / 1000; + + car_max += (car_max / 10); /* 10% margin */ + car_max = (65536 * car_max / 2); + car_max /= state->internal->mclk / 1000; + + if (car_max > 0x4000) + car_max = 0x4000; + + if ((agc2 > 0x2000) || (offst_car > 2 * car_max) || (offst_car < -2 * car_max)) { + no_signal = 1; + dprintk(FE_DEBUG, 1, "No Signal"); + } else { + no_signal = 0; + dprintk(FE_DEBUG, 1, "Found Signal"); + } + + return no_signal; +} + +static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 timeout, int zigzag, s32 steps_max) +{ + int no_signal, lock = 0; + s32 cpt_step = 0, offst_freq, car_max; + u32 reg; + + car_max = state->search_range / 1000; + car_max += (car_max / 10); + car_max = (65536 * car_max / 2); + car_max /= (state->internal->mclk / 1000); + if (car_max > 0x4000) + car_max = 0x4000; + + if (zigzag) + offst_freq = 0; + else + offst_freq = -car_max + inc; + + do { + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT1, ((offst_freq / 256) & 0xff)) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, offst_freq & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, PDELCTRL1); + STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x1); /* stop DVB-S2 packet delin */ + if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) + goto err; + + if (zigzag) { + if (offst_freq >= 0) + offst_freq = -offst_freq - 2 * inc; + else + offst_freq = -offst_freq; + } else { + offst_freq += 2 * inc; + } + + cpt_step++; + + lock = stv090x_get_dmdlock(state, timeout); + no_signal = stv090x_chk_signal(state); + + } while ((!lock) && + (!no_signal) && + ((offst_freq - inc) < car_max) && + ((offst_freq + inc) > -car_max) && + (cpt_step < steps_max)); + + reg = STV090x_READ_DEMOD(state, PDELCTRL1); + STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) + goto err; + + return lock; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_sw_algo(struct stv090x_state *state) +{ + int no_signal, zigzag, lock = 0; + u32 reg; + + s32 dvbs2_fly_wheel; + s32 inc, timeout_step, trials, steps_max; + + /* get params */ + stv090x_get_loop_params(state, &inc, &timeout_step, &steps_max); + + switch (state->search_mode) { + case STV090x_SEARCH_DVBS1: + case STV090x_SEARCH_DSS: + /* accelerate the frequency detector */ + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0) + goto err; + } + + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x49) < 0) + goto err; + zigzag = 0; + break; + + case STV090x_SEARCH_DVBS2: + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) + goto err; + } + + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0) + goto err; + zigzag = 1; + break; + + case STV090x_SEARCH_AUTO: + default: + /* accelerate the frequency detector */ + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) + goto err; + } + + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0xc9) < 0) + goto err; + zigzag = 0; + break; + } + + trials = 0; + do { + lock = stv090x_search_car_loop(state, inc, timeout_step, zigzag, steps_max); + no_signal = stv090x_chk_signal(state); + trials++; + + /*run the SW search 2 times maximum*/ + if (lock || no_signal || (trials == 2)) { + /*Check if the demod is not losing lock in DVBS2*/ + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) + goto err; + } + + reg = STV090x_READ_DEMOD(state, DMDSTATE); + if ((lock) && (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == STV090x_DVBS2)) { + /*Check if the demod is not losing lock in DVBS2*/ + msleep(timeout_step); + reg = STV090x_READ_DEMOD(state, DMDFLYW); + dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); + if (dvbs2_fly_wheel < 0xd) { /*if correct frames is decrementing */ + msleep(timeout_step); + reg = STV090x_READ_DEMOD(state, DMDFLYW); + dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); + } + if (dvbs2_fly_wheel < 0xd) { + /*FALSE lock, The demod is losing lock */ + lock = 0; + if (trials < 2) { + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) + goto err; + } + + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0) + goto err; + } + } + } + } + } while ((!lock) && (trials < 2) && (!no_signal)); + + return lock; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static enum stv090x_delsys stv090x_get_std(struct stv090x_state *state) +{ + u32 reg; + enum stv090x_delsys delsys; + + reg = STV090x_READ_DEMOD(state, DMDSTATE); + if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 2) + delsys = STV090x_DVBS2; + else if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 3) { + reg = STV090x_READ_DEMOD(state, FECM); + if (STV090x_GETFIELD_Px(reg, DSS_DVB_FIELD) == 1) + delsys = STV090x_DSS; + else + delsys = STV090x_DVBS1; + } else { + delsys = STV090x_ERROR; + } + + return delsys; +} + +/* in Hz */ +static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk) +{ + s32 derot, int_1, int_2, tmp_1, tmp_2; + + derot = STV090x_READ_DEMOD(state, CFR2) << 16; + derot |= STV090x_READ_DEMOD(state, CFR1) << 8; + derot |= STV090x_READ_DEMOD(state, CFR0); + + derot = comp2(derot, 24); + int_1 = mclk >> 12; + int_2 = derot >> 12; + + /* carrier_frequency = MasterClock * Reg / 2^24 */ + tmp_1 = mclk % 0x1000; + tmp_2 = derot % 0x1000; + + derot = (int_1 * int_2) + + ((int_1 * tmp_2) >> 12) + + ((int_2 * tmp_1) >> 12); + + return derot; +} + +static int stv090x_get_viterbi(struct stv090x_state *state) +{ + u32 reg, rate; + + reg = STV090x_READ_DEMOD(state, VITCURPUN); + rate = STV090x_GETFIELD_Px(reg, VIT_CURPUN_FIELD); + + switch (rate) { + case 13: + state->fec = STV090x_PR12; + break; + + case 18: + state->fec = STV090x_PR23; + break; + + case 21: + state->fec = STV090x_PR34; + break; + + case 24: + state->fec = STV090x_PR56; + break; + + case 25: + state->fec = STV090x_PR67; + break; + + case 26: + state->fec = STV090x_PR78; + break; + + default: + state->fec = STV090x_PRERR; + break; + } + + return 0; +} + +static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *state) +{ + struct dvb_frontend *fe = &state->frontend; + + u8 tmg; + u32 reg; + s32 i = 0, offst_freq; + + msleep(5); + + if (state->algo == STV090x_BLIND_SEARCH) { + tmg = STV090x_READ_DEMOD(state, TMGREG2); + STV090x_WRITE_DEMOD(state, SFRSTEP, 0x5c); + while ((i <= 50) && (tmg != 0) && (tmg != 0xff)) { + tmg = STV090x_READ_DEMOD(state, TMGREG2); + msleep(5); + i += 5; + } + } + state->delsys = stv090x_get_std(state); + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_get_frequency) { + if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000; + state->frequency += offst_freq; + + if (stv090x_get_viterbi(state) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, DMDMODCOD); + state->modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); + state->pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01; + state->frame_len = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) >> 1; + reg = STV090x_READ_DEMOD(state, TMGOBS); + state->rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD); + reg = STV090x_READ_DEMOD(state, FECM); + state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD); + + if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) { + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_get_frequency) { + if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) + return STV090x_RANGEOK; + else if (abs(offst_freq) <= (stv090x_car_width(state->srate, state->rolloff) / 2000)) + return STV090x_RANGEOK; + } else { + if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) + return STV090x_RANGEOK; + } + + return STV090x_OUTOFRANGE; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static u32 stv090x_get_tmgoffst(struct stv090x_state *state, u32 srate) +{ + s32 offst_tmg; + + offst_tmg = STV090x_READ_DEMOD(state, TMGREG2) << 16; + offst_tmg |= STV090x_READ_DEMOD(state, TMGREG1) << 8; + offst_tmg |= STV090x_READ_DEMOD(state, TMGREG0); + + offst_tmg = comp2(offst_tmg, 24); /* 2's complement */ + if (!offst_tmg) + offst_tmg = 1; + + offst_tmg = ((s32) srate * 10) / ((s32) 0x1000000 / offst_tmg); + offst_tmg /= 320; + + return offst_tmg; +} + +static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_modcod modcod, s32 pilots) +{ + u8 aclc = 0x29; + s32 i; + struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low; + + if (state->internal->dev_ver == 0x20) { + car_loop = stv090x_s2_crl_cut20; + car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20; + car_loop_apsk_low = stv090x_s2_apsk_crl_cut20; + } else { + /* >= Cut 3 */ + car_loop = stv090x_s2_crl_cut30; + car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut30; + car_loop_apsk_low = stv090x_s2_apsk_crl_cut30; + } + + if (modcod < STV090x_QPSK_12) { + i = 0; + while ((i < 3) && (modcod != car_loop_qpsk_low[i].modcod)) + i++; + + if (i >= 3) + i = 2; + + } else { + i = 0; + while ((i < 14) && (modcod != car_loop[i].modcod)) + i++; + + if (i >= 14) { + i = 0; + while ((i < 11) && (modcod != car_loop_apsk_low[i].modcod)) + i++; + + if (i >= 11) + i = 10; + } + } + + if (modcod <= STV090x_QPSK_25) { + if (pilots) { + if (state->srate <= 3000000) + aclc = car_loop_qpsk_low[i].crl_pilots_on_2; + else if (state->srate <= 7000000) + aclc = car_loop_qpsk_low[i].crl_pilots_on_5; + else if (state->srate <= 15000000) + aclc = car_loop_qpsk_low[i].crl_pilots_on_10; + else if (state->srate <= 25000000) + aclc = car_loop_qpsk_low[i].crl_pilots_on_20; + else + aclc = car_loop_qpsk_low[i].crl_pilots_on_30; + } else { + if (state->srate <= 3000000) + aclc = car_loop_qpsk_low[i].crl_pilots_off_2; + else if (state->srate <= 7000000) + aclc = car_loop_qpsk_low[i].crl_pilots_off_5; + else if (state->srate <= 15000000) + aclc = car_loop_qpsk_low[i].crl_pilots_off_10; + else if (state->srate <= 25000000) + aclc = car_loop_qpsk_low[i].crl_pilots_off_20; + else + aclc = car_loop_qpsk_low[i].crl_pilots_off_30; + } + + } else if (modcod <= STV090x_8PSK_910) { + if (pilots) { + if (state->srate <= 3000000) + aclc = car_loop[i].crl_pilots_on_2; + else if (state->srate <= 7000000) + aclc = car_loop[i].crl_pilots_on_5; + else if (state->srate <= 15000000) + aclc = car_loop[i].crl_pilots_on_10; + else if (state->srate <= 25000000) + aclc = car_loop[i].crl_pilots_on_20; + else + aclc = car_loop[i].crl_pilots_on_30; + } else { + if (state->srate <= 3000000) + aclc = car_loop[i].crl_pilots_off_2; + else if (state->srate <= 7000000) + aclc = car_loop[i].crl_pilots_off_5; + else if (state->srate <= 15000000) + aclc = car_loop[i].crl_pilots_off_10; + else if (state->srate <= 25000000) + aclc = car_loop[i].crl_pilots_off_20; + else + aclc = car_loop[i].crl_pilots_off_30; + } + } else { /* 16APSK and 32APSK */ + /* + * This should never happen in practice, except if + * something is really wrong at the car_loop table. + */ + if (i >= 11) + i = 10; + if (state->srate <= 3000000) + aclc = car_loop_apsk_low[i].crl_pilots_on_2; + else if (state->srate <= 7000000) + aclc = car_loop_apsk_low[i].crl_pilots_on_5; + else if (state->srate <= 15000000) + aclc = car_loop_apsk_low[i].crl_pilots_on_10; + else if (state->srate <= 25000000) + aclc = car_loop_apsk_low[i].crl_pilots_on_20; + else + aclc = car_loop_apsk_low[i].crl_pilots_on_30; + } + + return aclc; +} + +static u8 stv090x_optimize_carloop_short(struct stv090x_state *state) +{ + struct stv090x_short_frame_crloop *short_crl = NULL; + s32 index = 0; + u8 aclc = 0x0b; + + switch (state->modulation) { + case STV090x_QPSK: + default: + index = 0; + break; + case STV090x_8PSK: + index = 1; + break; + case STV090x_16APSK: + index = 2; + break; + case STV090x_32APSK: + index = 3; + break; + } + + if (state->internal->dev_ver >= 0x30) { + /* Cut 3.0 and up */ + short_crl = stv090x_s2_short_crl_cut30; + } else { + /* Cut 2.0 and up: we don't support cuts older than 2.0 */ + short_crl = stv090x_s2_short_crl_cut20; + } + + if (state->srate <= 3000000) + aclc = short_crl[index].crl_2; + else if (state->srate <= 7000000) + aclc = short_crl[index].crl_5; + else if (state->srate <= 15000000) + aclc = short_crl[index].crl_10; + else if (state->srate <= 25000000) + aclc = short_crl[index].crl_20; + else + aclc = short_crl[index].crl_30; + + return aclc; +} + +static int stv090x_optimize_track(struct stv090x_state *state) +{ + struct dvb_frontend *fe = &state->frontend; + + enum stv090x_modcod modcod; + + s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0; + u32 reg; + + srate = stv090x_get_srate(state, state->internal->mclk); + srate += stv090x_get_tmgoffst(state, srate); + + switch (state->delsys) { + case STV090x_DVBS1: + case STV090x_DSS: + if (state->search_mode == STV090x_SEARCH_AUTO) { + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + } + reg = STV090x_READ_DEMOD(state, DEMOD); + STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff); + STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x01); + if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) + goto err; + + if (state->internal->dev_ver >= 0x30) { + if (stv090x_get_viterbi(state) < 0) + goto err; + + if (state->fec == STV090x_PR12) { + if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x98) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x18) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0) + goto err; + } + } + + if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0) + goto err; + break; + + case STV090x_DVBS2: + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + if (state->internal->dev_ver >= 0x30) { + if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) + goto err; + } + if (state->frame_len == STV090x_LONG_FRAME) { + reg = STV090x_READ_DEMOD(state, DMDMODCOD); + modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); + pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01; + aclc = stv090x_optimize_carloop(state, modcod, pilots); + if (modcod <= STV090x_QPSK_910) { + STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc); + } else if (modcod <= STV090x_8PSK_910) { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0) + goto err; + } + if ((state->demod_mode == STV090x_SINGLE) && (modcod > STV090x_8PSK_910)) { + if (modcod <= STV090x_16APSK_910) { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0) + goto err; + } + } + } else { + /*Carrier loop setting for short frame*/ + aclc = stv090x_optimize_carloop_short(state); + if (state->modulation == STV090x_QPSK) { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc) < 0) + goto err; + } else if (state->modulation == STV090x_8PSK) { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0) + goto err; + } else if (state->modulation == STV090x_16APSK) { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0) + goto err; + } else if (state->modulation == STV090x_32APSK) { + if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0) + goto err; + } + } + + STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67); /* PER */ + break; + + case STV090x_ERROR: + default: + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + break; + } + + f_1 = STV090x_READ_DEMOD(state, CFR2); + f_0 = STV090x_READ_DEMOD(state, CFR1); + reg = STV090x_READ_DEMOD(state, TMGOBS); + + if (state->algo == STV090x_BLIND_SEARCH) { + STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00); + reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) + goto err; + + if (stv090x_set_srate(state, srate) < 0) + goto err; + blind_tune = 1; + + if (stv090x_dvbs_track_crl(state) < 0) + goto err; + } + + if (state->internal->dev_ver >= 0x20) { + if ((state->search_mode == STV090x_SEARCH_DVBS1) || + (state->search_mode == STV090x_SEARCH_DSS) || + (state->search_mode == STV090x_SEARCH_AUTO)) { + + if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x0a) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x00) < 0) + goto err; + } + } + + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + + /* AUTO tracking MODE */ + if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x80) < 0) + goto err; + /* AUTO tracking MODE */ + if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0) + goto err; + + if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) || + (state->srate < 10000000)) { + /* update initial carrier freq with the found freq offset */ + if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) + goto err; + state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000; + + if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) { + + if (state->algo != STV090x_WARM_SEARCH) { + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_set_bandwidth) { + if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + } + } + if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) + msleep(50); /* blind search: wait 50ms for SR stabilization */ + else + msleep(5); + + stv090x_get_lock_tmg(state); + + if (!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) { + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) + goto err; + + i = 0; + + while ((!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) && (i <= 2)) { + + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) + goto err; + i++; + } + } + + } + + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) + goto err; + } + + if ((state->delsys == STV090x_DVBS1) || (state->delsys == STV090x_DSS)) + stv090x_set_vit_thtracq(state); + + return 0; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_get_feclock(struct stv090x_state *state, s32 timeout) +{ + s32 timer = 0, lock = 0, stat; + u32 reg; + + while ((timer < timeout) && (!lock)) { + reg = STV090x_READ_DEMOD(state, DMDSTATE); + stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); + + switch (stat) { + case 0: /* searching */ + case 1: /* first PLH detected */ + default: + lock = 0; + break; + + case 2: /* DVB-S2 mode */ + reg = STV090x_READ_DEMOD(state, PDELSTATUS1); + lock = STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD); + break; + + case 3: /* DVB-S1/legacy mode */ + reg = STV090x_READ_DEMOD(state, VSTATUSVIT); + lock = STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD); + break; + } + if (!lock) { + msleep(10); + timer += 10; + } + } + return lock; +} + +static int stv090x_get_lock(struct stv090x_state *state, s32 timeout_dmd, s32 timeout_fec) +{ + u32 reg; + s32 timer = 0; + int lock; + + lock = stv090x_get_dmdlock(state, timeout_dmd); + if (lock) + lock = stv090x_get_feclock(state, timeout_fec); + + if (lock) { + lock = 0; + + while ((timer < timeout_fec) && (!lock)) { + reg = STV090x_READ_DEMOD(state, TSSTATUS); + lock = STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD); + msleep(1); + timer++; + } + } + + return lock; +} + +static int stv090x_set_s2rolloff(struct stv090x_state *state) +{ + u32 reg; + + if (state->internal->dev_ver <= 0x20) { + /* rolloff to auto mode if DVBS2 */ + reg = STV090x_READ_DEMOD(state, DEMOD); + STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00); + if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) + goto err; + } else { + /* DVB-S2 rolloff to auto mode if DVBS2 */ + reg = STV090x_READ_DEMOD(state, DEMOD); + STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 0x00); + if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) + goto err; + } + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + + +static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) +{ + struct dvb_frontend *fe = &state->frontend; + enum stv090x_signal_state signal_state = STV090x_NOCARRIER; + u32 reg; + s32 agc1_power, power_iq = 0, i; + int lock = 0, low_sr = 0; + + reg = STV090x_READ_DEMOD(state, TSCFGH); + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */ + if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */ + goto err; + + if (state->internal->dev_ver >= 0x20) { + if (state->srate > 5000000) { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0) + goto err; + } + } + + stv090x_get_lock_tmg(state); + + if (state->algo == STV090x_BLIND_SEARCH) { + state->tuner_bw = 2 * 36000000; /* wide bw for unknown srate */ + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) /* wider srate scan */ + goto err; + if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) + goto err; + if (stv090x_set_srate(state, 1000000) < 0) /* initial srate = 1Msps */ + goto err; + } else { + /* known srate */ + if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0) + goto err; + + if (state->srate < 2000000) { + /* SR < 2MSPS */ + if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x63) < 0) + goto err; + } else { + /* SR >= 2Msps */ + if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) + goto err; + } + + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + + if (state->internal->dev_ver >= 0x20) { + if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0) + goto err; + if (state->algo == STV090x_COLD_SEARCH) + state->tuner_bw = (15 * (stv090x_car_width(state->srate, state->rolloff) + 10000000)) / 10; + else if (state->algo == STV090x_WARM_SEARCH) + state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + 10000000; + } + + /* if cold start or warm (Symbolrate is known) + * use a Narrow symbol rate scan range + */ + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) /* narrow srate scan */ + goto err; + + if (stv090x_set_srate(state, state->srate) < 0) + goto err; + + if (stv090x_set_max_srate(state, state->internal->mclk, + state->srate) < 0) + goto err; + if (stv090x_set_min_srate(state, state->internal->mclk, + state->srate) < 0) + goto err; + + if (state->srate >= 10000000) + low_sr = 0; + else + low_sr = 1; + } + + /* Setup tuner */ + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_set_bbgain) { + reg = state->config->tuner_bbgain; + if (reg == 0) + reg = 10; /* default: 10dB */ + if (state->config->tuner_set_bbgain(fe, reg) < 0) + goto err_gateoff; + } + + if (state->config->tuner_set_frequency) { + if (state->config->tuner_set_frequency(fe, state->frequency) < 0) + goto err_gateoff; + } + + if (state->config->tuner_set_bandwidth) { + if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + msleep(50); + + if (state->config->tuner_get_status) { + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + if (state->config->tuner_get_status(fe, ®) < 0) + goto err_gateoff; + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + if (reg) + dprintk(FE_DEBUG, 1, "Tuner phase locked"); + else { + dprintk(FE_DEBUG, 1, "Tuner unlocked"); + return STV090x_NOCARRIER; + } + } + + msleep(10); + agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1), + STV090x_READ_DEMOD(state, AGCIQIN0)); + + if (agc1_power == 0) { + /* If AGC1 integrator value is 0 + * then read POWERI, POWERQ + */ + for (i = 0; i < 5; i++) { + power_iq += (STV090x_READ_DEMOD(state, POWERI) + + STV090x_READ_DEMOD(state, POWERQ)) >> 1; + } + power_iq /= 5; + } + + if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) { + dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq); + lock = 0; + signal_state = STV090x_NOAGC1; + } else { + reg = STV090x_READ_DEMOD(state, DEMOD); + STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion); + + if (state->internal->dev_ver <= 0x20) { + /* rolloff to auto mode if DVBS2 */ + STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1); + } else { + /* DVB-S2 rolloff to auto mode if DVBS2 */ + STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 1); + } + if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) + goto err; + + if (stv090x_delivery_search(state) < 0) + goto err; + + if (state->algo != STV090x_BLIND_SEARCH) { + if (stv090x_start_search(state) < 0) + goto err; + } + } + + if (signal_state == STV090x_NOAGC1) + return signal_state; + + if (state->algo == STV090x_BLIND_SEARCH) + lock = stv090x_blind_search(state); + + else if (state->algo == STV090x_COLD_SEARCH) + lock = stv090x_get_coldlock(state, state->DemodTimeout); + + else if (state->algo == STV090x_WARM_SEARCH) + lock = stv090x_get_dmdlock(state, state->DemodTimeout); + + if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) { + if (!low_sr) { + if (stv090x_chk_tmg(state)) + lock = stv090x_sw_algo(state); + } + } + + if (lock) + signal_state = stv090x_get_sig_params(state); + + if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */ + stv090x_optimize_track(state); + + if (state->internal->dev_ver >= 0x20) { + /* >= Cut 2.0 :release TS reset after + * demod lock and optimized Tracking + */ + reg = STV090x_READ_DEMOD(state, TSCFGH); + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */ + if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) + goto err; + + msleep(3); + + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* merger reset */ + if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) + goto err; + + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */ + if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) + goto err; + } + + lock = stv090x_get_lock(state, state->FecTimeout, + state->FecTimeout); + if (lock) { + if (state->delsys == STV090x_DVBS2) { + stv090x_set_s2rolloff(state); + + reg = STV090x_READ_DEMOD(state, PDELCTRL2); + STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 1); + if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0) + goto err; + /* Reset DVBS2 packet delinator error counter */ + reg = STV090x_READ_DEMOD(state, PDELCTRL2); + STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 0); + if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67) < 0) /* PER */ + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0) + goto err; + } + /* Reset the Total packet counter */ + if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0x00) < 0) + goto err; + /* Reset the packet Error counter2 */ + if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) + goto err; + } else { + signal_state = STV090x_NODATA; + stv090x_chk_signal(state); + } + } + return signal_state; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_pls(struct stv090x_state *state, u32 pls_code) +{ + dprintk(FE_DEBUG, 1, "Set Gold PLS code %d", pls_code); + if (STV090x_WRITE_DEMOD(state, PLROOT0, pls_code & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, PLROOT1, (pls_code >> 8) & 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, PLROOT2, 0x04 | (pls_code >> 16)) < 0) + goto err; + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_mis(struct stv090x_state *state, int mis) +{ + u32 reg; + + if (mis < 0 || mis > 255) { + dprintk(FE_DEBUG, 1, "Disable MIS filtering"); + reg = STV090x_READ_DEMOD(state, PDELCTRL1); + STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x00); + if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) + goto err; + } else { + dprintk(FE_DEBUG, 1, "Enable MIS filtering - %d", mis); + reg = STV090x_READ_DEMOD(state, PDELCTRL1); + STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x01); + if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ISIENTRY, mis) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ISIBITENA, 0xff) < 0) + goto err; + } + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static enum dvbfe_search stv090x_search(struct dvb_frontend *fe) +{ + struct stv090x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *props = &fe->dtv_property_cache; + + if (props->frequency == 0) + return DVBFE_ALGO_SEARCH_INVALID; + + switch (props->delivery_system) { + case SYS_DSS: + state->delsys = STV090x_DSS; + break; + case SYS_DVBS: + state->delsys = STV090x_DVBS1; + break; + case SYS_DVBS2: + state->delsys = STV090x_DVBS2; + break; + default: + return DVBFE_ALGO_SEARCH_INVALID; + } + + state->frequency = props->frequency; + state->srate = props->symbol_rate; + state->search_mode = STV090x_SEARCH_AUTO; + state->algo = STV090x_COLD_SEARCH; + state->fec = STV090x_PRERR; + if (state->srate > 10000000) { + dprintk(FE_DEBUG, 1, "Search range: 10 MHz"); + state->search_range = 10000000; + } else { + dprintk(FE_DEBUG, 1, "Search range: 5 MHz"); + state->search_range = 5000000; + } + + stv090x_set_pls(state, props->scrambling_sequence_index); + stv090x_set_mis(state, props->stream_id); + + if (stv090x_algo(state) == STV090x_RANGEOK) { + dprintk(FE_DEBUG, 1, "Search success!"); + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + dprintk(FE_DEBUG, 1, "Search failed!"); + return DVBFE_ALGO_SEARCH_FAILED; + } + + return DVBFE_ALGO_SEARCH_ERROR; +} + +static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg, dstatus; + u8 search_state; + + *status = 0; + + dstatus = STV090x_READ_DEMOD(state, DSTATUS); + if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD)) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + reg = STV090x_READ_DEMOD(state, DMDSTATE); + search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); + + switch (search_state) { + case 0: /* searching */ + case 1: /* first PLH detected */ + default: + dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)"); + break; + + case 2: /* DVB-S2 mode */ + dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2"); + if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { + reg = STV090x_READ_DEMOD(state, PDELSTATUS1); + if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { + *status |= FE_HAS_VITERBI; + reg = STV090x_READ_DEMOD(state, TSSTATUS); + if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + } + } + break; + + case 3: /* DVB-S1/legacy mode */ + dprintk(FE_DEBUG, 1, "Delivery system: DVB-S"); + if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { + reg = STV090x_READ_DEMOD(state, VSTATUSVIT); + if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) { + *status |= FE_HAS_VITERBI; + reg = STV090x_READ_DEMOD(state, TSSTATUS); + if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + } + } + break; + } + + return 0; +} + +static int stv090x_read_per(struct dvb_frontend *fe, u32 *per) +{ + struct stv090x_state *state = fe->demodulator_priv; + + s32 count_4, count_3, count_2, count_1, count_0, count; + u32 reg, h, m, l; + enum fe_status status; + + stv090x_read_status(fe, &status); + if (!(status & FE_HAS_LOCK)) { + *per = 1 << 23; /* Max PER */ + } else { + /* Counter 2 */ + reg = STV090x_READ_DEMOD(state, ERRCNT22); + h = STV090x_GETFIELD_Px(reg, ERR_CNT2_FIELD); + + reg = STV090x_READ_DEMOD(state, ERRCNT21); + m = STV090x_GETFIELD_Px(reg, ERR_CNT21_FIELD); + + reg = STV090x_READ_DEMOD(state, ERRCNT20); + l = STV090x_GETFIELD_Px(reg, ERR_CNT20_FIELD); + + *per = ((h << 16) | (m << 8) | l); + + count_4 = STV090x_READ_DEMOD(state, FBERCPT4); + count_3 = STV090x_READ_DEMOD(state, FBERCPT3); + count_2 = STV090x_READ_DEMOD(state, FBERCPT2); + count_1 = STV090x_READ_DEMOD(state, FBERCPT1); + count_0 = STV090x_READ_DEMOD(state, FBERCPT0); + + if ((!count_4) && (!count_3)) { + count = (count_2 & 0xff) << 16; + count |= (count_1 & 0xff) << 8; + count |= count_0 & 0xff; + } else { + count = 1 << 24; + } + if (count == 0) + *per = 1; + } + if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) +{ + int res = 0; + int min = 0, med; + + if ((val >= tab[min].read && val < tab[max].read) || + (val >= tab[max].read && val < tab[min].read)) { + while ((max - min) > 1) { + med = (max + min) / 2; + if ((val >= tab[min].read && val < tab[med].read) || + (val >= tab[med].read && val < tab[min].read)) + max = med; + else + min = med; + } + res = ((val - tab[min].read) * + (tab[max].real - tab[min].real) / + (tab[max].read - tab[min].read)) + + tab[min].real; + } else { + if (tab[min].read < tab[max].read) { + if (val < tab[min].read) + res = tab[min].real; + else if (val >= tab[max].read) + res = tab[max].real; + } else { + if (val >= tab[min].read) + res = tab[min].real; + else if (val < tab[max].read) + res = tab[max].real; + } + } + + return res; +} + +static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg; + s32 agc_0, agc_1, agc; + s32 str; + + reg = STV090x_READ_DEMOD(state, AGCIQIN1); + agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + reg = STV090x_READ_DEMOD(state, AGCIQIN0); + agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + agc = MAKEWORD16(agc_1, agc_0); + + str = stv090x_table_lookup(stv090x_rf_tab, + ARRAY_SIZE(stv090x_rf_tab) - 1, agc); + if (agc > stv090x_rf_tab[0].read) + str = 0; + else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read) + str = -100; + *strength = (str + 100) * 0xFFFF / 100; + + return 0; +} + +static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg_0, reg_1, reg, i; + s32 val_0, val_1, val = 0; + u8 lock_f; + s32 div; + u32 last; + + switch (state->delsys) { + case STV090x_DVBS2: + reg = STV090x_READ_DEMOD(state, DSTATUS); + lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); + if (lock_f) { + msleep(5); + for (i = 0; i < 16; i++) { + reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1); + val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); + reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0); + val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD); + val += MAKEWORD16(val_1, val_0); + msleep(1); + } + val /= 16; + last = ARRAY_SIZE(stv090x_s2cn_tab) - 1; + div = stv090x_s2cn_tab[last].real - + stv090x_s2cn_tab[3].real; + val = stv090x_table_lookup(stv090x_s2cn_tab, last, val); + if (val < 0) + val = 0; + *cnr = val * 0xFFFF / div; + } + break; + + case STV090x_DVBS1: + case STV090x_DSS: + reg = STV090x_READ_DEMOD(state, DSTATUS); + lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); + if (lock_f) { + msleep(5); + for (i = 0; i < 16; i++) { + reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1); + val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); + reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0); + val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD); + val += MAKEWORD16(val_1, val_0); + msleep(1); + } + val /= 16; + last = ARRAY_SIZE(stv090x_s1cn_tab) - 1; + div = stv090x_s1cn_tab[last].real - + stv090x_s1cn_tab[0].real; + val = stv090x_table_lookup(stv090x_s1cn_tab, last, val); + *cnr = val * 0xFFFF / div; + } + break; + default: + break; + } + + return 0; +} + +static int stv090x_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg; + + reg = STV090x_READ_DEMOD(state, DISTXCTL); + switch (tone) { + case SEC_TONE_ON: + STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0); + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + break; + + case SEC_TONE_OFF: + STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0); + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + break; + default: + return -EINVAL; + } + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + + +static enum dvbfe_algo stv090x_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_CUSTOM; +} + +static int stv090x_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg, idle = 0, fifo_full = 1; + int i; + + reg = STV090x_READ_DEMOD(state, DISTXCTL); + + STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, + (state->config->diseqc_envelope_mode) ? 4 : 2); + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + + STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + + for (i = 0; i < cmd->msg_len; i++) { + + while (fifo_full) { + reg = STV090x_READ_DEMOD(state, DISTXSTATUS); + fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD); + } + + if (STV090x_WRITE_DEMOD(state, DISTXDATA, cmd->msg[i]) < 0) + goto err; + } + reg = STV090x_READ_DEMOD(state, DISTXCTL); + STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + + i = 0; + + while ((!idle) && (i < 10)) { + reg = STV090x_READ_DEMOD(state, DISTXSTATUS); + idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD); + msleep(10); + i++; + } + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg, idle = 0, fifo_full = 1; + u8 mode, value; + int i; + + reg = STV090x_READ_DEMOD(state, DISTXCTL); + + if (burst == SEC_MINI_A) { + mode = (state->config->diseqc_envelope_mode) ? 5 : 3; + value = 0x00; + } else { + mode = (state->config->diseqc_envelope_mode) ? 4 : 2; + value = 0xFF; + } + + STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, mode); + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + + STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + + while (fifo_full) { + reg = STV090x_READ_DEMOD(state, DISTXSTATUS); + fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD); + } + + if (STV090x_WRITE_DEMOD(state, DISTXDATA, value) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, DISTXCTL); + STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) + goto err; + + i = 0; + + while ((!idle) && (i < 10)) { + reg = STV090x_READ_DEMOD(state, DISTXSTATUS); + idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD); + msleep(10); + i++; + } + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg = 0, i = 0, rx_end = 0; + + while ((rx_end != 1) && (i < 10)) { + msleep(10); + i++; + reg = STV090x_READ_DEMOD(state, DISRX_ST0); + rx_end = STV090x_GETFIELD_Px(reg, RX_END_FIELD); + } + + if (rx_end) { + reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD); + for (i = 0; i < reply->msg_len; i++) + reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA); + } + + return 0; +} + +static int stv090x_sleep(struct dvb_frontend *fe) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg; + u8 full_standby = 0; + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_sleep) { + if (state->config->tuner_sleep(fe) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep", + state->device == STV0900 ? "STV0900" : "STV0903", + state->demod); + + mutex_lock(&state->internal->demod_lock); + + switch (state->demod) { + case STV090x_DEMODULATOR_0: + /* power off ADC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err_unlock; + /* power off DiSEqC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR2); + STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) + goto err_unlock; + + /* check whether path 2 is already sleeping, that is when + ADC2 is off */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + if (STV090x_GETFIELD(reg, ADC2_PON_FIELD) == 0) + full_standby = 1; + + /* stop clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 1); + /* ADC 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 1); + /* FEC clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err_unlock; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1); + /* viterbi 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 1); + /* TS clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err_unlock; + break; + + case STV090x_DEMODULATOR_1: + /* power off ADC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err_unlock; + /* power off DiSEqC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR4); + STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) + goto err_unlock; + + /* check whether path 1 is already sleeping, that is when + ADC1 is off */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + if (STV090x_GETFIELD(reg, ADC1_PON_FIELD) == 0) + full_standby = 1; + + /* stop clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 1); + /* ADC 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 1); + /* FEC clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err_unlock; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1); + /* viterbi 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 1); + /* TS clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err_unlock; + break; + + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + + if (full_standby) { + /* general power off */ + reg = stv090x_read_reg(state, STV090x_SYNTCTRL); + STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) + goto err_unlock; + } + + mutex_unlock(&state->internal->demod_lock); + return 0; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); + goto err; +err_unlock: + mutex_unlock(&state->internal->demod_lock); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_wakeup(struct dvb_frontend *fe) +{ + struct stv090x_state *state = fe->demodulator_priv; + u32 reg; + + dprintk(FE_DEBUG, 1, "Wake %s(%d) from standby", + state->device == STV0900 ? "STV0900" : "STV0903", + state->demod); + + mutex_lock(&state->internal->demod_lock); + + /* general power on */ + reg = stv090x_read_reg(state, STV090x_SYNTCTRL); + STV090x_SETFIELD(reg, STANDBY_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) + goto err; + + switch (state->demod) { + case STV090x_DEMODULATOR_0: + /* power on ADC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err; + /* power on DiSEqC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR2); + STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) + goto err; + + /* activate clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 0); + /* ADC 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 0); + /* FEC clock */ + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 0); + /* viterbi 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 0); + /* TS clock */ + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + + case STV090x_DEMODULATOR_1: + /* power on ADC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err; + /* power on DiSEqC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR4); + STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) + goto err; + + /* activate clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 0); + /* ADC 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 0); + /* FEC clock */ + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 0); + /* viterbi 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 0); + /* TS clock */ + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + + mutex_unlock(&state->internal->demod_lock); + return 0; +err: + mutex_unlock(&state->internal->demod_lock); + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static void stv090x_release(struct dvb_frontend *fe) +{ + struct stv090x_state *state = fe->demodulator_priv; + + state->internal->num_used--; + if (state->internal->num_used <= 0) { + + dprintk(FE_ERROR, 1, "Actually removing"); + + remove_dev(state->internal); + kfree(state->internal); + } + + kfree(state); +} + +static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc_mode) +{ + u32 reg = 0; + + reg = stv090x_read_reg(state, STV090x_GENCFG); + + switch (ldpc_mode) { + case STV090x_DUAL: + default: + if ((state->demod_mode != STV090x_DUAL) || (STV090x_GETFIELD(reg, DDEMOD_FIELD) != 1)) { + /* set LDPC to dual mode */ + if (stv090x_write_reg(state, STV090x_GENCFG, 0x1d) < 0) + goto err; + + state->demod_mode = STV090x_DUAL; + + reg = stv090x_read_reg(state, STV090x_TSTRES0); + STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1); + if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) + goto err; + STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0); + if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0) + goto err; + + if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0) + goto err; + } + break; + + case STV090x_SINGLE: + if (stv090x_stop_modcod(state) < 0) + goto err; + if (stv090x_activate_modcod_single(state) < 0) + goto err; + + if (state->demod == STV090x_DEMODULATOR_1) { + if (stv090x_write_reg(state, STV090x_GENCFG, 0x06) < 0) /* path 2 */ + goto err; + } else { + if (stv090x_write_reg(state, STV090x_GENCFG, 0x04) < 0) /* path 1 */ + goto err; + } + + reg = stv090x_read_reg(state, STV090x_TSTRES0); + STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1); + if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) + goto err; + STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0); + if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, PDELCTRL1); + STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x01); + if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x00); + if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) + goto err; + break; + } + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +/* return (Hz), clk in Hz*/ +static u32 stv090x_get_mclk(struct stv090x_state *state) +{ + const struct stv090x_config *config = state->config; + u32 div, reg; + u8 ratio; + + div = stv090x_read_reg(state, STV090x_NCOARSE); + reg = stv090x_read_reg(state, STV090x_SYNTCTRL); + ratio = STV090x_GETFIELD(reg, SELX1RATIO_FIELD) ? 4 : 6; + + return (div + 1) * config->xtal / ratio; /* kHz */ +} + +static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk) +{ + const struct stv090x_config *config = state->config; + u32 reg, div, clk_sel; + + reg = stv090x_read_reg(state, STV090x_SYNTCTRL); + clk_sel = ((STV090x_GETFIELD(reg, SELX1RATIO_FIELD) == 1) ? 4 : 6); + + div = ((clk_sel * mclk) / config->xtal) - 1; + + reg = stv090x_read_reg(state, STV090x_NCOARSE); + STV090x_SETFIELD(reg, M_DIV_FIELD, div); + if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0) + goto err; + + state->internal->mclk = stv090x_get_mclk(state); + + /*Set the DiseqC frequency to 22KHz */ + div = state->internal->mclk / 704000; + if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv0900_set_tspath(struct stv090x_state *state) +{ + u32 reg; + + if (state->internal->dev_ver >= 0x20) { + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + switch (state->config->ts2_mode) { + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL, 0x00); + break; + + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x06) < 0) /* Mux'd stream mode */ + goto err; + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0) + goto err; + break; + } + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + switch (state->config->ts2_mode) { + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) + goto err; + break; + + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0a) < 0) + goto err; + break; + } + break; + } + } else { + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + switch (state->config->ts2_mode) { + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10); + break; + + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x16); + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 0); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0) + goto err; + break; + } + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + switch (state->config->ts2_mode) { + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14); + break; + + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x12); + break; + } + break; + } + } + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_DVBCI: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_CONTINUOUS: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + default: + break; + } + + switch (state->config->ts2_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_DVBCI: + reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_CONTINUOUS: + reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) + goto err; + break; + + default: + break; + } + + if (state->config->ts1_clk > 0) { + u32 speed; + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + default: + speed = state->internal->mclk / + (state->config->ts1_clk / 4); + if (speed < 0x08) + speed = 0x08; + if (speed > 0xFF) + speed = 0xFF; + break; + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + speed = state->internal->mclk / + (state->config->ts1_clk / 32); + if (speed < 0x20) + speed = 0x20; + if (speed > 0xFF) + speed = 0xFF; + break; + } + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0) + goto err; + } + + if (state->config->ts2_clk > 0) { + u32 speed; + + switch (state->config->ts2_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + default: + speed = state->internal->mclk / + (state->config->ts2_clk / 4); + if (speed < 0x08) + speed = 0x08; + if (speed > 0xFF) + speed = 0xFF; + break; + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + speed = state->internal->mclk / + (state->config->ts2_clk / 32); + if (speed < 0x20) + speed = 0x20; + if (speed > 0xFF) + speed = 0xFF; + break; + } + reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0) + goto err; + } + + reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) + goto err; + + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv0903_set_tspath(struct stv090x_state *state) +{ + u32 reg; + + if (state->internal->dev_ver >= 0x20) { + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + stv090x_write_reg(state, STV090x_TSGENERAL, 0x00); + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c); + break; + } + } else { + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10); + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + default: + stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14); + break; + } + } + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_DVBCI: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_PUNCTURED: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + case STV090x_TSMODE_SERIAL_CONTINUOUS: + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); + STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + break; + + default: + break; + } + + if (state->config->ts1_clk > 0) { + u32 speed; + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + default: + speed = state->internal->mclk / + (state->config->ts1_clk / 4); + if (speed < 0x08) + speed = 0x08; + if (speed > 0xFF) + speed = 0xFF; + break; + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + speed = state->internal->mclk / + (state->config->ts1_clk / 32); + if (speed < 0x20) + speed = 0x20; + if (speed > 0xFF) + speed = 0xFF; + break; + } + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0) + goto err; + } + + reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); + if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_init(struct dvb_frontend *fe) +{ + struct stv090x_state *state = fe->demodulator_priv; + const struct stv090x_config *config = state->config; + u32 reg; + + if (state->internal->mclk == 0) { + /* call tuner init to configure the tuner's clock output + divider directly before setting up the master clock of + the stv090x. */ + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (config->tuner_init) { + if (config->tuner_init(fe) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */ + msleep(5); + if (stv090x_write_reg(state, STV090x_SYNTCTRL, + 0x20 | config->clk_mode) < 0) + goto err; + stv090x_get_mclk(state); + } + + if (stv090x_wakeup(fe) < 0) { + dprintk(FE_ERROR, 1, "Error waking device"); + goto err; + } + + if (stv090x_ldpc_mode(state, state->demod_mode) < 0) + goto err; + + reg = STV090x_READ_DEMOD(state, TNRCFG2); + STV090x_SETFIELD_Px(reg, TUN_IQSWAP_FIELD, state->inversion); + if (STV090x_WRITE_DEMOD(state, TNRCFG2, reg) < 0) + goto err; + reg = STV090x_READ_DEMOD(state, DEMOD); + STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff); + if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) + goto err; + + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (config->tuner_set_mode) { + if (config->tuner_set_mode(fe, TUNER_WAKE) < 0) + goto err_gateoff; + } + + if (config->tuner_init) { + if (config->tuner_init(fe) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + + if (state->device == STV0900) { + if (stv0900_set_tspath(state) < 0) + goto err; + } else { + if (stv0903_set_tspath(state) < 0) + goto err; + } + + return 0; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_setup(struct dvb_frontend *fe) +{ + struct stv090x_state *state = fe->demodulator_priv; + const struct stv090x_config *config = state->config; + const struct stv090x_reg *stv090x_initval = NULL; + const struct stv090x_reg *stv090x_cut20_val = NULL; + unsigned long t1_size = 0, t2_size = 0; + u32 reg = 0; + + int i; + + if (state->device == STV0900) { + dprintk(FE_DEBUG, 1, "Initializing STV0900"); + stv090x_initval = stv0900_initval; + t1_size = ARRAY_SIZE(stv0900_initval); + stv090x_cut20_val = stv0900_cut20_val; + t2_size = ARRAY_SIZE(stv0900_cut20_val); + } else if (state->device == STV0903) { + dprintk(FE_DEBUG, 1, "Initializing STV0903"); + stv090x_initval = stv0903_initval; + t1_size = ARRAY_SIZE(stv0903_initval); + stv090x_cut20_val = stv0903_cut20_val; + t2_size = ARRAY_SIZE(stv0903_cut20_val); + } + + /* STV090x init */ + + /* Stop Demod */ + if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0) + goto err; + if (state->device == STV0900) + if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0) + goto err; + + msleep(5); + + /* Set No Tuner Mode */ + if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0) + goto err; + if (state->device == STV0900) + if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0) + goto err; + + /* I2C repeater OFF */ + STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level); + if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0) + goto err; + if (state->device == STV0900) + if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0) + goto err; + + if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */ + goto err; + msleep(5); + if (stv090x_write_reg(state, STV090x_I2CCFG, 0x08) < 0) /* 1/41 oversampling */ + goto err; + if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0) /* enable PLL */ + goto err; + msleep(5); + + /* write initval */ + dprintk(FE_DEBUG, 1, "Setting up initial values"); + for (i = 0; i < t1_size; i++) { + if (stv090x_write_reg(state, stv090x_initval[i].addr, stv090x_initval[i].data) < 0) + goto err; + } + + state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID); + if (state->internal->dev_ver >= 0x20) { + if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) + goto err; + + /* write cut20_val*/ + dprintk(FE_DEBUG, 1, "Setting up Cut 2.0 initial values"); + for (i = 0; i < t2_size; i++) { + if (stv090x_write_reg(state, stv090x_cut20_val[i].addr, stv090x_cut20_val[i].data) < 0) + goto err; + } + + } else if (state->internal->dev_ver < 0x20) { + dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!", + state->internal->dev_ver); + + goto err; + } else if (state->internal->dev_ver > 0x30) { + /* we shouldn't bail out from here */ + dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!", + state->internal->dev_ver); + } + + /* ADC1 range */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_INMODE_FIELD, + (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err; + + /* ADC2 range */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_INMODE_FIELD, + (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err; + + if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) + goto err; + + return 0; +err: + dprintk(FE_ERROR, 1, "I/O error"); + return -1; +} + +static int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, + u8 value, u8 xor_value) +{ + struct stv090x_state *state = fe->demodulator_priv; + u8 reg = 0; + + STV090x_SETFIELD(reg, GPIOx_OPD_FIELD, dir); + STV090x_SETFIELD(reg, GPIOx_CONFIG_FIELD, value); + STV090x_SETFIELD(reg, GPIOx_XOR_FIELD, xor_value); + + return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); +} + +static int stv090x_setup_compound(struct stv090x_state *state) +{ + struct stv090x_dev *temp_int; + + temp_int = find_dev(state->i2c, + state->config->address); + + if (temp_int && state->demod_mode == STV090x_DUAL) { + state->internal = temp_int->internal; + state->internal->num_used++; + dprintk(FE_INFO, 1, "Found Internal Structure!"); + } else { + state->internal = kmalloc(sizeof(*state->internal), GFP_KERNEL); + if (!state->internal) + goto error; + temp_int = append_internal(state->internal); + if (!temp_int) { + kfree(state->internal); + goto error; + } + state->internal->num_used = 1; + state->internal->mclk = 0; + state->internal->dev_ver = 0; + state->internal->i2c_adap = state->i2c; + state->internal->i2c_addr = state->config->address; + dprintk(FE_INFO, 1, "Create New Internal Structure!"); + + mutex_init(&state->internal->demod_lock); + mutex_init(&state->internal->tuner_lock); + + if (stv090x_setup(&state->frontend) < 0) { + dprintk(FE_ERROR, 1, "Error setting up device"); + goto err_remove; + } + } + + if (state->internal->dev_ver >= 0x30) + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; + + /* workaround for stuck DiSEqC output */ + if (state->config->diseqc_envelope_mode) + stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); + + state->config->set_gpio = stv090x_set_gpio; + + dprintk(FE_ERROR, 1, "Probing %s demodulator(%d) Cut=0x%02x", + state->device == STV0900 ? "STV0900" : "STV0903", + state->config->demod, + state->internal->dev_ver); + + return 0; + +error: + return -ENOMEM; +err_remove: + remove_dev(state->internal); + kfree(state->internal); + return -ENODEV; +} + +static const struct dvb_frontend_ops stv090x_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, + .info = { + .name = "STV090x Multistandard", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION + }, + + .release = stv090x_release, + .init = stv090x_init, + + .sleep = stv090x_sleep, + .get_frontend_algo = stv090x_frontend_algo, + + .diseqc_send_master_cmd = stv090x_send_diseqc_msg, + .diseqc_send_burst = stv090x_send_diseqc_burst, + .diseqc_recv_slave_reply = stv090x_recv_slave_reply, + .set_tone = stv090x_set_tone, + + .search = stv090x_search, + .read_status = stv090x_read_status, + .read_ber = stv090x_read_per, + .read_signal_strength = stv090x_read_signal_strength, + .read_snr = stv090x_read_cnr, +}; + +static struct dvb_frontend *stv090x_get_dvb_frontend(struct i2c_client *client) +{ + struct stv090x_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &state->frontend; +} + +static int stv090x_probe(struct i2c_client *client) +{ + int ret = 0; + struct stv090x_config *config = client->dev.platform_data; + + struct stv090x_state *state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto error; + } + + state->verbose = &verbose; + state->config = config; + state->i2c = client->adapter; + state->frontend.ops = stv090x_ops; + state->frontend.demodulator_priv = state; + state->demod = config->demod; + /* Single or Dual mode */ + state->demod_mode = config->demod_mode; + state->device = config->device; + /* default */ + state->rolloff = STV090x_RO_35; + + ret = stv090x_setup_compound(state); + if (ret) + goto error; + + i2c_set_clientdata(client, state); + + /* setup callbacks */ + config->get_dvb_frontend = stv090x_get_dvb_frontend; + + return 0; + +error: + kfree(state); + return ret; +} + +static void stv090x_remove(struct i2c_client *client) +{ + struct stv090x_state *state = i2c_get_clientdata(client); + + stv090x_release(&state->frontend); +} + +struct dvb_frontend *stv090x_attach(struct stv090x_config *config, + struct i2c_adapter *i2c, + enum stv090x_demodulator demod) +{ + int ret = 0; + struct stv090x_state *state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto error; + + state->verbose = &verbose; + state->config = config; + state->i2c = i2c; + state->frontend.ops = stv090x_ops; + state->frontend.demodulator_priv = state; + state->demod = demod; + /* Single or Dual mode */ + state->demod_mode = config->demod_mode; + state->device = config->device; + /* default */ + state->rolloff = STV090x_RO_35; + + ret = stv090x_setup_compound(state); + if (ret) + goto error; + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stv090x_attach); + +static const struct i2c_device_id stv090x_id_table[] = { + {"stv090x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stv090x_id_table); + +static struct i2c_driver stv090x_driver = { + .driver = { + .name = "stv090x", + .suppress_bind_attrs = true, + }, + .probe = stv090x_probe, + .remove = stv090x_remove, + .id_table = stv090x_id_table, +}; + +module_i2c_driver(stv090x_driver); + +MODULE_PARM_DESC(verbose, "Set Verbosity level"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h new file mode 100644 index 0000000000..89f45d9fa4 --- /dev/null +++ b/drivers/media/dvb-frontends/stv090x.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STV0900/0903 Multistandard Broadcast Frontend driver + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STV090x_H +#define __STV090x_H + +enum stv090x_demodulator { + STV090x_DEMODULATOR_0 = 1, + STV090x_DEMODULATOR_1 +}; + +enum stv090x_device { + STV0903 = 0, + STV0900, +}; + +enum stv090x_mode { + STV090x_DUAL = 0, + STV090x_SINGLE +}; + +enum stv090x_tsmode { + STV090x_TSMODE_SERIAL_PUNCTURED = 1, + STV090x_TSMODE_SERIAL_CONTINUOUS, + STV090x_TSMODE_PARALLEL_PUNCTURED, + STV090x_TSMODE_DVBCI +}; + +enum stv090x_clkmode { + STV090x_CLK_INT = 0, /* Clk i/p = CLKI */ + STV090x_CLK_EXT = 2 /* Clk i/p = XTALI */ +}; + +enum stv090x_i2crpt { + STV090x_RPTLEVEL_256 = 0, + STV090x_RPTLEVEL_128 = 1, + STV090x_RPTLEVEL_64 = 2, + STV090x_RPTLEVEL_32 = 3, + STV090x_RPTLEVEL_16 = 4, + STV090x_RPTLEVEL_8 = 5, + STV090x_RPTLEVEL_4 = 6, + STV090x_RPTLEVEL_2 = 7, +}; + +enum stv090x_adc_range { + STV090x_ADC_2Vpp = 0, + STV090x_ADC_1Vpp = 1 +}; + +struct stv090x_config { + enum stv090x_device device; + enum stv090x_mode demod_mode; + enum stv090x_clkmode clk_mode; + enum stv090x_demodulator demod; + + u32 xtal; /* default: 8000000 */ + u8 address; /* default: 0x68 */ + + u8 ts1_mode; + u8 ts2_mode; + u32 ts1_clk; + u32 ts2_clk; + + u8 ts1_tei : 1; + u8 ts2_tei : 1; + + enum stv090x_i2crpt repeater_level; + + u8 tuner_bbgain; /* default: 10db */ + enum stv090x_adc_range adc1_range; /* default: 2Vpp */ + enum stv090x_adc_range adc2_range; /* default: 2Vpp */ + + bool diseqc_envelope_mode; + + int (*tuner_init)(struct dvb_frontend *fe); + int (*tuner_sleep)(struct dvb_frontend *fe); + int (*tuner_set_mode)(struct dvb_frontend *fe, enum tuner_mode mode); + int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency); + int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency); + int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); + int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); + int (*tuner_set_bbgain)(struct dvb_frontend *fe, u32 gain); + int (*tuner_get_bbgain)(struct dvb_frontend *fe, u32 *gain); + int (*tuner_set_refclk)(struct dvb_frontend *fe, u32 refclk); + int (*tuner_get_status)(struct dvb_frontend *fe, u32 *status); + void (*tuner_i2c_lock)(struct dvb_frontend *fe, int lock); + + /* dir = 0 -> output, dir = 1 -> input/open-drain */ + int (*set_gpio)(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value, + u8 xor_value); + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *i2c); +}; + +#if IS_REACHABLE(CONFIG_DVB_STV090x) + +struct dvb_frontend *stv090x_attach(struct stv090x_config *config, + struct i2c_adapter *i2c, + enum stv090x_demodulator demod); + +#else + +static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, + struct i2c_adapter *i2c, + enum stv090x_demodulator demod) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_STV090x */ + +#endif /* __STV090x_H */ diff --git a/drivers/media/dvb-frontends/stv090x_priv.h b/drivers/media/dvb-frontends/stv090x_priv.h new file mode 100644 index 0000000000..f8ece898c1 --- /dev/null +++ b/drivers/media/dvb-frontends/stv090x_priv.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STV0900/0903 Multistandard Broadcast Frontend driver + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STV090x_PRIV_H +#define __STV090x_PRIV_H + +#include <media/dvb_frontend.h> + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 +#define FE_DEBUGREG 4 + +#define dprintk(__y, __z, format, arg...) do { \ + if (__z) { \ + if ((verbose > FE_ERROR) && (verbose > __y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_NOTICE) && (verbose > __y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_INFO) && (verbose > __y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_DEBUG) && (verbose > __y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (verbose > __y) \ + printk(format, ##arg); \ + } \ +} while (0) + +#define STV090x_READ_DEMOD(__state, __reg) (( \ + (__state)->demod == STV090x_DEMODULATOR_1) ? \ + stv090x_read_reg(__state, STV090x_P2_##__reg) : \ + stv090x_read_reg(__state, STV090x_P1_##__reg)) + +#define STV090x_WRITE_DEMOD(__state, __reg, __data) (( \ + (__state)->demod == STV090x_DEMODULATOR_1) ? \ + stv090x_write_reg(__state, STV090x_P2_##__reg, __data) :\ + stv090x_write_reg(__state, STV090x_P1_##__reg, __data)) + +#define STV090x_ADDR_OFFST(__state, __x) (( \ + (__state->demod) == STV090x_DEMODULATOR_1) ? \ + STV090x_P1_##__x : \ + STV090x_P2_##__x) + + +#define STV090x_SETFIELD(mask, bitf, val) (mask = (mask & (~(((1 << STV090x_WIDTH_##bitf) - 1) <<\ + STV090x_OFFST_##bitf))) | \ + (val << STV090x_OFFST_##bitf)) + +#define STV090x_GETFIELD(val, bitf) ((val >> STV090x_OFFST_##bitf) & ((1 << STV090x_WIDTH_##bitf) - 1)) + + +#define STV090x_SETFIELD_Px(mask, bitf, val) (mask = (mask & (~(((1 << STV090x_WIDTH_Px_##bitf) - 1) <<\ + STV090x_OFFST_Px_##bitf))) | \ + (val << STV090x_OFFST_Px_##bitf)) + +#define STV090x_GETFIELD_Px(val, bitf) ((val >> STV090x_OFFST_Px_##bitf) & ((1 << STV090x_WIDTH_Px_##bitf) - 1)) + +#define MAKEWORD16(__a, __b) (((__a) << 8) | (__b)) + +#define MSB(__x) ((__x >> 8) & 0xff) +#define LSB(__x) (__x & 0xff) + + +#define STV090x_IQPOWER_THRESHOLD 30 +#define STV090x_SEARCH_AGC2_TH_CUT20 700 +#define STV090x_SEARCH_AGC2_TH_CUT30 1400 + +#define STV090x_SEARCH_AGC2_TH(__ver) \ + ((__ver <= 0x20) ? \ + STV090x_SEARCH_AGC2_TH_CUT20 : \ + STV090x_SEARCH_AGC2_TH_CUT30) + +enum stv090x_signal_state { + STV090x_NOAGC1, + STV090x_NOCARRIER, + STV090x_NODATA, + STV090x_DATAOK, + STV090x_RANGEOK, + STV090x_OUTOFRANGE +}; + +enum stv090x_fec { + STV090x_PR12 = 0, + STV090x_PR23, + STV090x_PR34, + STV090x_PR45, + STV090x_PR56, + STV090x_PR67, + STV090x_PR78, + STV090x_PR89, + STV090x_PR910, + STV090x_PRERR +}; + +enum stv090x_modulation { + STV090x_QPSK, + STV090x_8PSK, + STV090x_16APSK, + STV090x_32APSK, + STV090x_UNKNOWN +}; + +enum stv090x_frame { + STV090x_LONG_FRAME, + STV090x_SHORT_FRAME +}; + +enum stv090x_pilot { + STV090x_PILOTS_OFF, + STV090x_PILOTS_ON +}; + +enum stv090x_rolloff { + STV090x_RO_35, + STV090x_RO_25, + STV090x_RO_20 +}; + +enum stv090x_inversion { + STV090x_IQ_AUTO, + STV090x_IQ_NORMAL, + STV090x_IQ_SWAP +}; + +enum stv090x_modcod { + STV090x_DUMMY_PLF = 0, + STV090x_QPSK_14, + STV090x_QPSK_13, + STV090x_QPSK_25, + STV090x_QPSK_12, + STV090x_QPSK_35, + STV090x_QPSK_23, + STV090x_QPSK_34, + STV090x_QPSK_45, + STV090x_QPSK_56, + STV090x_QPSK_89, + STV090x_QPSK_910, + STV090x_8PSK_35, + STV090x_8PSK_23, + STV090x_8PSK_34, + STV090x_8PSK_56, + STV090x_8PSK_89, + STV090x_8PSK_910, + STV090x_16APSK_23, + STV090x_16APSK_34, + STV090x_16APSK_45, + STV090x_16APSK_56, + STV090x_16APSK_89, + STV090x_16APSK_910, + STV090x_32APSK_34, + STV090x_32APSK_45, + STV090x_32APSK_56, + STV090x_32APSK_89, + STV090x_32APSK_910, + STV090x_MODCODE_UNKNOWN +}; + +enum stv090x_search { + STV090x_SEARCH_DSS = 0, + STV090x_SEARCH_DVBS1, + STV090x_SEARCH_DVBS2, + STV090x_SEARCH_AUTO +}; + +enum stv090x_algo { + STV090x_BLIND_SEARCH, + STV090x_COLD_SEARCH, + STV090x_WARM_SEARCH +}; + +enum stv090x_delsys { + STV090x_ERROR = 0, + STV090x_DVBS1 = 1, + STV090x_DVBS2, + STV090x_DSS +}; + +struct stv090x_long_frame_crloop { + enum stv090x_modcod modcod; + + u8 crl_pilots_on_2; + u8 crl_pilots_off_2; + u8 crl_pilots_on_5; + u8 crl_pilots_off_5; + u8 crl_pilots_on_10; + u8 crl_pilots_off_10; + u8 crl_pilots_on_20; + u8 crl_pilots_off_20; + u8 crl_pilots_on_30; + u8 crl_pilots_off_30; +}; + +struct stv090x_short_frame_crloop { + enum stv090x_modulation modulation; + + u8 crl_2; /* SR < 3M */ + u8 crl_5; /* 3 < SR <= 7M */ + u8 crl_10; /* 7 < SR <= 15M */ + u8 crl_20; /* 10 < SR <= 25M */ + u8 crl_30; /* 10 < SR <= 45M */ +}; + +struct stv090x_reg { + u16 addr; + u8 data; +}; + +struct stv090x_tab { + s32 real; + s32 read; +}; + +struct stv090x_internal { + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + + struct mutex demod_lock; /* Lock access to shared register */ + struct mutex tuner_lock; /* Lock access to tuners */ + s32 mclk; /* Masterclock Divider factor */ + u32 dev_ver; + + int num_used; +}; + +struct stv090x_state { + enum stv090x_device device; + enum stv090x_demodulator demod; + enum stv090x_mode demod_mode; + struct stv090x_internal *internal; + + struct i2c_adapter *i2c; + struct stv090x_config *config; + struct dvb_frontend frontend; + + u32 *verbose; /* Cached module verbosity */ + + enum stv090x_delsys delsys; + enum stv090x_fec fec; + enum stv090x_modulation modulation; + enum stv090x_modcod modcod; + enum stv090x_search search_mode; + enum stv090x_frame frame_len; + enum stv090x_pilot pilots; + enum stv090x_rolloff rolloff; + enum stv090x_inversion inversion; + enum stv090x_algo algo; + + u32 frequency; + u32 srate; + + s32 tuner_bw; + + s32 search_range; + + s32 DemodTimeout; + s32 FecTimeout; +}; + +#endif /* __STV090x_PRIV_H */ diff --git a/drivers/media/dvb-frontends/stv090x_reg.h b/drivers/media/dvb-frontends/stv090x_reg.h new file mode 100644 index 0000000000..7fb2bb6894 --- /dev/null +++ b/drivers/media/dvb-frontends/stv090x_reg.h @@ -0,0 +1,2359 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STV0900/0903 Multistandard Broadcast Frontend driver + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STV090x_REG_H +#define __STV090x_REG_H + +#define STV090x_MID 0xf100 +#define STV090x_OFFST_MCHIP_IDENT_FIELD 4 +#define STV090x_WIDTH_MCHIP_IDENT_FIELD 4 +#define STV090x_OFFST_MRELEASE_FIELD 0 +#define STV090x_WIDTH_MRELEASE_FIELD 4 + +#define STV090x_DACR1 0xf113 +#define STV090x_OFFST_DACR1_MODE_FIELD 5 +#define STV090x_WIDTH_DACR1_MODE_FIELD 3 +#define STV090x_OFFST_DACR1_VALUE_FIELD 0 +#define STV090x_WIDTH_DACR1_VALUE_FIELD 4 + +#define STV090x_DACR2 0xf114 +#define STV090x_OFFST_DACR2_VALUE_FIELD 0 +#define STV090x_WIDTH_DACR2_VALUE_FIELD 8 + +#define STV090x_OUTCFG 0xf11c +#define STV090x_OFFST_OUTSERRS1_HZ_FIELD 6 +#define STV090x_WIDTH_OUTSERRS1_HZ_FIELD 1 +#define STV090x_OFFST_OUTSERRS2_HZ_FIELD 5 +#define STV090x_WIDTH_OUTSERRS2_HZ_FIELD 1 +#define STV090x_OFFST_OUTSERRS3_HZ_FIELD 4 +#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD 1 +#define STV090x_OFFST_OUTPARRS3_HZ_FIELD 3 +#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 + +#define STV090x_MODECFG 0xf11d + +#define STV090x_IRQSTATUS3 0xf120 +#define STV090x_OFFST_SPLL_LOCK_FIELD 5 +#define STV090x_WIDTH_SPLL_LOCK_FIELD 1 +#define STV090x_OFFST_SSTREAM_LCK_3_FIELD 4 +#define STV090x_WIDTH_SSTREAM_LCK_3_FIELD 1 +#define STV090x_OFFST_SSTREAM_LCK_2_FIELD 3 +#define STV090x_WIDTH_SSTREAM_LCK_2_FIELD 1 +#define STV090x_OFFST_SSTREAM_LCK_1_FIELD 2 +#define STV090x_WIDTH_SSTREAM_LCK_1_FIELD 1 +#define STV090x_OFFST_SDVBS1_PRF_2_FIELD 1 +#define STV090x_WIDTH_SDVBS1_PRF_2_FIELD 1 +#define STV090x_OFFST_SDVBS1_PRF_1_FIELD 0 +#define STV090x_WIDTH_SDVBS1_PRF_1_FIELD 1 + +#define STV090x_IRQSTATUS2 0xf121 +#define STV090x_OFFST_SSPY_ENDSIM_3_FIELD 7 +#define STV090x_WIDTH_SSPY_ENDSIM_3_FIELD 1 +#define STV090x_OFFST_SSPY_ENDSIM_2_FIELD 6 +#define STV090x_WIDTH_SSPY_ENDSIM_2_FIELD 1 +#define STV090x_OFFST_SSPY_ENDSIM_1_FIELD 5 +#define STV090x_WIDTH_SSPY_ENDSIM_1_FIELD 1 +#define STV090x_OFFST_SPKTDEL_ERROR_2_FIELD 4 +#define STV090x_WIDTH_SPKTDEL_ERROR_2_FIELD 1 +#define STV090x_OFFST_SPKTDEL_LOCKB_2_FIELD 3 +#define STV090x_WIDTH_SPKTDEL_LOCKB_2_FIELD 1 +#define STV090x_OFFST_SPKTDEL_LOCK_2_FIELD 2 +#define STV090x_WIDTH_SPKTDEL_LOCK_2_FIELD 1 +#define STV090x_OFFST_SPKTDEL_ERROR_1_FIELD 1 +#define STV090x_WIDTH_SPKTDEL_ERROR_1_FIELD 1 +#define STV090x_OFFST_SPKTDEL_LOCKB_1_FIELD 0 +#define STV090x_WIDTH_SPKTDEL_LOCKB_1_FIELD 1 + +#define STV090x_IRQSTATUS1 0xf122 +#define STV090x_OFFST_SPKTDEL_LOCK_1_FIELD 7 +#define STV090x_WIDTH_SPKTDEL_LOCK_1_FIELD 1 +#define STV090x_OFFST_SDEMOD_LOCKB_2_FIELD 2 +#define STV090x_WIDTH_SDEMOD_LOCKB_2_FIELD 1 +#define STV090x_OFFST_SDEMOD_LOCK_2_FIELD 1 +#define STV090x_WIDTH_SDEMOD_LOCK_2_FIELD 1 +#define STV090x_OFFST_SDEMOD_IRQ_2_FIELD 0 +#define STV090x_WIDTH_SDEMOD_IRQ_2_FIELD 1 + +#define STV090x_IRQSTATUS0 0xf123 +#define STV090x_OFFST_SDEMOD_LOCKB_1_FIELD 7 +#define STV090x_WIDTH_SDEMOD_LOCKB_1_FIELD 1 +#define STV090x_OFFST_SDEMOD_LOCK_1_FIELD 6 +#define STV090x_WIDTH_SDEMOD_LOCK_1_FIELD 1 +#define STV090x_OFFST_SDEMOD_IRQ_1_FIELD 5 +#define STV090x_WIDTH_SDEMOD_IRQ_1_FIELD 1 +#define STV090x_OFFST_SBCH_ERRFLAG_FIELD 4 +#define STV090x_WIDTH_SBCH_ERRFLAG_FIELD 1 +#define STV090x_OFFST_SDISEQC2RX_IRQ_FIELD 3 +#define STV090x_WIDTH_SDISEQC2RX_IRQ_FIELD 1 +#define STV090x_OFFST_SDISEQC2TX_IRQ_FIELD 2 +#define STV090x_WIDTH_SDISEQC2TX_IRQ_FIELD 1 +#define STV090x_OFFST_SDISEQC1RX_IRQ_FIELD 1 +#define STV090x_WIDTH_SDISEQC1RX_IRQ_FIELD 1 +#define STV090x_OFFST_SDISEQC1TX_IRQ_FIELD 0 +#define STV090x_WIDTH_SDISEQC1TX_IRQ_FIELD 1 + +#define STV090x_IRQMASK3 0xf124 +#define STV090x_OFFST_MPLL_LOCK_FIELD 5 +#define STV090x_WIDTH_MPLL_LOCK_FIELD 1 +#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 4 +#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 1 +#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 3 +#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 1 +#define STV090x_OFFST_MSTREAM_LCK_1_FIELD 2 +#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 1 +#define STV090x_OFFST_MDVBS1_PRF_2_FIELD 1 +#define STV090x_WIDTH_MDVBS1_PRF_2_FIELD 1 +#define STV090x_OFFST_MDVBS1_PRF_1_FIELD 0 +#define STV090x_WIDTH_MDVBS1_PRF_1_FIELD 1 + +#define STV090x_IRQMASK2 0xf125 +#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 7 +#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 1 +#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 6 +#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 1 +#define STV090x_OFFST_MSPY_ENDSIM_1_FIELD 5 +#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 1 +#define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD 4 +#define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD 1 +#define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD 3 +#define STV090x_WIDTH_MPKTDEL_LOCKB_2_FIELD 1 +#define STV090x_OFFST_MPKTDEL_LOCK_2_FIELD 2 +#define STV090x_WIDTH_MPKTDEL_LOCK_2_FIELD 1 +#define STV090x_OFFST_MPKTDEL_ERROR_1_FIELD 1 +#define STV090x_WIDTH_MPKTDEL_ERROR_1_FIELD 1 +#define STV090x_OFFST_MPKTDEL_LOCKB_1_FIELD 0 +#define STV090x_WIDTH_MPKTDEL_LOCKB_1_FIELD 1 + +#define STV090x_IRQMASK1 0xf126 +#define STV090x_OFFST_MPKTDEL_LOCK_1_FIELD 7 +#define STV090x_WIDTH_MPKTDEL_LOCK_1_FIELD 1 +#define STV090x_OFFST_MEXTPINB2_FIELD 6 +#define STV090x_WIDTH_MEXTPINB2_FIELD 1 +#define STV090x_OFFST_MEXTPIN2_FIELD 5 +#define STV090x_WIDTH_MEXTPIN2_FIELD 1 +#define STV090x_OFFST_MEXTPINB1_FIELD 4 +#define STV090x_WIDTH_MEXTPINB1_FIELD 1 +#define STV090x_OFFST_MEXTPIN1_FIELD 3 +#define STV090x_WIDTH_MEXTPIN1_FIELD 1 +#define STV090x_OFFST_MDEMOD_LOCKB_2_FIELD 2 +#define STV090x_WIDTH_MDEMOD_LOCKB_2_FIELD 1 +#define STV090x_OFFST_MDEMOD_LOCK_2_FIELD 1 +#define STV090x_WIDTH_MDEMOD_LOCK_2_FIELD 1 +#define STV090x_OFFST_MDEMOD_IRQ_2_FIELD 0 +#define STV090x_WIDTH_MDEMOD_IRQ_2_FIELD 1 + +#define STV090x_IRQMASK0 0xf127 +#define STV090x_OFFST_MDEMOD_LOCKB_1_FIELD 7 +#define STV090x_WIDTH_MDEMOD_LOCKB_1_FIELD 1 +#define STV090x_OFFST_MDEMOD_LOCK_1_FIELD 6 +#define STV090x_WIDTH_MDEMOD_LOCK_1_FIELD 1 +#define STV090x_OFFST_MDEMOD_IRQ_1_FIELD 5 +#define STV090x_WIDTH_MDEMOD_IRQ_1_FIELD 1 +#define STV090x_OFFST_MBCH_ERRFLAG_FIELD 4 +#define STV090x_WIDTH_MBCH_ERRFLAG_FIELD 1 +#define STV090x_OFFST_MDISEQC2RX_IRQ_FIELD 3 +#define STV090x_WIDTH_MDISEQC2RX_IRQ_FIELD 1 +#define STV090x_OFFST_MDISEQC2TX_IRQ_FIELD 2 +#define STV090x_WIDTH_MDISEQC2TX_IRQ_FIELD 1 +#define STV090x_OFFST_MDISEQC1RX_IRQ_FIELD 1 +#define STV090x_WIDTH_MDISEQC1RX_IRQ_FIELD 1 +#define STV090x_OFFST_MDISEQC1TX_IRQ_FIELD 0 +#define STV090x_WIDTH_MDISEQC1TX_IRQ_FIELD 1 + +#define STV090x_I2CCFG 0xf129 +#define STV090x_OFFST_12C_FASTMODE_FIELD 3 +#define STV090x_WIDTH_12C_FASTMODE_FIELD 1 +#define STV090x_OFFST_12CADDR_INC_FIELD 0 +#define STV090x_WIDTH_12CADDR_INC_FIELD 2 + +#define STV090x_Px_I2CRPT(__x) (0xf12a + (__x - 1) * 0x1) +#define STV090x_P1_I2CRPT STV090x_Px_I2CRPT(1) +#define STV090x_P2_I2CRPT STV090x_Px_I2CRPT(2) +#define STV090x_OFFST_Px_I2CT_ON_FIELD 7 +#define STV090x_WIDTH_Px_I2CT_ON_FIELD 1 +#define STV090x_OFFST_Px_ENARPT_LEVEL_FIELD 4 +#define STV090x_WIDTH_Px_ENARPT_LEVEL_FIELD 3 +#define STV090x_OFFST_Px_SCLT_DELAY_FIELD 3 +#define STV090x_WIDTH_Px_SCLT_DELAY_FIELD 1 +#define STV090x_OFFST_Px_STOP_ENABLE_FIELD 2 +#define STV090x_WIDTH_Px_STOP_ENABLE_FIELD 1 +#define STV090x_OFFST_Px_STOP_SDAT2SDA_FIELD 1 +#define STV090x_WIDTH_Px_STOP_SDAT2SDA_FIELD 1 + +#define STV090x_CLKI2CFG 0xf140 +#define STV090x_OFFST_CLKI2_OPD_FIELD 7 +#define STV090x_WIDTH_CLKI2_OPD_FIELD 1 +#define STV090x_OFFST_CLKI2_CONFIG_FIELD 1 +#define STV090x_WIDTH_CLKI2_CONFIG_FIELD 6 +#define STV090x_OFFST_CLKI2_XOR_FIELD 0 +#define STV090x_WIDTH_CLKI2_XOR_FIELD 1 + +#define STV090x_GPIOxCFG(__x) (0xf141 + (__x - 1)) +#define STV090x_GPIO1CFG STV090x_GPIOxCFG(1) +#define STV090x_GPIO2CFG STV090x_GPIOxCFG(2) +#define STV090x_GPIO3CFG STV090x_GPIOxCFG(3) +#define STV090x_GPIO4CFG STV090x_GPIOxCFG(4) +#define STV090x_GPIO5CFG STV090x_GPIOxCFG(5) +#define STV090x_GPIO6CFG STV090x_GPIOxCFG(6) +#define STV090x_GPIO7CFG STV090x_GPIOxCFG(7) +#define STV090x_GPIO8CFG STV090x_GPIOxCFG(8) +#define STV090x_GPIO9CFG STV090x_GPIOxCFG(9) +#define STV090x_GPIO10CFG STV090x_GPIOxCFG(10) +#define STV090x_GPIO11CFG STV090x_GPIOxCFG(11) +#define STV090x_GPIO12CFG STV090x_GPIOxCFG(12) +#define STV090x_GPIO13CFG STV090x_GPIOxCFG(13) +#define STV090x_OFFST_GPIOx_OPD_FIELD 7 +#define STV090x_WIDTH_GPIOx_OPD_FIELD 1 +#define STV090x_OFFST_GPIOx_CONFIG_FIELD 1 +#define STV090x_WIDTH_GPIOx_CONFIG_FIELD 6 +#define STV090x_OFFST_GPIOx_XOR_FIELD 0 +#define STV090x_WIDTH_GPIOx_XOR_FIELD 1 + +#define STV090x_CSxCFG(__x) (0xf14e + __x * 0x1) +#define STV090x_CS0CFG STV090x_CSxCFG(0) +#define STV090x_CS1CFG STV090x_CSxCFG(1) +#define STV090x_OFFST_CSX_OPD_FIELD 7 +#define STV090x_WIDTH_CSX_OPD_FIELD 1 +#define STV090x_OFFST_CSX_CONFIG_FIELD 1 +#define STV090x_WIDTH_CSX_CONFIG_FIELD 6 +#define STV090x_OFFST_CSX_XOR_FIELD 0 +#define STV090x_WIDTH_CSX_XOR_FIELD 1 + + +#define STV090x_STDBYCFG 0xf150 +#define STV090x_OFFST_STDBY_OPD_FIELD 7 +#define STV090x_WIDTH_STDBY_OPD_FIELD 1 +#define STV090x_OFFST_STDBY_CONFIG_FIELD 1 +#define STV090x_WIDTH_STDBY_CONFIG_FIELD 6 +#define STV090x_OFFST_STDBY_XOR_FIELD 0 +#define STV090x_WIDTH_STDBY_XOR_FIELD 1 + +#define STV090x_DIRCLKCFG 0xf151 +#define STV090x_OFFST_DIRCLK_OPD_FIELD 7 +#define STV090x_WIDTH_DIRCLK_OPD_FIELD 1 +#define STV090x_OFFST_DIRCLK_CONFIG_FIELD 1 +#define STV090x_WIDTH_DIRCLK_CONFIG_FIELD 6 +#define STV090x_OFFST_DIRCLK_XOR_FIELD 0 +#define STV090x_WIDTH_DIRCLK_XOR_FIELD 1 + + +#define STV090x_AGCRFxCFG(__x) (0xf152 + (__x - 1) * 0x4) +#define STV090x_AGCRF1CFG STV090x_AGCRFxCFG(1) +#define STV090x_AGCRF2CFG STV090x_AGCRFxCFG(2) +#define STV090x_OFFST_AGCRFx_OPD_FIELD 7 +#define STV090x_WIDTH_AGCRFx_OPD_FIELD 1 +#define STV090x_OFFST_AGCRFx_CONFIG_FIELD 1 +#define STV090x_WIDTH_AGCRFx_CONFIG_FIELD 6 +#define STV090x_OFFST_AGCRFx_XOR_FIELD 0 +#define STV090x_WIDTH_AGCRFx_XOR_FIELD 1 + +#define STV090x_SDATxCFG(__x) (0xf153 + (__x - 1) * 0x4) +#define STV090x_SDAT1CFG STV090x_SDATxCFG(1) +#define STV090x_SDAT2CFG STV090x_SDATxCFG(2) +#define STV090x_OFFST_SDATx_OPD_FIELD 7 +#define STV090x_WIDTH_SDATx_OPD_FIELD 1 +#define STV090x_OFFST_SDATx_CONFIG_FIELD 1 +#define STV090x_WIDTH_SDATx_CONFIG_FIELD 6 +#define STV090x_OFFST_SDATx_XOR_FIELD 0 +#define STV090x_WIDTH_SDATx_XOR_FIELD 1 + +#define STV090x_SCLTxCFG(__x) (0xf154 + (__x - 1) * 0x4) +#define STV090x_SCLT1CFG STV090x_SCLTxCFG(1) +#define STV090x_SCLT2CFG STV090x_SCLTxCFG(2) +#define STV090x_OFFST_SCLTx_OPD_FIELD 7 +#define STV090x_WIDTH_SCLTx_OPD_FIELD 1 +#define STV090x_OFFST_SCLTx_CONFIG_FIELD 1 +#define STV090x_WIDTH_SCLTx_CONFIG_FIELD 6 +#define STV090x_OFFST_SCLTx_XOR_FIELD 0 +#define STV090x_WIDTH_SCLTx_XOR_FIELD 1 + +#define STV090x_DISEQCOxCFG(__x) (0xf155 + (__x - 1) * 0x4) +#define STV090x_DISEQCO1CFG STV090x_DISEQCOxCFG(1) +#define STV090x_DISEQCO2CFG STV090x_DISEQCOxCFG(2) +#define STV090x_OFFST_DISEQCOx_OPD_FIELD 7 +#define STV090x_WIDTH_DISEQCOx_OPD_FIELD 1 +#define STV090x_OFFST_DISEQCOx_CONFIG_FIELD 1 +#define STV090x_WIDTH_DISEQCOx_CONFIG_FIELD 6 +#define STV090x_OFFST_DISEQCOx_XOR_FIELD 0 +#define STV090x_WIDTH_DISEQCOx_XOR_FIELD 1 + +#define STV090x_CLKOUT27CFG 0xf15a +#define STV090x_OFFST_CLKOUT27_OPD_FIELD 7 +#define STV090x_WIDTH_CLKOUT27_OPD_FIELD 1 +#define STV090x_OFFST_CLKOUT27_CONFIG_FIELD 1 +#define STV090x_WIDTH_CLKOUT27_CONFIG_FIELD 6 +#define STV090x_OFFST_CLKOUT27_XOR_FIELD 0 +#define STV090x_WIDTH_CLKOUT27_XOR_FIELD 1 + +#define STV090x_ERRORxCFG(__x) (0xf15b + (__x - 1) * 0x5) +#define STV090x_ERROR1CFG STV090x_ERRORxCFG(1) +#define STV090x_ERROR2CFG STV090x_ERRORxCFG(2) +#define STV090x_ERROR3CFG STV090x_ERRORxCFG(3) +#define STV090x_OFFST_ERRORx_OPD_FIELD 7 +#define STV090x_WIDTH_ERRORx_OPD_FIELD 1 +#define STV090x_OFFST_ERRORx_CONFIG_FIELD 1 +#define STV090x_WIDTH_ERRORx_CONFIG_FIELD 6 +#define STV090x_OFFST_ERRORx_XOR_FIELD 0 +#define STV090x_WIDTH_ERRORx_XOR_FIELD 1 + +#define STV090x_DPNxCFG(__x) (0xf15c + (__x - 1) * 0x5) +#define STV090x_DPN1CFG STV090x_DPNxCFG(1) +#define STV090x_DPN2CFG STV090x_DPNxCFG(2) +#define STV090x_DPN3CFG STV090x_DPNxCFG(3) +#define STV090x_OFFST_DPNx_OPD_FIELD 7 +#define STV090x_WIDTH_DPNx_OPD_FIELD 1 +#define STV090x_OFFST_DPNx_CONFIG_FIELD 1 +#define STV090x_WIDTH_DPNx_CONFIG_FIELD 6 +#define STV090x_OFFST_DPNx_XOR_FIELD 0 +#define STV090x_WIDTH_DPNx_XOR_FIELD 1 + +#define STV090x_STROUTxCFG(__x) (0xf15d + (__x - 1) * 0x5) +#define STV090x_STROUT1CFG STV090x_STROUTxCFG(1) +#define STV090x_STROUT2CFG STV090x_STROUTxCFG(2) +#define STV090x_STROUT3CFG STV090x_STROUTxCFG(3) +#define STV090x_OFFST_STROUTx_OPD_FIELD 7 +#define STV090x_WIDTH_STROUTx_OPD_FIELD 1 +#define STV090x_OFFST_STROUTx_CONFIG_FIELD 1 +#define STV090x_WIDTH_STROUTx_CONFIG_FIELD 6 +#define STV090x_OFFST_STROUTx_XOR_FIELD 0 +#define STV090x_WIDTH_STROUTx_XOR_FIELD 1 + +#define STV090x_CLKOUTxCFG(__x) (0xf15e + (__x - 1) * 0x5) +#define STV090x_CLKOUT1CFG STV090x_CLKOUTxCFG(1) +#define STV090x_CLKOUT2CFG STV090x_CLKOUTxCFG(2) +#define STV090x_CLKOUT3CFG STV090x_CLKOUTxCFG(3) +#define STV090x_OFFST_CLKOUTx_OPD_FIELD 7 +#define STV090x_WIDTH_CLKOUTx_OPD_FIELD 1 +#define STV090x_OFFST_CLKOUTx_CONFIG_FIELD 1 +#define STV090x_WIDTH_CLKOUTx_CONFIG_FIELD 6 +#define STV090x_OFFST_CLKOUTx_XOR_FIELD 0 +#define STV090x_WIDTH_CLKOUTx_XOR_FIELD 1 + +#define STV090x_DATAxCFG(__x) (0xf15f + (__x - 71) * 0x5) +#define STV090x_DATA71CFG STV090x_DATAxCFG(71) +#define STV090x_DATA72CFG STV090x_DATAxCFG(72) +#define STV090x_DATA73CFG STV090x_DATAxCFG(73) +#define STV090x_OFFST_DATAx_OPD_FIELD 7 +#define STV090x_WIDTH_DATAx_OPD_FIELD 1 +#define STV090x_OFFST_DATAx_CONFIG_FIELD 1 +#define STV090x_WIDTH_DATAx_CONFIG_FIELD 6 +#define STV090x_OFFST_DATAx_XOR_FIELD 0 +#define STV090x_WIDTH_DATAx_XOR_FIELD 1 + +#define STV090x_NCOARSE 0xf1b3 +#define STV090x_OFFST_M_DIV_FIELD 0 +#define STV090x_WIDTH_M_DIV_FIELD 8 + +#define STV090x_SYNTCTRL 0xf1b6 +#define STV090x_OFFST_STANDBY_FIELD 7 +#define STV090x_WIDTH_STANDBY_FIELD 1 +#define STV090x_OFFST_BYPASSPLLCORE_FIELD 6 +#define STV090x_WIDTH_BYPASSPLLCORE_FIELD 1 +#define STV090x_OFFST_SELX1RATIO_FIELD 5 +#define STV090x_WIDTH_SELX1RATIO_FIELD 1 +#define STV090x_OFFST_STOP_PLL_FIELD 3 +#define STV090x_WIDTH_STOP_PLL_FIELD 1 +#define STV090x_OFFST_BYPASSPLLFSK_FIELD 2 +#define STV090x_WIDTH_BYPASSPLLFSK_FIELD 1 +#define STV090x_OFFST_SELOSCI_FIELD 1 +#define STV090x_WIDTH_SELOSCI_FIELD 1 +#define STV090x_OFFST_BYPASSPLLADC_FIELD 0 +#define STV090x_WIDTH_BYPASSPLLADC_FIELD 1 + +#define STV090x_FILTCTRL 0xf1b7 +#define STV090x_OFFST_INV_CLK135_FIELD 7 +#define STV090x_WIDTH_INV_CLK135_FIELD 1 +#define STV090x_OFFST_SEL_FSKCKDIV_FIELD 2 +#define STV090x_WIDTH_SEL_FSKCKDIV_FIELD 1 +#define STV090x_OFFST_INV_CLKFSK_FIELD 1 +#define STV090x_WIDTH_INV_CLKFSK_FIELD 1 +#define STV090x_OFFST_BYPASS_APPLI_FIELD 0 +#define STV090x_WIDTH_BYPASS_APPLI_FIELD 1 + +#define STV090x_PLLSTAT 0xf1b8 +#define STV090x_OFFST_PLLLOCK_FIELD 0 +#define STV090x_WIDTH_PLLLOCK_FIELD 1 + +#define STV090x_STOPCLK1 0xf1c2 +#define STV090x_OFFST_STOP_CLKPKDT2_FIELD 6 +#define STV090x_WIDTH_STOP_CLKPKDT2_FIELD 1 +#define STV090x_OFFST_STOP_CLKPKDT1_FIELD 5 +#define STV090x_WIDTH_STOP_CLKPKDT1_FIELD 1 +#define STV090x_OFFST_STOP_CLKFEC_FIELD 4 +#define STV090x_WIDTH_STOP_CLKFEC_FIELD 1 +#define STV090x_OFFST_STOP_CLKADCI2_FIELD 3 +#define STV090x_WIDTH_STOP_CLKADCI2_FIELD 1 +#define STV090x_OFFST_INV_CLKADCI2_FIELD 2 +#define STV090x_WIDTH_INV_CLKADCI2_FIELD 1 +#define STV090x_OFFST_STOP_CLKADCI1_FIELD 1 +#define STV090x_WIDTH_STOP_CLKADCI1_FIELD 1 +#define STV090x_OFFST_INV_CLKADCI1_FIELD 0 +#define STV090x_WIDTH_INV_CLKADCI1_FIELD 1 + +#define STV090x_STOPCLK2 0xf1c3 +#define STV090x_OFFST_STOP_CLKSAMP2_FIELD 4 +#define STV090x_WIDTH_STOP_CLKSAMP2_FIELD 1 +#define STV090x_OFFST_STOP_CLKSAMP1_FIELD 3 +#define STV090x_WIDTH_STOP_CLKSAMP1_FIELD 1 +#define STV090x_OFFST_STOP_CLKVIT2_FIELD 2 +#define STV090x_WIDTH_STOP_CLKVIT2_FIELD 1 +#define STV090x_OFFST_STOP_CLKVIT1_FIELD 1 +#define STV090x_WIDTH_STOP_CLKVIT1_FIELD 1 +#define STV090x_OFFST_STOP_CLKTS_FIELD 0 +#define STV090x_WIDTH_STOP_CLKTS_FIELD 1 + +#define STV090x_TSTTNR0 0xf1df +#define STV090x_OFFST_SEL_FSK_FIELD 7 +#define STV090x_WIDTH_SEL_FSK_FIELD 1 +#define STV090x_OFFST_FSK_PON_FIELD 2 +#define STV090x_WIDTH_FSK_PON_FIELD 1 + +#define STV090x_TSTTNR1 0xf1e0 +#define STV090x_OFFST_ADC1_PON_FIELD 1 +#define STV090x_WIDTH_ADC1_PON_FIELD 1 +#define STV090x_OFFST_ADC1_INMODE_FIELD 0 +#define STV090x_WIDTH_ADC1_INMODE_FIELD 1 + +#define STV090x_TSTTNR2 0xf1e1 +#define STV090x_OFFST_DISEQC1_PON_FIELD 5 +#define STV090x_WIDTH_DISEQC1_PON_FIELD 1 + +#define STV090x_TSTTNR3 0xf1e2 +#define STV090x_OFFST_ADC2_PON_FIELD 1 +#define STV090x_WIDTH_ADC2_PON_FIELD 1 +#define STV090x_OFFST_ADC2_INMODE_FIELD 0 +#define STV090x_WIDTH_ADC2_INMODE_FIELD 1 + +#define STV090x_TSTTNR4 0xf1e3 +#define STV090x_OFFST_DISEQC2_PON_FIELD 5 +#define STV090x_WIDTH_DISEQC2_PON_FIELD 1 + +#define STV090x_FSKTFC2 0xf170 +#define STV090x_OFFST_FSKT_KMOD_FIELD 2 +#define STV090x_WIDTH_FSKT_KMOD_FIELD 6 +#define STV090x_OFFST_FSKT_CAR_FIELD 0 +#define STV090x_WIDTH_FSKT_CAR_FIELD 2 + +#define STV090x_FSKTFC1 0xf171 +#define STV090x_OFFST_FSKTC1_CAR_FIELD 0 +#define STV090x_WIDTH_FSKTC1_CAR_FIELD 8 + +#define STV090x_FSKTFC0 0xf172 +#define STV090x_OFFST_FSKTC0_CAR_FIELD 0 +#define STV090x_WIDTH_FSKTC0_CAR_FIELD 8 + +#define STV090x_FSKTDELTAF1 0xf173 +#define STV090x_OFFST_FSKTF1_DELTAF_FIELD 0 +#define STV090x_WIDTH_FSKTF1_DELTAF_FIELD 4 + +#define STV090x_FSKTDELTAF0 0xf174 +#define STV090x_OFFST_FSKTF0_DELTAF_FIELD 0 +#define STV090x_WIDTH_FSKTF0_DELTAF_FIELD 8 + +#define STV090x_FSKTCTRL 0xf175 +#define STV090x_OFFST_FSKT_EN_SGN_FIELD 6 +#define STV090x_WIDTH_FSKT_EN_SGN_FIELD 1 +#define STV090x_OFFST_FSKT_MOD_SGN_FIELD 5 +#define STV090x_WIDTH_FSKT_MOD_SGN_FIELD 1 +#define STV090x_OFFST_FSKT_MOD_EN_FIELD 2 +#define STV090x_WIDTH_FSKT_MOD_EN_FIELD 3 +#define STV090x_OFFST_FSKT_DACMODE_FIELD 0 +#define STV090x_WIDTH_FSKT_DACMODE_FIELD 2 + +#define STV090x_FSKRFC2 0xf176 +#define STV090x_OFFST_FSKRC2_DETSGN_FIELD 6 +#define STV090x_WIDTH_FSKRC2_DETSGN_FIELD 1 +#define STV090x_OFFST_FSKRC2_OUTSGN_FIELD 5 +#define STV090x_WIDTH_FSKRC2_OUTSGN_FIELD 1 +#define STV090x_OFFST_FSKRC2_KAGC_FIELD 2 +#define STV090x_WIDTH_FSKRC2_KAGC_FIELD 3 +#define STV090x_OFFST_FSKRC2_CAR_FIELD 0 +#define STV090x_WIDTH_FSKRC2_CAR_FIELD 2 + +#define STV090x_FSKRFC1 0xf177 +#define STV090x_OFFST_FSKRC1_CAR_FIELD 0 +#define STV090x_WIDTH_FSKRC1_CAR_FIELD 8 + +#define STV090x_FSKRFC0 0xf178 +#define STV090x_OFFST_FSKRC0_CAR_FIELD 0 +#define STV090x_WIDTH_FSKRC0_CAR_FIELD 8 + +#define STV090x_FSKRK1 0xf179 +#define STV090x_OFFST_FSKR_K1_EXP_FIELD 5 +#define STV090x_WIDTH_FSKR_K1_EXP_FIELD 3 +#define STV090x_OFFST_FSKR_K1_MANT_FIELD 0 +#define STV090x_WIDTH_FSKR_K1_MANT_FIELD 5 + +#define STV090x_FSKRK2 0xf17a +#define STV090x_OFFST_FSKR_K2_EXP_FIELD 5 +#define STV090x_WIDTH_FSKR_K2_EXP_FIELD 3 +#define STV090x_OFFST_FSKR_K2_MANT_FIELD 0 +#define STV090x_WIDTH_FSKR_K2_MANT_FIELD 5 + +#define STV090x_FSKRAGCR 0xf17b +#define STV090x_OFFST_FSKR_OUTCTL_FIELD 6 +#define STV090x_WIDTH_FSKR_OUTCTL_FIELD 2 +#define STV090x_OFFST_FSKR_AGC_REF_FIELD 0 +#define STV090x_WIDTH_FSKR_AGC_REF_FIELD 6 + +#define STV090x_FSKRAGC 0xf17c +#define STV090x_OFFST_FSKR_AGC_ACCU_FIELD 0 +#define STV090x_WIDTH_FSKR_AGC_ACCU_FIELD 8 + +#define STV090x_FSKRALPHA 0xf17d +#define STV090x_OFFST_FSKR_ALPHA_EXP_FIELD 2 +#define STV090x_WIDTH_FSKR_ALPHA_EXP_FIELD 3 +#define STV090x_OFFST_FSKR_ALPHA_M_FIELD 0 +#define STV090x_WIDTH_FSKR_ALPHA_M_FIELD 2 + +#define STV090x_FSKRPLTH1 0xf17e +#define STV090x_OFFST_FSKR_BETA_FIELD 4 +#define STV090x_WIDTH_FSKR_BETA_FIELD 4 +#define STV090x_OFFST_FSKR_PLL_TRESH1_FIELD 0 +#define STV090x_WIDTH_FSKR_PLL_TRESH1_FIELD 4 + +#define STV090x_FSKRPLTH0 0xf17f +#define STV090x_OFFST_FSKR_PLL_TRESH0_FIELD 0 +#define STV090x_WIDTH_FSKR_PLL_TRESH0_FIELD 8 + +#define STV090x_FSKRDF1 0xf180 +#define STV090x_OFFST_FSKR_DELTAF1_FIELD 0 +#define STV090x_WIDTH_FSKR_DELTAF1_FIELD 5 + +#define STV090x_FSKRDF0 0xf181 +#define STV090x_OFFST_FSKR_DELTAF0_FIELD 0 +#define STV090x_WIDTH_FSKR_DELTAF0_FIELD 8 + +#define STV090x_FSKRSTEPP 0xf182 +#define STV090x_OFFST_FSKR_STEP_PLUS_FIELD 0 +#define STV090x_WIDTH_FSKR_STEP_PLUS_FIELD 8 + +#define STV090x_FSKRSTEPM 0xf183 +#define STV090x_OFFST_FSKR_STEP_MINUS_FIELD 0 +#define STV090x_WIDTH_FSKR_STEP_MINUS_FIELD 8 + +#define STV090x_FSKRDET1 0xf184 +#define STV090x_OFFST_FSKR_CARDET1_ACCU_FIELD 0 +#define STV090x_WIDTH_FSKR_CARDET1_ACCU_FIELD 4 + +#define STV090x_FSKRDET0 0xf185 +#define STV090x_OFFST_FSKR_CARDET0_ACCU_FIELD 0 +#define STV090x_WIDTH_FSKR_CARDET0_ACCU_FIELD 8 + +#define STV090x_FSKRDTH1 0xf186 +#define STV090x_OFFST_FSKR_CARLOSS_THRESH1_FIELD 4 +#define STV090x_WIDTH_FSKR_CARLOSS_THRESH1_FIELD 4 +#define STV090x_OFFST_FSKR_CARDET_THRESH1_FIELD 0 +#define STV090x_WIDTH_FSKR_CARDET_THRESH1_FIELD 4 + +#define STV090x_FSKRDTH0 0xf187 +#define STV090x_OFFST_FSKR_CARDET_THRESH0_FIELD 0 +#define STV090x_WIDTH_FSKR_CARDET_THRESH0_FIELD 8 + +#define STV090x_FSKRLOSS 0xf188 +#define STV090x_OFFST_FSKR_CARLOSS_THRESH_FIELD 0 +#define STV090x_WIDTH_FSKR_CARLOSS_THRESH_FIELD 8 + +#define STV090x_Px_DISTXCTL(__x) (0xF1A0 - (__x - 1) * 0x10) +#define STV090x_P1_DISTXCTL STV090x_Px_DISTXCTL(1) +#define STV090x_P2_DISTXCTL STV090x_Px_DISTXCTL(2) +#define STV090x_OFFST_Px_TIM_OFF_FIELD 7 +#define STV090x_WIDTH_Px_TIM_OFF_FIELD 1 +#define STV090x_OFFST_Px_DISEQC_RESET_FIELD 6 +#define STV090x_WIDTH_Px_DISEQC_RESET_FIELD 1 +#define STV090x_OFFST_Px_TIM_CMD_FIELD 4 +#define STV090x_WIDTH_Px_TIM_CMD_FIELD 2 +#define STV090x_OFFST_Px_DIS_PRECHARGE_FIELD 3 +#define STV090x_WIDTH_Px_DIS_PRECHARGE_FIELD 1 +#define STV090x_OFFST_Px_DISTX_MODE_FIELD 0 +#define STV090x_WIDTH_Px_DISTX_MODE_FIELD 3 + +#define STV090x_Px_DISRXCTL(__x) (0xf1a1 - (__x - 1) * 0x10) +#define STV090x_P1_DISRXCTL STV090x_Px_DISRXCTL(1) +#define STV090x_P2_DISRXCTL STV090x_Px_DISRXCTL(2) +#define STV090x_OFFST_Px_RECEIVER_ON_FIELD 7 +#define STV090x_WIDTH_Px_RECEIVER_ON_FIELD 1 +#define STV090x_OFFST_Px_IGNO_SHORT22K_FIELD 6 +#define STV090x_WIDTH_Px_IGNO_SHORT22K_FIELD 1 +#define STV090x_OFFST_Px_ONECHIP_TRX_FIELD 5 +#define STV090x_WIDTH_Px_ONECHIP_TRX_FIELD 1 +#define STV090x_OFFST_Px_EXT_ENVELOP_FIELD 4 +#define STV090x_WIDTH_Px_EXT_ENVELOP_FIELD 1 +#define STV090x_OFFST_Px_PIN_SELECT_FIELD 2 +#define STV090x_WIDTH_Px_PIN_SELECT_FIELD 2 +#define STV090x_OFFST_Px_IRQ_RXEND_FIELD 1 +#define STV090x_WIDTH_Px_IRQ_RXEND_FIELD 1 +#define STV090x_OFFST_Px_IRQ_4NBYTES_FIELD 0 +#define STV090x_WIDTH_Px_IRQ_4NBYTES_FIELD 1 + +#define STV090x_Px_DISRX_ST0(__x) (0xf1a4 - (__x - 1) * 0x10) +#define STV090x_P1_DISRX_ST0 STV090x_Px_DISRX_ST0(1) +#define STV090x_P2_DISRX_ST0 STV090x_Px_DISRX_ST0(2) +#define STV090x_OFFST_Px_RX_END_FIELD 7 +#define STV090x_WIDTH_Px_RX_END_FIELD 1 +#define STV090x_OFFST_Px_RX_ACTIVE_FIELD 6 +#define STV090x_WIDTH_Px_RX_ACTIVE_FIELD 1 +#define STV090x_OFFST_Px_SHORT_22KHZ_FIELD 5 +#define STV090x_WIDTH_Px_SHORT_22KHZ_FIELD 1 +#define STV090x_OFFST_Px_CONT_TONE_FIELD 4 +#define STV090x_WIDTH_Px_CONT_TONE_FIELD 1 +#define STV090x_OFFST_Px_FIFO_4BREADY_FIELD 3 +#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 1 +#define STV090x_OFFST_Px_FIFO_EMPTY_FIELD 2 +#define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD 1 +#define STV090x_OFFST_Px_ABORT_DISRX_FIELD 0 +#define STV090x_WIDTH_Px_ABORT_DISRX_FIELD 1 + +#define STV090x_Px_DISRX_ST1(__x) (0xf1a5 - (__x - 1) * 0x10) +#define STV090x_P1_DISRX_ST1 STV090x_Px_DISRX_ST1(1) +#define STV090x_P2_DISRX_ST1 STV090x_Px_DISRX_ST1(2) +#define STV090x_OFFST_Px_RX_FAIL_FIELD 7 +#define STV090x_WIDTH_Px_RX_FAIL_FIELD 1 +#define STV090x_OFFST_Px_FIFO_PARITYFAIL_FIELD 6 +#define STV090x_WIDTH_Px_FIFO_PARITYFAIL_FIELD 1 +#define STV090x_OFFST_Px_RX_NONBYTE_FIELD 5 +#define STV090x_WIDTH_Px_RX_NONBYTE_FIELD 1 +#define STV090x_OFFST_Px_FIFO_OVERFLOW_FIELD 4 +#define STV090x_WIDTH_Px_FIFO_OVERFLOW_FIELD 1 +#define STV090x_OFFST_Px_FIFO_BYTENBR_FIELD 0 +#define STV090x_WIDTH_Px_FIFO_BYTENBR_FIELD 4 + +#define STV090x_Px_DISRXDATA(__x) (0xf1a6 - (__x - 1) * 0x10) +#define STV090x_P1_DISRXDATA STV090x_Px_DISRXDATA(1) +#define STV090x_P2_DISRXDATA STV090x_Px_DISRXDATA(2) +#define STV090x_OFFST_Px_DISRX_DATA_FIELD 0 +#define STV090x_WIDTH_Px_DISRX_DATA_FIELD 8 + +#define STV090x_Px_DISTXDATA(__x) (0xf1a7 - (__x - 1) * 0x10) +#define STV090x_P1_DISTXDATA STV090x_Px_DISTXDATA(1) +#define STV090x_P2_DISTXDATA STV090x_Px_DISTXDATA(2) +#define STV090x_OFFST_Px_DISEQC_FIFO_FIELD 0 +#define STV090x_WIDTH_Px_DISEQC_FIFO_FIELD 8 + +#define STV090x_Px_DISTXSTATUS(__x) (0xf1a8 - (__x - 1) * 0x10) +#define STV090x_P1_DISTXSTATUS STV090x_Px_DISTXSTATUS(1) +#define STV090x_P2_DISTXSTATUS STV090x_Px_DISTXSTATUS(2) +#define STV090x_OFFST_Px_TX_FAIL_FIELD 7 +#define STV090x_WIDTH_Px_TX_FAIL_FIELD 1 +#define STV090x_OFFST_Px_FIFO_FULL_FIELD 6 +#define STV090x_WIDTH_Px_FIFO_FULL_FIELD 1 +#define STV090x_OFFST_Px_TX_IDLE_FIELD 5 +#define STV090x_WIDTH_Px_TX_IDLE_FIELD 1 +#define STV090x_OFFST_Px_GAP_BURST_FIELD 4 +#define STV090x_WIDTH_Px_GAP_BURST_FIELD 1 +#define STV090x_OFFST_Px_TXFIFO_BYTES_FIELD 0 +#define STV090x_WIDTH_Px_TXFIFO_BYTES_FIELD 4 + +#define STV090x_Px_F22TX(__x) (0xf1a9 - (__x - 1) * 0x10) +#define STV090x_P1_F22TX STV090x_Px_F22TX(1) +#define STV090x_P2_F22TX STV090x_Px_F22TX(2) +#define STV090x_OFFST_Px_F22_REG_FIELD 0 +#define STV090x_WIDTH_Px_F22_REG_FIELD 8 + +#define STV090x_Px_F22RX(__x) (0xf1aa - (__x - 1) * 0x10) +#define STV090x_P1_F22RX STV090x_Px_F22RX(1) +#define STV090x_P2_F22RX STV090x_Px_F22RX(2) +#define STV090x_OFFST_Px_F22RX_REG_FIELD 0 +#define STV090x_WIDTH_Px_F22RX_REG_FIELD 8 + +#define STV090x_Px_ACRPRESC(__x) (0xf1ac - (__x - 1) * 0x10) +#define STV090x_P1_ACRPRESC STV090x_Px_ACRPRESC(1) +#define STV090x_P2_ACRPRESC STV090x_Px_ACRPRESC(2) +#define STV090x_OFFST_Px_ACR_PRESC_FIELD 0 +#define STV090x_WIDTH_Px_ACR_PRESC_FIELD 3 + +#define STV090x_Px_ACRDIV(__x) (0xf1ad - (__x - 1) * 0x10) +#define STV090x_P1_ACRDIV STV090x_Px_ACRDIV(1) +#define STV090x_P2_ACRDIV STV090x_Px_ACRDIV(2) +#define STV090x_OFFST_Px_ACR_DIV_FIELD 0 +#define STV090x_WIDTH_Px_ACR_DIV_FIELD 8 + +#define STV090x_Px_IQCONST(__x) (0xF400 - (__x - 1) * 0x200) +#define STV090x_P1_IQCONST STV090x_Px_IQCONST(1) +#define STV090x_P2_IQCONST STV090x_Px_IQCONST(2) +#define STV090x_OFFST_Px_CONSTEL_SELECT_FIELD 5 +#define STV090x_WIDTH_Px_CONSTEL_SELECT_FIELD 2 + +#define STV090x_Px_NOSCFG(__x) (0xF401 - (__x - 1) * 0x200) +#define STV090x_P1_NOSCFG STV090x_Px_NOSCFG(1) +#define STV090x_P2_NOSCFG STV090x_Px_NOSCFG(2) +#define STV090x_OFFST_Px_NOSPLH_BETA_FIELD 3 +#define STV090x_WIDTH_Px_NOSPLH_BETA_FIELD 2 +#define STV090x_OFFST_Px_NOSDATA_BETA_FIELD 0 +#define STV090x_WIDTH_Px_NOSDATA_BETA_FIELD 3 + +#define STV090x_Px_ISYMB(__x) (0xF402 - (__x - 1) * 0x200) +#define STV090x_P1_ISYMB STV090x_Px_ISYMB(1) +#define STV090x_P2_ISYMB STV090x_Px_ISYMB(2) +#define STV090x_OFFST_Px_I_SYMBOL_FIELD 0 +#define STV090x_WIDTH_Px_I_SYMBOL_FIELD 8 + +#define STV090x_Px_QSYMB(__x) (0xF403 - (__x - 1) * 0x200) +#define STV090x_P1_QSYMB STV090x_Px_QSYMB(1) +#define STV090x_P2_QSYMB STV090x_Px_QSYMB(2) +#define STV090x_OFFST_Px_Q_SYMBOL_FIELD 0 +#define STV090x_WIDTH_Px_Q_SYMBOL_FIELD 8 + +#define STV090x_Px_AGC1CFG(__x) (0xF404 - (__x - 1) * 0x200) +#define STV090x_P1_AGC1CFG STV090x_Px_AGC1CFG(1) +#define STV090x_P2_AGC1CFG STV090x_Px_AGC1CFG(2) +#define STV090x_OFFST_Px_DC_FROZEN_FIELD 7 +#define STV090x_WIDTH_Px_DC_FROZEN_FIELD 1 +#define STV090x_OFFST_Px_DC_CORRECT_FIELD 6 +#define STV090x_WIDTH_Px_DC_CORRECT_FIELD 1 +#define STV090x_OFFST_Px_AMM_FROZEN_FIELD 5 +#define STV090x_WIDTH_Px_AMM_FROZEN_FIELD 1 +#define STV090x_OFFST_Px_AMM_CORRECT_FIELD 4 +#define STV090x_WIDTH_Px_AMM_CORRECT_FIELD 1 +#define STV090x_OFFST_Px_QUAD_FROZEN_FIELD 3 +#define STV090x_WIDTH_Px_QUAD_FROZEN_FIELD 1 +#define STV090x_OFFST_Px_QUAD_CORRECT_FIELD 2 +#define STV090x_WIDTH_Px_QUAD_CORRECT_FIELD 1 + +#define STV090x_Px_AGC1CN(__x) (0xF406 - (__x - 1) * 0x200) +#define STV090x_P1_AGC1CN STV090x_Px_AGC1CN(1) +#define STV090x_P2_AGC1CN STV090x_Px_AGC1CN(2) +#define STV090x_WIDTH_Px_AGC1_LOCKED_FIELD 7 +#define STV090x_OFFST_Px_AGC1_LOCKED_FIELD 1 +#define STV090x_OFFST_Px_AGC1_MINPOWER_FIELD 4 +#define STV090x_WIDTH_Px_AGC1_MINPOWER_FIELD 1 +#define STV090x_OFFST_Px_AGCOUT_FAST_FIELD 3 +#define STV090x_WIDTH_Px_AGCOUT_FAST_FIELD 1 +#define STV090x_OFFST_Px_AGCIQ_BETA_FIELD 0 +#define STV090x_WIDTH_Px_AGCIQ_BETA_FIELD 3 + +#define STV090x_Px_AGC1REF(__x) (0xF407 - (__x - 1) * 0x200) +#define STV090x_P1_AGC1REF STV090x_Px_AGC1REF(1) +#define STV090x_P2_AGC1REF STV090x_Px_AGC1REF(2) +#define STV090x_OFFST_Px_AGCIQ_REF_FIELD 0 +#define STV090x_WIDTH_Px_AGCIQ_REF_FIELD 8 + +#define STV090x_Px_IDCCOMP(__x) (0xF408 - (__x - 1) * 0x200) +#define STV090x_P1_IDCCOMP STV090x_Px_IDCCOMP(1) +#define STV090x_P2_IDCCOMP STV090x_Px_IDCCOMP(2) +#define STV090x_OFFST_Px_IAVERAGE_ADJ_FIELD 0 +#define STV090x_WIDTH_Px_IAVERAGE_ADJ_FIELD 8 + +#define STV090x_Px_QDCCOMP(__x) (0xF409 - (__x - 1) * 0x200) +#define STV090x_P1_QDCCOMP STV090x_Px_QDCCOMP(1) +#define STV090x_P2_QDCCOMP STV090x_Px_QDCCOMP(2) +#define STV090x_OFFST_Px_QAVERAGE_ADJ_FIELD 0 +#define STV090x_WIDTH_Px_QAVERAGE_ADJ_FIELD 8 + +#define STV090x_Px_POWERI(__x) (0xF40A - (__x - 1) * 0x200) +#define STV090x_P1_POWERI STV090x_Px_POWERI(1) +#define STV090x_P2_POWERI STV090x_Px_POWERI(2) +#define STV090x_OFFST_Px_POWER_I_FIELD 0 +#define STV090x_WIDTH_Px_POWER_I_FIELD 8 + +#define STV090x_Px_POWERQ(__x) (0xF40B - (__x - 1) * 0x200) +#define STV090x_P1_POWERQ STV090x_Px_POWERQ(1) +#define STV090x_P2_POWERQ STV090x_Px_POWERQ(2) +#define STV090x_OFFST_Px_POWER_Q_FIELD 0 +#define STV090x_WIDTH_Px_POWER_Q_FIELD 8 + +#define STV090x_Px_AGC1AMM(__x) (0xF40C - (__x - 1) * 0x200) +#define STV090x_P1_AGC1AMM STV090x_Px_AGC1AMM(1) +#define STV090x_P2_AGC1AMM STV090x_Px_AGC1AMM(2) +#define STV090x_OFFST_Px_AMM_VALUE_FIELD 0 +#define STV090x_WIDTH_Px_AMM_VALUE_FIELD 8 + +#define STV090x_Px_AGC1QUAD(__x) (0xF40D - (__x - 1) * 0x200) +#define STV090x_P1_AGC1QUAD STV090x_Px_AGC1QUAD(1) +#define STV090x_P2_AGC1QUAD STV090x_Px_AGC1QUAD(2) +#define STV090x_OFFST_Px_QUAD_VALUE_FIELD 0 +#define STV090x_WIDTH_Px_QUAD_VALUE_FIELD 8 + +#define STV090x_Px_AGCIQINy(__x, __y) (0xF40F - (__x-1) * 0x200 - __y * 0x1) +#define STV090x_P1_AGCIQIN0 STV090x_Px_AGCIQINy(1, 0) +#define STV090x_P1_AGCIQIN1 STV090x_Px_AGCIQINy(1, 1) +#define STV090x_P2_AGCIQIN0 STV090x_Px_AGCIQINy(2, 0) +#define STV090x_P2_AGCIQIN1 STV090x_Px_AGCIQINy(2, 1) +#define STV090x_OFFST_Px_AGCIQ_VALUE_FIELD 0 +#define STV090x_WIDTH_Px_AGCIQ_VALUE_FIELD 8 + +#define STV090x_Px_DEMOD(__x) (0xF410 - (__x - 1) * 0x200) +#define STV090x_P1_DEMOD STV090x_Px_DEMOD(1) +#define STV090x_P2_DEMOD STV090x_Px_DEMOD(2) +#define STV090x_OFFST_Px_MANUAL_S2ROLLOFF_FIELD 7 +#define STV090x_WIDTH_Px_MANUAL_S2ROLLOFF_FIELD 1 +#define STV090x_OFFST_Px_DEMOD_STOP_FIELD 6 +#define STV090x_WIDTH_Px_DEMOD_STOP_FIELD 1 +#define STV090x_OFFST_Px_SPECINV_CONTROL_FIELD 4 +#define STV090x_WIDTH_Px_SPECINV_CONTROL_FIELD 2 +#define STV090x_OFFST_Px_FORCE_ENASAMP_FIELD 3 +#define STV090x_WIDTH_Px_FORCE_ENASAMP_FIELD 1 +#define STV090x_OFFST_Px_MANUAL_SXROLLOFF_FIELD 2 +#define STV090x_WIDTH_Px_MANUAL_SXROLLOFF_FIELD 1 +#define STV090x_OFFST_Px_ROLLOFF_CONTROL_FIELD 0 +#define STV090x_WIDTH_Px_ROLLOFF_CONTROL_FIELD 2 + +#define STV090x_Px_DMDMODCOD(__x) (0xF411 - (__x - 1) * 0x200) +#define STV090x_P1_DMDMODCOD STV090x_Px_DMDMODCOD(1) +#define STV090x_P2_DMDMODCOD STV090x_Px_DMDMODCOD(2) +#define STV090x_OFFST_Px_MANUAL_MODCOD_FIELD 7 +#define STV090x_WIDTH_Px_MANUAL_MODCOD_FIELD 1 +#define STV090x_OFFST_Px_DEMOD_MODCOD_FIELD 2 +#define STV090x_WIDTH_Px_DEMOD_MODCOD_FIELD 5 +#define STV090x_OFFST_Px_DEMOD_TYPE_FIELD 0 +#define STV090x_WIDTH_Px_DEMOD_TYPE_FIELD 2 + +#define STV090x_Px_DSTATUS(__x) (0xF412 - (__x - 1) * 0x200) +#define STV090x_P1_DSTATUS STV090x_Px_DSTATUS(1) +#define STV090x_P2_DSTATUS STV090x_Px_DSTATUS(2) +#define STV090x_OFFST_Px_CAR_LOCK_FIELD 7 +#define STV090x_WIDTH_Px_CAR_LOCK_FIELD 1 +#define STV090x_OFFST_Px_TMGLOCK_QUALITY_FIELD 5 +#define STV090x_WIDTH_Px_TMGLOCK_QUALITY_FIELD 2 +#define STV090x_OFFST_Px_LOCK_DEFINITIF_FIELD 3 +#define STV090x_WIDTH_Px_LOCK_DEFINITIF_FIELD 1 + +#define STV090x_Px_DSTATUS2(__x) (0xF413 - (__x - 1) * 0x200) +#define STV090x_P1_DSTATUS2 STV090x_Px_DSTATUS2(1) +#define STV090x_P2_DSTATUS2 STV090x_Px_DSTATUS2(2) +#define STV090x_OFFST_Px_DEMOD_DELOCK_FIELD 7 +#define STV090x_WIDTH_Px_DEMOD_DELOCK_FIELD 1 +#define STV090x_OFFST_Px_AGC1_NOSIGNALACK_FIELD 3 +#define STV090x_WIDTH_Px_AGC1_NOSIGNALACK_FIELD 1 +#define STV090x_OFFST_Px_AGC2_OVERFLOW_FIELD 2 +#define STV090x_WIDTH_Px_AGC2_OVERFLOW_FIELD 1 +#define STV090x_OFFST_Px_CFR_OVERFLOW_FIELD 1 +#define STV090x_WIDTH_Px_CFR_OVERFLOW_FIELD 1 +#define STV090x_OFFST_Px_GAMMA_OVERUNDER_FIELD 0 +#define STV090x_WIDTH_Px_GAMMA_OVERUNDER_FIELD 1 + +#define STV090x_Px_DMDCFGMD(__x) (0xF414 - (__x - 1) * 0x200) +#define STV090x_P1_DMDCFGMD STV090x_Px_DMDCFGMD(1) +#define STV090x_P2_DMDCFGMD STV090x_Px_DMDCFGMD(2) +#define STV090x_OFFST_Px_DVBS2_ENABLE_FIELD 7 +#define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD 1 +#define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD 6 +#define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD 1 +#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 +#define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD 1 +#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 3 +#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 +#define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD 2 +#define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD 1 +#define STV090x_OFFST_Px_TUN_RNG_FIELD 0 +#define STV090x_WIDTH_Px_TUN_RNG_FIELD 2 + +#define STV090x_Px_DMDCFG2(__x) (0xF415 - (__x - 1) * 0x200) +#define STV090x_P1_DMDCFG2 STV090x_Px_DMDCFG2(1) +#define STV090x_P2_DMDCFG2 STV090x_Px_DMDCFG2(2) +#define STV090x_OFFST_Px_S1S2_SEQUENTIAL_FIELD 6 +#define STV090x_WIDTH_Px_S1S2_SEQUENTIAL_FIELD 1 + +#define STV090x_Px_DMDISTATE(__x) (0xF416 - (__x - 1) * 0x200) +#define STV090x_P1_DMDISTATE STV090x_Px_DMDISTATE(1) +#define STV090x_P2_DMDISTATE STV090x_Px_DMDISTATE(2) +#define STV090x_OFFST_Px_I2C_DEMOD_MODE_FIELD 0 +#define STV090x_WIDTH_Px_I2C_DEMOD_MODE_FIELD 5 + +#define STV090x_Px_DMDTOM(__x) (0xF417 - (__x - 1) * 0x200) /* check */ +#define STV090x_P1_DMDTOM STV090x_Px_DMDTOM(1) +#define STV090x_P2_DMDTOM STV090x_Px_DMDTOM(2) + +#define STV090x_Px_DMDSTATE(__x) (0xF41B - (__x - 1) * 0x200) +#define STV090x_P1_DMDSTATE STV090x_Px_DMDSTATE(1) +#define STV090x_P2_DMDSTATE STV090x_Px_DMDSTATE(2) +#define STV090x_OFFST_Px_HEADER_MODE_FIELD 5 +#define STV090x_WIDTH_Px_HEADER_MODE_FIELD 2 + +#define STV090x_Px_DMDFLYW(__x) (0xF41C - (__x - 1) * 0x200) +#define STV090x_P1_DMDFLYW STV090x_Px_DMDFLYW(1) +#define STV090x_P2_DMDFLYW STV090x_Px_DMDFLYW(2) +#define STV090x_OFFST_Px_I2C_IRQVAL_FIELD 4 +#define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD 4 +#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 +#define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD 4 + +#define STV090x_Px_DSTATUS3(__x) (0xF41D - (__x - 1) * 0x200) +#define STV090x_P1_DSTATUS3 STV090x_Px_DSTATUS3(1) +#define STV090x_P2_DSTATUS3 STV090x_Px_DSTATUS3(2) +#define STV090x_OFFST_Px_DEMOD_CFGMODE_FIELD 5 +#define STV090x_WIDTH_Px_DEMOD_CFGMODE_FIELD 2 + +#define STV090x_Px_DMDCFG3(__x) (0xF41E - (__x - 1) * 0x200) +#define STV090x_P1_DMDCFG3 STV090x_Px_DMDCFG3(1) +#define STV090x_P2_DMDCFG3 STV090x_Px_DMDCFG3(2) +#define STV090x_OFFST_Px_NOSTOP_FIFOFULL_FIELD 3 +#define STV090x_WIDTH_Px_NOSTOP_FIFOFULL_FIELD 1 + +#define STV090x_Px_DMDCFG4(__x) (0xf41f - (__x - 1) * 0x200) +#define STV090x_P1_DMDCFG4 STV090x_Px_DMDCFG4(1) +#define STV090x_P2_DMDCFG4 STV090x_Px_DMDCFG4(2) + +#define STV090x_Px_CORRELMANT(__x) (0xF420 - (__x - 1) * 0x200) +#define STV090x_P1_CORRELMANT STV090x_Px_CORRELMANT(1) +#define STV090x_P2_CORRELMANT STV090x_Px_CORRELMANT(2) +#define STV090x_OFFST_Px_CORREL_MANT_FIELD 0 +#define STV090x_WIDTH_Px_CORREL_MANT_FIELD 8 + +#define STV090x_Px_CORRELABS(__x) (0xF421 - (__x - 1) * 0x200) +#define STV090x_P1_CORRELABS STV090x_Px_CORRELABS(1) +#define STV090x_P2_CORRELABS STV090x_Px_CORRELABS(2) +#define STV090x_OFFST_Px_CORREL_ABS_FIELD 0 +#define STV090x_WIDTH_Px_CORREL_ABS_FIELD 8 + +#define STV090x_Px_CORRELEXP(__x) (0xF422 - (__x - 1) * 0x200) +#define STV090x_P1_CORRELEXP STV090x_Px_CORRELEXP(1) +#define STV090x_P2_CORRELEXP STV090x_Px_CORRELEXP(2) +#define STV090x_OFFST_Px_CORREL_ABSEXP_FIELD 4 +#define STV090x_WIDTH_Px_CORREL_ABSEXP_FIELD 4 +#define STV090x_OFFST_Px_CORREL_EXP_FIELD 0 +#define STV090x_WIDTH_Px_CORREL_EXP_FIELD 4 + +#define STV090x_Px_PLHMODCOD(__x) (0xF424 - (__x - 1) * 0x200) +#define STV090x_P1_PLHMODCOD STV090x_Px_PLHMODCOD(1) +#define STV090x_P2_PLHMODCOD STV090x_Px_PLHMODCOD(2) +#define STV090x_OFFST_Px_SPECINV_DEMOD_FIELD 7 +#define STV090x_WIDTH_Px_SPECINV_DEMOD_FIELD 1 +#define STV090x_OFFST_Px_PLH_MODCOD_FIELD 2 +#define STV090x_WIDTH_Px_PLH_MODCOD_FIELD 5 +#define STV090x_OFFST_Px_PLH_TYPE_FIELD 0 +#define STV090x_WIDTH_Px_PLH_TYPE_FIELD 2 + +#define STV090x_Px_AGCK32(__x) (0xf42b - (__x - 1) * 0x200) +#define STV090x_P1_AGCK32 STV090x_Px_AGCK32(1) +#define STV090x_P2_AGCK32 STV090x_Px_AGCK32(2) + +#define STV090x_Px_AGC2O(__x) (0xF42C - (__x - 1) * 0x200) +#define STV090x_P1_AGC2O STV090x_Px_AGC2O(1) +#define STV090x_P2_AGC2O STV090x_Px_AGC2O(2) + +#define STV090x_Px_AGC2REF(__x) (0xF42D - (__x - 1) * 0x200) +#define STV090x_P1_AGC2REF STV090x_Px_AGC2REF(1) +#define STV090x_P2_AGC2REF STV090x_Px_AGC2REF(2) +#define STV090x_OFFST_Px_AGC2_REF_FIELD 0 +#define STV090x_WIDTH_Px_AGC2_REF_FIELD 8 + +#define STV090x_Px_AGC1ADJ(__x) (0xF42E - (__x - 1) * 0x200) +#define STV090x_P1_AGC1ADJ STV090x_Px_AGC1ADJ(1) +#define STV090x_P2_AGC1ADJ STV090x_Px_AGC1ADJ(2) +#define STV090x_OFFST_Px_AGC1_ADJUSTED_FIELD 0 +#define STV090x_WIDTH_Px_AGC1_ADJUSTED_FIELD 7 + +#define STV090x_Px_AGC2Iy(__x, __y) (0xF437 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_AGC2I0 STV090x_Px_AGC2Iy(1, 0) +#define STV090x_P1_AGC2I1 STV090x_Px_AGC2Iy(1, 1) +#define STV090x_P2_AGC2I0 STV090x_Px_AGC2Iy(2, 0) +#define STV090x_P2_AGC2I1 STV090x_Px_AGC2Iy(2, 1) +#define STV090x_OFFST_Px_AGC2_INTEGRATOR_FIELD 0 +#define STV090x_WIDTH_Px_AGC2_INTEGRATOR_FIELD 8 + +#define STV090x_Px_CARCFG(__x) (0xF438 - (__x - 1) * 0x200) +#define STV090x_P1_CARCFG STV090x_Px_CARCFG(1) +#define STV090x_P2_CARCFG STV090x_Px_CARCFG(2) +#define STV090x_OFFST_Px_EN_CAR2CENTER_FIELD 5 +#define STV090x_WIDTH_Px_EN_CAR2CENTER_FIELD 1 +#define STV090x_OFFST_Px_ROTATON_FIELD 2 +#define STV090x_WIDTH_Px_ROTATON_FIELD 1 +#define STV090x_OFFST_Px_PH_DET_ALGO_FIELD 0 +#define STV090x_WIDTH_Px_PH_DET_ALGO_FIELD 2 + +#define STV090x_Px_ACLC(__x) (0xF439 - (__x - 1) * 0x200) +#define STV090x_P1_ACLC STV090x_Px_ACLC(1) +#define STV090x_P2_ACLC STV090x_Px_ACLC(2) +#define STV090x_OFFST_Px_CAR_ALPHA_MANT_FIELD 4 +#define STV090x_WIDTH_Px_CAR_ALPHA_MANT_FIELD 2 +#define STV090x_OFFST_Px_CAR_ALPHA_EXP_FIELD 0 +#define STV090x_WIDTH_Px_CAR_ALPHA_EXP_FIELD 4 + +#define STV090x_Px_BCLC(__x) (0xF43A - (__x - 1) * 0x200) +#define STV090x_P1_BCLC STV090x_Px_BCLC(1) +#define STV090x_P2_BCLC STV090x_Px_BCLC(2) +#define STV090x_OFFST_Px_CAR_BETA_MANT_FIELD 4 +#define STV090x_WIDTH_Px_CAR_BETA_MANT_FIELD 2 +#define STV090x_OFFST_Px_CAR_BETA_EXP_FIELD 0 +#define STV090x_WIDTH_Px_CAR_BETA_EXP_FIELD 4 + +#define STV090x_Px_CARFREQ(__x) (0xF43D - (__x - 1) * 0x200) +#define STV090x_P1_CARFREQ STV090x_Px_CARFREQ(1) +#define STV090x_P2_CARFREQ STV090x_Px_CARFREQ(2) +#define STV090x_OFFST_Px_KC_COARSE_EXP_FIELD 4 +#define STV090x_WIDTH_Px_KC_COARSE_EXP_FIELD 4 +#define STV090x_OFFST_Px_BETA_FREQ_FIELD 0 +#define STV090x_WIDTH_Px_BETA_FREQ_FIELD 4 + +#define STV090x_Px_CARHDR(__x) (0xF43E - (__x - 1) * 0x200) +#define STV090x_P1_CARHDR STV090x_Px_CARHDR(1) +#define STV090x_P2_CARHDR STV090x_Px_CARHDR(2) +#define STV090x_OFFST_Px_FREQ_HDR_FIELD 0 +#define STV090x_WIDTH_Px_FREQ_HDR_FIELD 8 + +#define STV090x_Px_LDT(__x) (0xF43F - (__x - 1) * 0x200) +#define STV090x_P1_LDT STV090x_Px_LDT(1) +#define STV090x_P2_LDT STV090x_Px_LDT(2) +#define STV090x_OFFST_Px_CARLOCK_THRES_FIELD 0 +#define STV090x_WIDTH_Px_CARLOCK_THRES_FIELD 8 + +#define STV090x_Px_LDT2(__x) (0xF440 - (__x - 1) * 0x200) +#define STV090x_P1_LDT2 STV090x_Px_LDT2(1) +#define STV090x_P2_LDT2 STV090x_Px_LDT2(2) +#define STV090x_OFFST_Px_CARLOCK_THRES2_FIELD 0 +#define STV090x_WIDTH_Px_CARLOCK_THRES2_FIELD 8 + +#define STV090x_Px_CFRICFG(__x) (0xF441 - (__x - 1) * 0x200) +#define STV090x_P1_CFRICFG STV090x_Px_CFRICFG(1) +#define STV090x_P2_CFRICFG STV090x_Px_CFRICFG(2) +#define STV090x_OFFST_Px_NEG_CFRSTEP_FIELD 0 +#define STV090x_WIDTH_Px_NEG_CFRSTEP_FIELD 1 + +#define STV090x_Pn_CFRUPy(__x, __y) (0xF443 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_CFRUP0 STV090x_Pn_CFRUPy(1, 0) +#define STV090x_P1_CFRUP1 STV090x_Pn_CFRUPy(1, 1) +#define STV090x_P2_CFRUP0 STV090x_Pn_CFRUPy(2, 0) +#define STV090x_P2_CFRUP1 STV090x_Pn_CFRUPy(2, 1) +#define STV090x_OFFST_Px_CFR_UP_FIELD 0 +#define STV090x_WIDTH_Px_CFR_UP_FIELD 8 + +#define STV090x_Pn_CFRLOWy(__x, __y) (0xF447 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_CFRLOW0 STV090x_Pn_CFRLOWy(1, 0) +#define STV090x_P1_CFRLOW1 STV090x_Pn_CFRLOWy(1, 1) +#define STV090x_P2_CFRLOW0 STV090x_Pn_CFRLOWy(2, 0) +#define STV090x_P2_CFRLOW1 STV090x_Pn_CFRLOWy(2, 1) +#define STV090x_OFFST_Px_CFR_LOW_FIELD 0 +#define STV090x_WIDTH_Px_CFR_LOW_FIELD 8 + +#define STV090x_Pn_CFRINITy(__x, __y) (0xF449 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_CFRINIT0 STV090x_Pn_CFRINITy(1, 0) +#define STV090x_P1_CFRINIT1 STV090x_Pn_CFRINITy(1, 1) +#define STV090x_P2_CFRINIT0 STV090x_Pn_CFRINITy(2, 0) +#define STV090x_P2_CFRINIT1 STV090x_Pn_CFRINITy(2, 1) +#define STV090x_OFFST_Px_CFR_INIT_FIELD 0 +#define STV090x_WIDTH_Px_CFR_INIT_FIELD 8 + +#define STV090x_Px_CFRINC1(__x) (0xF44A - (__x - 1) * 0x200) +#define STV090x_P1_CFRINC1 STV090x_Px_CFRINC1(1) +#define STV090x_P2_CFRINC1 STV090x_Px_CFRINC1(2) +#define STV090x_OFFST_Px_CFR_INC1_FIELD 0 +#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 /* check */ + +#define STV090x_Px_CFRINC0(__x) (0xF44B - (__x - 1) * 0x200) +#define STV090x_P1_CFRINC0 STV090x_Px_CFRINC0(1) +#define STV090x_P2_CFRINC0 STV090x_Px_CFRINC0(2) +#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 /* check */ +#define STV090x_WIDTH_Px_CFR_INC0_FIELD 4 + +#define STV090x_Pn_CFRy(__x, __y) (0xF44E - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_CFR0 STV090x_Pn_CFRy(1, 0) +#define STV090x_P1_CFR1 STV090x_Pn_CFRy(1, 1) +#define STV090x_P1_CFR2 STV090x_Pn_CFRy(1, 2) +#define STV090x_P2_CFR0 STV090x_Pn_CFRy(2, 0) +#define STV090x_P2_CFR1 STV090x_Pn_CFRy(2, 1) +#define STV090x_P2_CFR2 STV090x_Pn_CFRy(2, 2) +#define STV090x_OFFST_Px_CAR_FREQ_FIELD 0 +#define STV090x_WIDTH_Px_CAR_FREQ_FIELD 8 + +#define STV090x_Px_LDI(__x) (0xF44F - (__x - 1) * 0x200) +#define STV090x_P1_LDI STV090x_Px_LDI(1) +#define STV090x_P2_LDI STV090x_Px_LDI(2) +#define STV090x_OFFST_Px_LOCK_DET_INTEGR_FIELD 0 +#define STV090x_WIDTH_Px_LOCK_DET_INTEGR_FIELD 8 + +#define STV090x_Px_TMGCFG(__x) (0xF450 - (__x - 1) * 0x200) +#define STV090x_P1_TMGCFG STV090x_Px_TMGCFG(1) +#define STV090x_P2_TMGCFG STV090x_Px_TMGCFG(2) +#define STV090x_OFFST_Px_TMGLOCK_BETA_FIELD 6 +#define STV090x_WIDTH_Px_TMGLOCK_BETA_FIELD 2 +#define STV090x_OFFST_Px_DO_TIMING_FIELD 4 +#define STV090x_WIDTH_Px_DO_TIMING_FIELD 1 +#define STV090x_OFFST_Px_TMG_MINFREQ_FIELD 0 +#define STV090x_WIDTH_Px_TMG_MINFREQ_FIELD 2 + +#define STV090x_Px_RTC(__x) (0xF451 - (__x - 1) * 0x200) +#define STV090x_P1_RTC STV090x_Px_RTC(1) +#define STV090x_P2_RTC STV090x_Px_RTC(2) +#define STV090x_OFFST_Px_TMGALPHA_EXP_FIELD 4 +#define STV090x_WIDTH_Px_TMGALPHA_EXP_FIELD 4 +#define STV090x_OFFST_Px_TMGBETA_EXP_FIELD 0 +#define STV090x_WIDTH_Px_TMGBETA_EXP_FIELD 4 + +#define STV090x_Px_RTCS2(__x) (0xF452 - (__x - 1) * 0x200) +#define STV090x_P1_RTCS2 STV090x_Px_RTCS2(1) +#define STV090x_P2_RTCS2 STV090x_Px_RTCS2(2) +#define STV090x_OFFST_Px_TMGALPHAS2_EXP_FIELD 4 +#define STV090x_WIDTH_Px_TMGALPHAS2_EXP_FIELD 4 +#define STV090x_OFFST_Px_TMGBETAS2_EXP_FIELD 0 +#define STV090x_WIDTH_Px_TMGBETAS2_EXP_FIELD 4 + +#define STV090x_Px_TMGTHRISE(__x) (0xF453 - (__x - 1) * 0x200) +#define STV090x_P1_TMGTHRISE STV090x_Px_TMGTHRISE(1) +#define STV090x_P2_TMGTHRISE STV090x_Px_TMGTHRISE(2) +#define STV090x_OFFST_Px_TMGLOCK_THRISE_FIELD 0 +#define STV090x_WIDTH_Px_TMGLOCK_THRISE_FIELD 8 + +#define STV090x_Px_TMGTHFALL(__x) (0xF454 - (__x - 1) * 0x200) +#define STV090x_P1_TMGTHFALL STV090x_Px_TMGTHFALL(1) +#define STV090x_P2_TMGTHFALL STV090x_Px_TMGTHFALL(2) +#define STV090x_OFFST_Px_TMGLOCK_THFALL_FIELD 0 +#define STV090x_WIDTH_Px_TMGLOCK_THFALL_FIELD 8 + +#define STV090x_Px_SFRUPRATIO(__x) (0xF455 - (__x - 1) * 0x200) +#define STV090x_P1_SFRUPRATIO STV090x_Px_SFRUPRATIO(1) +#define STV090x_P2_SFRUPRATIO STV090x_Px_SFRUPRATIO(2) +#define STV090x_OFFST_Px_SFR_UPRATIO_FIELD 0 +#define STV090x_WIDTH_Px_SFR_UPRATIO_FIELD 8 + +#define STV090x_Px_SFRLOWRATIO(__x) (0xF456 - (__x - 1) * 0x200) +#define STV090x_P1_SFRLOWRATIO STV090x_Px_SFRLOWRATIO(1) +#define STV090x_P2_SFRLOWRATIO STV090x_Px_SFRLOWRATIO(2) +#define STV090x_OFFST_Px_SFR_LOWRATIO_FIELD 0 +#define STV090x_WIDTH_Px_SFR_LOWRATIO_FIELD 8 + +#define STV090x_Px_KREFTMG(__x) (0xF458 - (__x - 1) * 0x200) +#define STV090x_P1_KREFTMG STV090x_Px_KREFTMG(1) +#define STV090x_P2_KREFTMG STV090x_Px_KREFTMG(2) +#define STV090x_OFFST_Px_KREF_TMG_FIELD 0 +#define STV090x_WIDTH_Px_KREF_TMG_FIELD 8 + +#define STV090x_Px_SFRSTEP(__x) (0xF459 - (__x - 1) * 0x200) +#define STV090x_P1_SFRSTEP STV090x_Px_SFRSTEP(1) +#define STV090x_P2_SFRSTEP STV090x_Px_SFRSTEP(2) +#define STV090x_OFFST_Px_SFR_SCANSTEP_FIELD 4 +#define STV090x_WIDTH_Px_SFR_SCANSTEP_FIELD 4 +#define STV090x_OFFST_Px_SFR_CENTERSTEP_FIELD 0 +#define STV090x_WIDTH_Px_SFR_CENTERSTEP_FIELD 4 + +#define STV090x_Px_TMGCFG2(__x) (0xF45A - (__x - 1) * 0x200) +#define STV090x_P1_TMGCFG2 STV090x_Px_TMGCFG2(1) +#define STV090x_P2_TMGCFG2 STV090x_Px_TMGCFG2(2) +#define STV090x_OFFST_Px_SFRRATIO_FINE_FIELD 0 +#define STV090x_WIDTH_Px_SFRRATIO_FINE_FIELD 1 + +#define STV090x_Px_SFRINIT1(__x) (0xF45E - (__x - 1) * 0x200) +#define STV090x_P1_SFRINIT1 STV090x_Px_SFRINIT1(1) +#define STV090x_P2_SFRINIT1 STV090x_Px_SFRINIT1(2) +#define STV090x_OFFST_Px_SFR_INIT1_FIELD 0 +#define STV090x_WIDTH_Px_SFR_INIT1_FIELD 7 + +#define STV090x_Px_SFRINIT0(__x) (0xF45F - (__x - 1) * 0x200) +#define STV090x_P1_SFRINIT0 STV090x_Px_SFRINIT0(1) +#define STV090x_P2_SFRINIT0 STV090x_Px_SFRINIT0(2) +#define STV090x_OFFST_Px_SFR_INIT0_FIELD 0 +#define STV090x_WIDTH_Px_SFR_INIT0_FIELD 8 + +#define STV090x_Px_SFRUP1(__x) (0xF460 - (__x - 1) * 0x200) +#define STV090x_P1_SFRUP1 STV090x_Px_SFRUP1(1) +#define STV090x_P2_SFRUP1 STV090x_Px_SFRUP1(2) +#define STV090x_OFFST_Px_SYMB_FREQ_UP1_FIELD 0 +#define STV090x_WIDTH_Px_SYMB_FREQ_UP1_FIELD 7 + +#define STV090x_Px_SFRUP0(__x) (0xF461 - (__x - 1) * 0x200) +#define STV090x_P1_SFRUP0 STV090x_Px_SFRUP0(1) +#define STV090x_P2_SFRUP0 STV090x_Px_SFRUP0(2) +#define STV090x_OFFST_Px_SYMB_FREQ_UP0_FIELD 0 +#define STV090x_WIDTH_Px_SYMB_FREQ_UP0_FIELD 8 + +#define STV090x_Px_SFRLOW1(__x) (0xF462 - (__x - 1) * 0x200) +#define STV090x_P1_SFRLOW1 STV090x_Px_SFRLOW1(1) +#define STV090x_P2_SFRLOW1 STV090x_Px_SFRLOW1(2) +#define STV090x_OFFST_Px_SYMB_FREQ_LOW1_FIELD 0 +#define STV090x_WIDTH_Px_SYMB_FREQ_LOW1_FIELD 7 + +#define STV090x_Px_SFRLOW0(__x) (0xF463 - (__x - 1) * 0x200) +#define STV090x_P1_SFRLOW0 STV090x_Px_SFRLOW0(1) +#define STV090x_P2_SFRLOW0 STV090x_Px_SFRLOW0(2) +#define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD 0 +#define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD 8 + +#define STV090x_Px_SFRy(__x, __y) (0xF467 - (__x-1) * 0x200 - __y) +#define STV090x_P1_SFR0 STV090x_Px_SFRy(1, 0) +#define STV090x_P1_SFR1 STV090x_Px_SFRy(1, 1) +#define STV090x_P1_SFR2 STV090x_Px_SFRy(1, 2) +#define STV090x_P1_SFR3 STV090x_Px_SFRy(1, 3) +#define STV090x_P2_SFR0 STV090x_Px_SFRy(2, 0) +#define STV090x_P2_SFR1 STV090x_Px_SFRy(2, 1) +#define STV090x_P2_SFR2 STV090x_Px_SFRy(2, 2) +#define STV090x_P2_SFR3 STV090x_Px_SFRy(2, 3) +#define STV090x_OFFST_Px_SYMB_FREQ_FIELD 0 +#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 8 + +#define STV090x_Px_TMGREG2(__x) (0xF468 - (__x - 1) * 0x200) +#define STV090x_P1_TMGREG2 STV090x_Px_TMGREG2(1) +#define STV090x_P2_TMGREG2 STV090x_Px_TMGREG2(2) +#define STV090x_OFFST_Px_TMGREG_FIELD 0 +#define STV090x_WIDTH_Px_TMGREG_FIELD 8 + +#define STV090x_Px_TMGREG1(__x) (0xF469 - (__x - 1) * 0x200) +#define STV090x_P1_TMGREG1 STV090x_Px_TMGREG1(1) +#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) +#define STV090x_OFFST_Px_TMGREG_FIELD 0 +#define STV090x_WIDTH_Px_TMGREG_FIELD 8 + +#define STV090x_Px_TMGREG0(__x) (0xF46A - (__x - 1) * 0x200) +#define STV090x_P1_TMGREG0 STV090x_Px_TMGREG0(1) +#define STV090x_P2_TMGREG0 STV090x_Px_TMGREG0(2) +#define STV090x_OFFST_Px_TMGREG_FIELD 0 +#define STV090x_WIDTH_Px_TMGREG_FIELD 8 + +#define STV090x_Px_TMGLOCKy(__x, __y) (0xF46C - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_TMGLOCK0 STV090x_Px_TMGLOCKy(1, 0) +#define STV090x_P1_TMGLOCK1 STV090x_Px_TMGLOCKy(1, 1) +#define STV090x_P2_TMGLOCK0 STV090x_Px_TMGLOCKy(2, 0) +#define STV090x_P2_TMGLOCK1 STV090x_Px_TMGLOCKy(2, 1) +#define STV090x_OFFST_Px_TMGLOCK_LEVEL_FIELD 0 +#define STV090x_WIDTH_Px_TMGLOCK_LEVEL_FIELD 8 + +#define STV090x_Px_TMGOBS(__x) (0xF46D - (__x - 1) * 0x200) +#define STV090x_P1_TMGOBS STV090x_Px_TMGOBS(1) +#define STV090x_P2_TMGOBS STV090x_Px_TMGOBS(2) +#define STV090x_OFFST_Px_ROLLOFF_STATUS_FIELD 6 +#define STV090x_WIDTH_Px_ROLLOFF_STATUS_FIELD 2 + +#define STV090x_Px_EQUALCFG(__x) (0xF46F - (__x - 1) * 0x200) +#define STV090x_P1_EQUALCFG STV090x_Px_EQUALCFG(1) +#define STV090x_P2_EQUALCFG STV090x_Px_EQUALCFG(2) +#define STV090x_OFFST_Px_EQUAL_ON_FIELD 6 +#define STV090x_WIDTH_Px_EQUAL_ON_FIELD 1 +#define STV090x_OFFST_Px_MU_EQUALDFE_FIELD 0 +#define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD 3 + +#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x-1) * 0x200 + 2 * (__y-1)) +#define STV090x_P1_EQUAI1 STV090x_Px_EQUAIy(1, 1) +#define STV090x_P1_EQUAI2 STV090x_Px_EQUAIy(1, 2) +#define STV090x_P1_EQUAI3 STV090x_Px_EQUAIy(1, 3) +#define STV090x_P1_EQUAI4 STV090x_Px_EQUAIy(1, 4) +#define STV090x_P1_EQUAI5 STV090x_Px_EQUAIy(1, 5) +#define STV090x_P1_EQUAI6 STV090x_Px_EQUAIy(1, 6) +#define STV090x_P1_EQUAI7 STV090x_Px_EQUAIy(1, 7) +#define STV090x_P1_EQUAI8 STV090x_Px_EQUAIy(1, 8) + +#define STV090x_P2_EQUAI1 STV090x_Px_EQUAIy(2, 1) +#define STV090x_P2_EQUAI2 STV090x_Px_EQUAIy(2, 2) +#define STV090x_P2_EQUAI3 STV090x_Px_EQUAIy(2, 3) +#define STV090x_P2_EQUAI4 STV090x_Px_EQUAIy(2, 4) +#define STV090x_P2_EQUAI5 STV090x_Px_EQUAIy(2, 5) +#define STV090x_P2_EQUAI6 STV090x_Px_EQUAIy(2, 6) +#define STV090x_P2_EQUAI7 STV090x_Px_EQUAIy(2, 7) +#define STV090x_P2_EQUAI8 STV090x_Px_EQUAIy(2, 8) +#define STV090x_OFFST_Px_EQUA_ACCIy_FIELD 0 +#define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD 8 + +#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x-1) * 0x200 + 2 * (__y-1)) +#define STV090x_P1_EQUAQ1 STV090x_Px_EQUAQy(1, 1) +#define STV090x_P1_EQUAQ2 STV090x_Px_EQUAQy(1, 2) +#define STV090x_P1_EQUAQ3 STV090x_Px_EQUAQy(1, 3) +#define STV090x_P1_EQUAQ4 STV090x_Px_EQUAQy(1, 4) +#define STV090x_P1_EQUAQ5 STV090x_Px_EQUAQy(1, 5) +#define STV090x_P1_EQUAQ6 STV090x_Px_EQUAQy(1, 6) +#define STV090x_P1_EQUAQ7 STV090x_Px_EQUAQy(1, 7) +#define STV090x_P1_EQUAQ8 STV090x_Px_EQUAQy(1, 8) + +#define STV090x_P2_EQUAQ1 STV090x_Px_EQUAQy(2, 1) +#define STV090x_P2_EQUAQ2 STV090x_Px_EQUAQy(2, 2) +#define STV090x_P2_EQUAQ3 STV090x_Px_EQUAQy(2, 3) +#define STV090x_P2_EQUAQ4 STV090x_Px_EQUAQy(2, 4) +#define STV090x_P2_EQUAQ5 STV090x_Px_EQUAQy(2, 5) +#define STV090x_P2_EQUAQ6 STV090x_Px_EQUAQy(2, 6) +#define STV090x_P2_EQUAQ7 STV090x_Px_EQUAQy(2, 7) +#define STV090x_P2_EQUAQ8 STV090x_Px_EQUAQy(2, 8) +#define STV090x_OFFST_Px_EQUA_ACCQy_FIELD 0 +#define STV090x_WIDTH_Px_EQUA_ACCQy_FIELD 8 + +#define STV090x_Px_NNOSDATATy(__x, __y) (0xf481 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NNOSDATAT0 STV090x_Px_NNOSDATATy(1, 0) +#define STV090x_P1_NNOSDATAT1 STV090x_Px_NNOSDATATy(1, 1) +#define STV090x_P2_NNOSDATAT0 STV090x_Px_NNOSDATATy(2, 0) +#define STV090x_P2_NNOSDATAT1 STV090x_Px_NNOSDATATy(2, 1) +#define STV090x_OFFST_Px_NOSDATAT_NORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSDATAT_NORMED_FIELD 8 + +#define STV090x_Px_NNOSDATAy(__x, __y) (0xf483 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NNOSDATA0 STV090x_Px_NNOSDATAy(1, 0) +#define STV090x_P1_NNOSDATA1 STV090x_Px_NNOSDATAy(1, 1) +#define STV090x_P2_NNOSDATA0 STV090x_Px_NNOSDATAy(2, 0) +#define STV090x_P2_NNOSDATA1 STV090x_Px_NNOSDATAy(2, 1) +#define STV090x_OFFST_Px_NOSDATA_NORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSDATA_NORMED_FIELD 8 + +#define STV090x_Px_NNOSPLHTy(__x, __y) (0xf485 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NNOSPLHT0 STV090x_Px_NNOSPLHTy(1, 0) +#define STV090x_P1_NNOSPLHT1 STV090x_Px_NNOSPLHTy(1, 1) +#define STV090x_P2_NNOSPLHT0 STV090x_Px_NNOSPLHTy(2, 0) +#define STV090x_P2_NNOSPLHT1 STV090x_Px_NNOSPLHTy(2, 1) +#define STV090x_OFFST_Px_NOSPLHT_NORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSPLHT_NORMED_FIELD 8 + +#define STV090x_Px_NNOSPLHy(__x, __y) (0xf487 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NNOSPLH0 STV090x_Px_NNOSPLHy(1, 0) +#define STV090x_P1_NNOSPLH1 STV090x_Px_NNOSPLHy(1, 1) +#define STV090x_P2_NNOSPLH0 STV090x_Px_NNOSPLHy(2, 0) +#define STV090x_P2_NNOSPLH1 STV090x_Px_NNOSPLHy(2, 1) +#define STV090x_OFFST_Px_NOSPLH_NORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSPLH_NORMED_FIELD 8 + +#define STV090x_Px_NOSDATATy(__x, __y) (0xf489 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NOSDATAT0 STV090x_Px_NOSDATATy(1, 0) +#define STV090x_P1_NOSDATAT1 STV090x_Px_NOSDATATy(1, 1) +#define STV090x_P2_NOSDATAT0 STV090x_Px_NOSDATATy(2, 0) +#define STV090x_P2_NOSDATAT1 STV090x_Px_NOSDATATy(2, 1) +#define STV090x_OFFST_Px_NOSDATAT_UNNORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSDATAT_UNNORMED_FIELD 8 + +#define STV090x_Px_NOSDATAy(__x, __y) (0xf48b - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NOSDATA0 STV090x_Px_NOSDATAy(1, 0) +#define STV090x_P1_NOSDATA1 STV090x_Px_NOSDATAy(1, 1) +#define STV090x_P2_NOSDATA0 STV090x_Px_NOSDATAy(2, 0) +#define STV090x_P2_NOSDATA1 STV090x_Px_NOSDATAy(2, 1) +#define STV090x_OFFST_Px_NOSDATA_UNNORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSDATA_UNNORMED_FIELD 8 + +#define STV090x_Px_NOSPLHTy(__x, __y) (0xf48d - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NOSPLHT0 STV090x_Px_NOSPLHTy(1, 0) +#define STV090x_P1_NOSPLHT1 STV090x_Px_NOSPLHTy(1, 1) +#define STV090x_P2_NOSPLHT0 STV090x_Px_NOSPLHTy(2, 0) +#define STV090x_P2_NOSPLHT1 STV090x_Px_NOSPLHTy(2, 1) +#define STV090x_OFFST_Px_NOSPLHT_UNNORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSPLHT_UNNORMED_FIELD 8 + +#define STV090x_Px_NOSPLHy(__x, __y) (0xf48f - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0) +#define STV090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1) +#define STV090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0) +#define STV090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1) +#define STV090x_OFFST_Px_NOSPLH_UNNORMED_FIELD 0 +#define STV090x_WIDTH_Px_NOSPLH_UNNORMED_FIELD 8 + +#define STV090x_Px_CAR2CFG(__x) (0xf490 - (__x - 1) * 0x200) +#define STV090x_P1_CAR2CFG STV090x_Px_CAR2CFG(1) +#define STV090x_P2_CAR2CFG STV090x_Px_CAR2CFG(2) +#define STV090x_OFFST_Px_PN4_SELECT_FIELD 6 +#define STV090x_WIDTH_Px_PN4_SELECT_FIELD 1 +#define STV090x_OFFST_Px_CFR2_STOPDVBS1_FIELD 5 +#define STV090x_WIDTH_Px_CFR2_STOPDVBS1_FIELD 1 +#define STV090x_OFFST_Px_ROTA2ON_FIELD 2 +#define STV090x_WIDTH_Px_ROTA2ON_FIELD 1 +#define STV090x_OFFST_Px_PH_DET_ALGO2_FIELD 0 +#define STV090x_WIDTH_Px_PH_DET_ALGO2_FIELD 2 + +#define STV090x_Px_ACLC2(__x) (0xf491 - (__x - 1) * 0x200) +#define STV090x_P1_ACLC2 STV090x_Px_ACLC2(1) +#define STV090x_P2_ACLC2 STV090x_Px_ACLC2(2) +#define STV090x_OFFST_Px_CAR2_ALPHA_MANT_FIELD 4 +#define STV090x_WIDTH_Px_CAR2_ALPHA_MANT_FIELD 2 +#define STV090x_OFFST_Px_CAR2_ALPHA_EXP_FIELD 0 +#define STV090x_WIDTH_Px_CAR2_ALPHA_EXP_FIELD 4 + +#define STV090x_Px_BCLC2(__x) (0xf492 - (__x - 1) * 0x200) +#define STV090x_P1_BCLC2 STV090x_Px_BCLC2(1) +#define STV090x_P2_BCLC2 STV090x_Px_BCLC2(2) +#define STV090x_OFFST_Px_CAR2_BETA_MANT_FIELD 4 +#define STV090x_WIDTH_Px_CAR2_BETA_MANT_FIELD 2 +#define STV090x_OFFST_Px_CAR2_BETA_EXP_FIELD 0 +#define STV090x_WIDTH_Px_CAR2_BETA_EXP_FIELD 4 + +#define STV090x_Px_ACLC2S2Q(__x) (0xf497 - (__x - 1) * 0x200) +#define STV090x_P1_ACLC2S2Q STV090x_Px_ACLC2S2Q(1) +#define STV090x_P2_ACLC2S2Q STV090x_Px_ACLC2S2Q(2) +#define STV090x_OFFST_Px_ENAB_SPSKSYMB_FIELD 7 +#define STV090x_WIDTH_Px_ENAB_SPSKSYMB_FIELD 1 +#define STV090x_OFFST_Px_CAR2S2_Q_ALPH_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_Q_ALPH_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_Q_ALPH_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_Q_ALPH_E_FIELD 4 + +#define STV090x_Px_ACLC2S28(__x) (0xf498 - (__x - 1) * 0x200) +#define STV090x_P1_ACLC2S28 STV090x_Px_ACLC2S28(1) +#define STV090x_P2_ACLC2S28 STV090x_Px_ACLC2S28(2) +#define STV090x_OFFST_Px_CAR2S2_8_ALPH_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_8_ALPH_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_8_ALPH_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_8_ALPH_E_FIELD 4 + +#define STV090x_Px_ACLC2S216A(__x) (0xf499 - (__x - 1) * 0x200) +#define STV090x_P1_ACLC2S216A STV090x_Px_ACLC2S216A(1) +#define STV090x_P2_ACLC2S216A STV090x_Px_ACLC2S216A(2) +#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD 4 + +#define STV090x_Px_ACLC2S232A(__x) (0xf49A - (__x - 1) * 0x200) +#define STV090x_P1_ACLC2S232A STV090x_Px_ACLC2S232A(1) +#define STV090x_P2_ACLC2S232A STV090x_Px_ACLC2S232A(2) +#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_32A_ALPH_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_32A_ALPH_E_FIELD 4 + +#define STV090x_Px_BCLC2S2Q(__x) (0xf49c - (__x - 1) * 0x200) +#define STV090x_P1_BCLC2S2Q STV090x_Px_BCLC2S2Q(1) +#define STV090x_P2_BCLC2S2Q STV090x_Px_BCLC2S2Q(2) +#define STV090x_OFFST_Px_CAR2S2_Q_BETA_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_Q_BETA_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_Q_BETA_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_Q_BETA_E_FIELD 4 + +#define STV090x_Px_BCLC2S28(__x) (0xf49d - (__x - 1) * 0x200) +#define STV090x_P1_BCLC2S28 STV090x_Px_BCLC2S28(1) +#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(2) +#define STV090x_OFFST_Px_CAR2S2_8_BETA_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_8_BETA_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4 + +#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200) +#define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1) +#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(2) +#define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD 4 + +#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200) +#define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1) +#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(2) +#define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4 +#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_M_FIELD 2 +#define STV090x_OFFST_Px_CAR2S2_32A_BETA_E_FIELD 0 +#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_E_FIELD 4 + +#define STV090x_Px_PLROOT2(__x) (0xf4ac - (__x - 1) * 0x200) +#define STV090x_P1_PLROOT2 STV090x_Px_PLROOT2(1) +#define STV090x_P2_PLROOT2 STV090x_Px_PLROOT2(2) +#define STV090x_OFFST_Px_PLSCRAMB_MODE_FIELD 2 +#define STV090x_WIDTH_Px_PLSCRAMB_MODE_FIELD 2 +#define STV090x_OFFST_Px_PLSCRAMB_ROOT_FIELD 0 +#define STV090x_WIDTH_Px_PLSCRAMB_ROOT_FIELD 2 + +#define STV090x_Px_PLROOT1(__x) (0xf4ad - (__x - 1) * 0x200) +#define STV090x_P1_PLROOT1 STV090x_Px_PLROOT1(1) +#define STV090x_P2_PLROOT1 STV090x_Px_PLROOT1(2) +#define STV090x_OFFST_Px_PLSCRAMB_ROOT1_FIELD 0 +#define STV090x_WIDTH_Px_PLSCRAMB_ROOT1_FIELD 8 + +#define STV090x_Px_PLROOT0(__x) (0xf4ae - (__x - 1) * 0x200) +#define STV090x_P1_PLROOT0 STV090x_Px_PLROOT0(1) +#define STV090x_P2_PLROOT0 STV090x_Px_PLROOT0(2) +#define STV090x_OFFST_Px_PLSCRAMB_ROOT0_FIELD 0 +#define STV090x_WIDTH_Px_PLSCRAMB_ROOT0_FIELD 8 + +#define STV090x_Px_MODCODLST0(__x) (0xf4b0 - (__x - 1) * 0x200) /* check */ +#define STV090x_P1_MODCODLST0 STV090x_Px_MODCODLST0(1) +#define STV090x_P2_MODCODLST0 STV090x_Px_MODCODLST0(2) + +#define STV090x_Px_MODCODLST1(__x) (0xf4b1 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST1 STV090x_Px_MODCODLST1(1) +#define STV090x_P2_MODCODLST1 STV090x_Px_MODCODLST1(2) +#define STV090x_OFFST_Px_DIS_MODCOD29_FIELD 4 +#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD 4 +#define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD 0 +#define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD 4 + +#define STV090x_Px_MODCODLST2(__x) (0xf4b2 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST2 STV090x_Px_MODCODLST2(1) +#define STV090x_P2_MODCODLST2 STV090x_Px_MODCODLST2(2) +#define STV090x_OFFST_Px_DIS_32PSK_8_9_FIELD 4 +#define STV090x_WIDTH_Px_DIS_32PSK_8_9_FIELD 4 +#define STV090x_OFFST_Px_DIS_32PSK_5_6_FIELD 0 +#define STV090x_WIDTH_Px_DIS_32PSK_5_6_FIELD 4 + +#define STV090x_Px_MODCODLST3(__x) (0xf4b3 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST3 STV090x_Px_MODCODLST3(1) +#define STV090x_P2_MODCODLST3 STV090x_Px_MODCODLST3(2) +#define STV090x_OFFST_Px_DIS_32PSK_4_5_FIELD 4 +#define STV090x_WIDTH_Px_DIS_32PSK_4_5_FIELD 4 +#define STV090x_OFFST_Px_DIS_32PSK_3_4_FIELD 0 +#define STV090x_WIDTH_Px_DIS_32PSK_3_4_FIELD 4 + +#define STV090x_Px_MODCODLST4(__x) (0xf4b4 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST4 STV090x_Px_MODCODLST4(1) +#define STV090x_P2_MODCODLST4 STV090x_Px_MODCODLST4(2) +#define STV090x_OFFST_Px_DIS_16PSK_9_10_FIELD 4 +#define STV090x_WIDTH_Px_DIS_16PSK_9_10_FIELD 4 +#define STV090x_OFFST_Px_DIS_16PSK_8_9_FIELD 0 +#define STV090x_WIDTH_Px_DIS_16PSK_8_9_FIELD 4 + +#define STV090x_Px_MODCODLST5(__x) (0xf4b5 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST5 STV090x_Px_MODCODLST5(1) +#define STV090x_P2_MODCODLST5 STV090x_Px_MODCODLST5(2) +#define STV090x_OFFST_Px_DIS_16PSK_5_6_FIELD 4 +#define STV090x_WIDTH_Px_DIS_16PSK_5_6_FIELD 4 +#define STV090x_OFFST_Px_DIS_16PSK_4_5_FIELD 0 +#define STV090x_WIDTH_Px_DIS_16PSK_4_5_FIELD 4 + +#define STV090x_Px_MODCODLST6(__x) (0xf4b6 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST6 STV090x_Px_MODCODLST6(1) +#define STV090x_P2_MODCODLST6 STV090x_Px_MODCODLST6(2) +#define STV090x_OFFST_Px_DIS_16PSK_3_4_FIELD 4 +#define STV090x_WIDTH_Px_DIS_16PSK_3_4_FIELD 4 +#define STV090x_OFFST_Px_DIS_16PSK_2_3_FIELD 0 +#define STV090x_WIDTH_Px_DIS_16PSK_2_3_FIELD 4 + +#define STV090x_Px_MODCODLST7(__x) (0xf4b7 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST7 STV090x_Px_MODCODLST7(1) +#define STV090x_P2_MODCODLST7 STV090x_Px_MODCODLST7(2) +#define STV090x_OFFST_Px_DIS_8P_9_10_FIELD 4 +#define STV090x_WIDTH_Px_DIS_8P_9_10_FIELD 4 +#define STV090x_OFFST_Px_DIS_8P_8_9_FIELD 0 +#define STV090x_WIDTH_Px_DIS_8P_8_9_FIELD 4 + +#define STV090x_Px_MODCODLST8(__x) (0xf4b8 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST8 STV090x_Px_MODCODLST8(1) +#define STV090x_P2_MODCODLST8 STV090x_Px_MODCODLST8(2) +#define STV090x_OFFST_Px_DIS_8P_5_6_FIELD 4 +#define STV090x_WIDTH_Px_DIS_8P_5_6_FIELD 4 +#define STV090x_OFFST_Px_DIS_8P_3_4_FIELD 0 +#define STV090x_WIDTH_Px_DIS_8P_3_4_FIELD 4 + +#define STV090x_Px_MODCODLST9(__x) (0xf4b9 - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLST9 STV090x_Px_MODCODLST9(1) +#define STV090x_P2_MODCODLST9 STV090x_Px_MODCODLST9(2) +#define STV090x_OFFST_Px_DIS_8P_2_3_FIELD 4 +#define STV090x_WIDTH_Px_DIS_8P_2_3_FIELD 4 +#define STV090x_OFFST_Px_DIS_8P_3_5_FIELD 0 +#define STV090x_WIDTH_Px_DIS_8P_3_5_FIELD 4 + +#define STV090x_Px_MODCODLSTA(__x) (0xf4ba - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLSTA STV090x_Px_MODCODLSTA(1) +#define STV090x_P2_MODCODLSTA STV090x_Px_MODCODLSTA(2) +#define STV090x_OFFST_Px_DIS_QP_9_10_FIELD 4 +#define STV090x_WIDTH_Px_DIS_QP_9_10_FIELD 4 +#define STV090x_OFFST_Px_DIS_QP_8_9_FIELD 0 +#define STV090x_WIDTH_Px_DIS_QP_8_9_FIELD 4 + +#define STV090x_Px_MODCODLSTB(__x) (0xf4bb - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLSTB STV090x_Px_MODCODLSTB(1) +#define STV090x_P2_MODCODLSTB STV090x_Px_MODCODLSTB(2) +#define STV090x_OFFST_Px_DIS_QP_5_6_FIELD 4 +#define STV090x_WIDTH_Px_DIS_QP_5_6_FIELD 4 +#define STV090x_OFFST_Px_DIS_QP_4_5_FIELD 0 +#define STV090x_WIDTH_Px_DIS_QP_4_5_FIELD 4 + +#define STV090x_Px_MODCODLSTC(__x) (0xf4bc - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLSTC STV090x_Px_MODCODLSTC(1) +#define STV090x_P2_MODCODLSTC STV090x_Px_MODCODLSTC(2) +#define STV090x_OFFST_Px_DIS_QP_3_4_FIELD 4 +#define STV090x_WIDTH_Px_DIS_QP_3_4_FIELD 4 +#define STV090x_OFFST_Px_DIS_QP_2_3_FIELD 0 +#define STV090x_WIDTH_Px_DIS_QP_2_3_FIELD 4 + +#define STV090x_Px_MODCODLSTD(__x) (0xf4bd - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLSTD STV090x_Px_MODCODLSTD(1) +#define STV090x_P2_MODCODLSTD STV090x_Px_MODCODLSTD(2) +#define STV090x_OFFST_Px_DIS_QP_3_5_FIELD 4 +#define STV090x_WIDTH_Px_DIS_QP_3_5_FIELD 4 +#define STV090x_OFFST_Px_DIS_QP_1_2_FIELD 0 +#define STV090x_WIDTH_Px_DIS_QP_1_2_FIELD 4 + +#define STV090x_Px_MODCODLSTE(__x) (0xf4be - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLSTE STV090x_Px_MODCODLSTE(1) +#define STV090x_P2_MODCODLSTE STV090x_Px_MODCODLSTE(2) +#define STV090x_OFFST_Px_DIS_QP_2_5_FIELD 4 +#define STV090x_WIDTH_Px_DIS_QP_2_5_FIELD 4 +#define STV090x_OFFST_Px_DIS_QP_1_3_FIELD 0 +#define STV090x_WIDTH_Px_DIS_QP_1_3_FIELD 4 + +#define STV090x_Px_MODCODLSTF(__x) (0xf4bf - (__x - 1) * 0x200) +#define STV090x_P1_MODCODLSTF STV090x_Px_MODCODLSTF(1) +#define STV090x_P2_MODCODLSTF STV090x_Px_MODCODLSTF(2) +#define STV090x_OFFST_Px_DIS_QP_1_4_FIELD 4 +#define STV090x_WIDTH_Px_DIS_QP_1_4_FIELD 4 + +#define STV090x_Px_GAUSSR0(__x) (0xf4c0 - (__x - 1) * 0x200) +#define STV090x_P1_GAUSSR0 STV090x_Px_GAUSSR0(1) +#define STV090x_P2_GAUSSR0 STV090x_Px_GAUSSR0(2) +#define STV090x_OFFST_Px_EN_CCIMODE_FIELD 7 +#define STV090x_WIDTH_Px_EN_CCIMODE_FIELD 1 +#define STV090x_OFFST_Px_R0_GAUSSIEN_FIELD 0 +#define STV090x_WIDTH_Px_R0_GAUSSIEN_FIELD 7 + +#define STV090x_Px_CCIR0(__x) (0xf4c1 - (__x - 1) * 0x200) +#define STV090x_P1_CCIR0 STV090x_Px_CCIR0(1) +#define STV090x_P2_CCIR0 STV090x_Px_CCIR0(2) +#define STV090x_OFFST_Px_CCIDETECT_PLH_FIELD 7 +#define STV090x_WIDTH_Px_CCIDETECT_PLH_FIELD 1 +#define STV090x_OFFST_Px_R0_CCI_FIELD 0 +#define STV090x_WIDTH_Px_R0_CCI_FIELD 7 + +#define STV090x_Px_CCIQUANT(__x) (0xf4c2 - (__x - 1) * 0x200) +#define STV090x_P1_CCIQUANT STV090x_Px_CCIQUANT(1) +#define STV090x_P2_CCIQUANT STV090x_Px_CCIQUANT(2) +#define STV090x_OFFST_Px_CCI_BETA_FIELD 5 +#define STV090x_WIDTH_Px_CCI_BETA_FIELD 3 +#define STV090x_OFFST_Px_CCI_QUANT_FIELD 0 +#define STV090x_WIDTH_Px_CCI_QUANT_FIELD 5 + +#define STV090x_Px_CCITHRESH(__x) (0xf4c3 - (__x - 1) * 0x200) +#define STV090x_P1_CCITHRESH STV090x_Px_CCITHRESH(1) +#define STV090x_P2_CCITHRESH STV090x_Px_CCITHRESH(2) +#define STV090x_OFFST_Px_CCI_THRESHOLD_FIELD 0 +#define STV090x_WIDTH_Px_CCI_THRESHOLD_FIELD 8 + +#define STV090x_Px_CCIACC(__x) (0xf4c4 - (__x - 1) * 0x200) +#define STV090x_P1_CCIACC STV090x_Px_CCIACC(1) +#define STV090x_P2_CCIACC STV090x_Px_CCIACC(2) +#define STV090x_OFFST_Px_CCI_VALUE_FIELD 0 +#define STV090x_WIDTH_Px_CCI_VALUE_FIELD 8 + +#define STV090x_Px_DMDRESCFG(__x) (0xF4C6 - (__x - 1) * 0x200) +#define STV090x_P1_DMDRESCFG STV090x_Px_DMDRESCFG(1) +#define STV090x_P2_DMDRESCFG STV090x_Px_DMDRESCFG(2) +#define STV090x_OFFST_Px_DMDRES_RESET_FIELD 7 +#define STV090x_WIDTH_Px_DMDRES_RESET_FIELD 1 + +#define STV090x_Px_DMDRESADR(__x) (0xF4C7 - (__x - 1) * 0x200) +#define STV090x_P1_DMDRESADR STV090x_Px_DMDRESADR(1) +#define STV090x_P2_DMDRESADR STV090x_Px_DMDRESADR(2) +#define STV090x_OFFST_Px_DMDRES_RESNBR_FIELD 0 +#define STV090x_WIDTH_Px_DMDRES_RESNBR_FIELD 4 + +#define STV090x_Px_DMDRESDATAy(__x, __y) (0xF4C8 - (__x - 1) * 0x200 + (7 - __y)) +#define STV090x_P1_DMDRESDATA0 STV090x_Px_DMDRESDATAy(1, 0) +#define STV090x_P1_DMDRESDATA1 STV090x_Px_DMDRESDATAy(1, 1) +#define STV090x_P1_DMDRESDATA2 STV090x_Px_DMDRESDATAy(1, 2) +#define STV090x_P1_DMDRESDATA3 STV090x_Px_DMDRESDATAy(1, 3) +#define STV090x_P1_DMDRESDATA4 STV090x_Px_DMDRESDATAy(1, 4) +#define STV090x_P1_DMDRESDATA5 STV090x_Px_DMDRESDATAy(1, 5) +#define STV090x_P1_DMDRESDATA6 STV090x_Px_DMDRESDATAy(1, 6) +#define STV090x_P1_DMDRESDATA7 STV090x_Px_DMDRESDATAy(1, 7) +#define STV090x_P2_DMDRESDATA0 STV090x_Px_DMDRESDATAy(2, 0) +#define STV090x_P2_DMDRESDATA1 STV090x_Px_DMDRESDATAy(2, 1) +#define STV090x_P2_DMDRESDATA2 STV090x_Px_DMDRESDATAy(2, 2) +#define STV090x_P2_DMDRESDATA3 STV090x_Px_DMDRESDATAy(2, 3) +#define STV090x_P2_DMDRESDATA4 STV090x_Px_DMDRESDATAy(2, 4) +#define STV090x_P2_DMDRESDATA5 STV090x_Px_DMDRESDATAy(2, 5) +#define STV090x_P2_DMDRESDATA6 STV090x_Px_DMDRESDATAy(2, 6) +#define STV090x_P2_DMDRESDATA7 STV090x_Px_DMDRESDATAy(2, 7) +#define STV090x_OFFST_Px_DMDRES_DATA_FIELD 0 +#define STV090x_WIDTH_Px_DMDRES_DATA_FIELD 8 + +#define STV090x_Px_FFEIy(__x, __y) (0xf4d0 - (__x - 1) * 0x200 + 0x2 * (__y - 1)) +#define STV090x_P1_FFEI1 STV090x_Px_FFEIy(1, 1) +#define STV090x_P1_FFEI2 STV090x_Px_FFEIy(1, 2) +#define STV090x_P1_FFEI3 STV090x_Px_FFEIy(1, 3) +#define STV090x_P1_FFEI4 STV090x_Px_FFEIy(1, 4) +#define STV090x_P2_FFEI1 STV090x_Px_FFEIy(2, 1) +#define STV090x_P2_FFEI2 STV090x_Px_FFEIy(2, 2) +#define STV090x_P2_FFEI3 STV090x_Px_FFEIy(2, 3) +#define STV090x_P2_FFEI4 STV090x_Px_FFEIy(2, 4) +#define STV090x_OFFST_Px_FFE_ACCIy_FIELD 0 +#define STV090x_WIDTH_Px_FFE_ACCIy_FIELD 8 + +#define STV090x_Px_FFEQy(__x, __y) (0xf4d1 - (__x - 1) * 0x200 + 0x2 * (__y - 1)) +#define STV090x_P1_FFEQ1 STV090x_Px_FFEQy(1, 1) +#define STV090x_P1_FFEQ2 STV090x_Px_FFEQy(1, 2) +#define STV090x_P1_FFEQ3 STV090x_Px_FFEQy(1, 3) +#define STV090x_P1_FFEQ4 STV090x_Px_FFEQy(1, 4) +#define STV090x_P2_FFEQ1 STV090x_Px_FFEQy(2, 1) +#define STV090x_P2_FFEQ2 STV090x_Px_FFEQy(2, 2) +#define STV090x_P2_FFEQ3 STV090x_Px_FFEQy(2, 3) +#define STV090x_P2_FFEQ4 STV090x_Px_FFEQy(2, 4) +#define STV090x_OFFST_Px_FFE_ACCQy_FIELD 0 +#define STV090x_WIDTH_Px_FFE_ACCQy_FIELD 8 + +#define STV090x_Px_FFECFG(__x) (0xf4d8 - (__x - 1) * 0x200) +#define STV090x_P1_FFECFG STV090x_Px_FFECFG(1) +#define STV090x_P2_FFECFG STV090x_Px_FFECFG(2) +#define STV090x_OFFST_Px_EQUALFFE_ON_FIELD 6 +#define STV090x_WIDTH_Px_EQUALFFE_ON_FIELD 1 + +#define STV090x_Px_SMAPCOEF7(__x) (0xf500 - (__x - 1) * 0x200) +#define STV090x_P1_SMAPCOEF7 STV090x_Px_SMAPCOEF7(1) +#define STV090x_P2_SMAPCOEF7 STV090x_Px_SMAPCOEF7(2) +#define STV090x_OFFST_Px_DIS_QSCALE_FIELD 7 +#define STV090x_WIDTH_Px_DIS_QSCALE_FIELD 1 +#define STV090x_OFFST_Px_SMAPCOEF_Q_LLR12_FIELD 0 +#define STV090x_WIDTH_Px_SMAPCOEF_Q_LLR12_FIELD 7 + +#define STV090x_Px_SMAPCOEF6(__x) (0xf501 - (__x - 1) * 0x200) +#define STV090x_P1_SMAPCOEF6 STV090x_Px_SMAPCOEF6(1) +#define STV090x_P2_SMAPCOEF6 STV090x_Px_SMAPCOEF6(2) +#define STV090x_OFFST_Px_ADJ_8PSKLLR1_FIELD 2 +#define STV090x_WIDTH_Px_ADJ_8PSKLLR1_FIELD 1 +#define STV090x_OFFST_Px_OLD_8PSKLLR1_FIELD 1 +#define STV090x_WIDTH_Px_OLD_8PSKLLR1_FIELD 1 +#define STV090x_OFFST_Px_DIS_AB8PSK_FIELD 0 +#define STV090x_WIDTH_Px_DIS_AB8PSK_FIELD 1 + +#define STV090x_Px_SMAPCOEF5(__x) (0xf502 - (__x - 1) * 0x200) +#define STV090x_P1_SMAPCOEF5 STV090x_Px_SMAPCOEF5(1) +#define STV090x_P2_SMAPCOEF5 STV090x_Px_SMAPCOEF5(2) +#define STV090x_OFFST_Px_DIS_8SCALE_FIELD 7 +#define STV090x_WIDTH_Px_DIS_8SCALE_FIELD 1 +#define STV090x_OFFST_Px_SMAPCOEF_8P_LLR23_FIELD 0 +#define STV090x_WIDTH_Px_SMAPCOEF_8P_LLR23_FIELD 7 + +#define STV090x_Px_DMDPLHSTAT(__x) (0xF520 - (__x - 1) * 0x200) +#define STV090x_P1_DMDPLHSTAT STV090x_Px_DMDPLHSTAT(1) +#define STV090x_P2_DMDPLHSTAT STV090x_Px_DMDPLHSTAT(2) +#define STV090x_OFFST_Px_PLH_STATISTIC_FIELD 0 +#define STV090x_WIDTH_Px_PLH_STATISTIC_FIELD 8 + +#define STV090x_Px_LOCKTIMEy(__x, __y) (0xF525 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_LOCKTIME0 STV090x_Px_LOCKTIMEy(1, 0) +#define STV090x_P1_LOCKTIME1 STV090x_Px_LOCKTIMEy(1, 1) +#define STV090x_P1_LOCKTIME2 STV090x_Px_LOCKTIMEy(1, 2) +#define STV090x_P1_LOCKTIME3 STV090x_Px_LOCKTIMEy(1, 3) +#define STV090x_P2_LOCKTIME0 STV090x_Px_LOCKTIMEy(2, 0) +#define STV090x_P2_LOCKTIME1 STV090x_Px_LOCKTIMEy(2, 1) +#define STV090x_P2_LOCKTIME2 STV090x_Px_LOCKTIMEy(2, 2) +#define STV090x_P2_LOCKTIME3 STV090x_Px_LOCKTIMEy(2, 3) +#define STV090x_OFFST_Px_DEMOD_LOCKTIME_FIELD 0 +#define STV090x_WIDTH_Px_DEMOD_LOCKTIME_FIELD 8 + +#define STV090x_Px_TNRCFG(__x) (0xf4e0 - (__x - 1) * 0x200) /* check */ +#define STV090x_P1_TNRCFG STV090x_Px_TNRCFG(1) +#define STV090x_P2_TNRCFG STV090x_Px_TNRCFG(2) + +#define STV090x_Px_TNRCFG2(__x) (0xf4e1 - (__x - 1) * 0x200) +#define STV090x_P1_TNRCFG2 STV090x_Px_TNRCFG2(1) +#define STV090x_P2_TNRCFG2 STV090x_Px_TNRCFG2(2) +#define STV090x_OFFST_Px_TUN_IQSWAP_FIELD 7 +#define STV090x_WIDTH_Px_TUN_IQSWAP_FIELD 1 + +#define STV090x_Px_VITSCALE(__x) (0xf532 - (__x - 1) * 0x200) +#define STV090x_P1_VITSCALE STV090x_Px_VITSCALE(1) +#define STV090x_P2_VITSCALE STV090x_Px_VITSCALE(2) +#define STV090x_OFFST_Px_NVTH_NOSRANGE_FIELD 7 +#define STV090x_WIDTH_Px_NVTH_NOSRANGE_FIELD 1 +#define STV090x_OFFST_Px_VERROR_MAXMODE_FIELD 6 +#define STV090x_WIDTH_Px_VERROR_MAXMODE_FIELD 1 +#define STV090x_OFFST_Px_NSLOWSN_LOCKED_FIELD 3 +#define STV090x_WIDTH_Px_NSLOWSN_LOCKED_FIELD 1 +#define STV090x_OFFST_Px_DIS_RSFLOCK_FIELD 1 +#define STV090x_WIDTH_Px_DIS_RSFLOCK_FIELD 1 + +#define STV090x_Px_FECM(__x) (0xf533 - (__x - 1) * 0x200) +#define STV090x_P1_FECM STV090x_Px_FECM(1) +#define STV090x_P2_FECM STV090x_Px_FECM(2) +#define STV090x_OFFST_Px_DSS_DVB_FIELD 7 +#define STV090x_WIDTH_Px_DSS_DVB_FIELD 1 +#define STV090x_OFFST_Px_DSS_SRCH_FIELD 4 +#define STV090x_WIDTH_Px_DSS_SRCH_FIELD 1 +#define STV090x_OFFST_Px_SYNCVIT_FIELD 1 +#define STV090x_WIDTH_Px_SYNCVIT_FIELD 1 +#define STV090x_OFFST_Px_IQINV_FIELD 0 +#define STV090x_WIDTH_Px_IQINV_FIELD 1 + +#define STV090x_Px_VTH12(__x) (0xf534 - (__x - 1) * 0x200) +#define STV090x_P1_VTH12 STV090x_Px_VTH12(1) +#define STV090x_P2_VTH12 STV090x_Px_VTH12(2) +#define STV090x_OFFST_Px_VTH12_FIELD 0 +#define STV090x_WIDTH_Px_VTH12_FIELD 8 + +#define STV090x_Px_VTH23(__x) (0xf535 - (__x - 1) * 0x200) +#define STV090x_P1_VTH23 STV090x_Px_VTH23(1) +#define STV090x_P2_VTH23 STV090x_Px_VTH23(2) +#define STV090x_OFFST_Px_VTH23_FIELD 0 +#define STV090x_WIDTH_Px_VTH23_FIELD 8 + +#define STV090x_Px_VTH34(__x) (0xf536 - (__x - 1) * 0x200) +#define STV090x_P1_VTH34 STV090x_Px_VTH34(1) +#define STV090x_P2_VTH34 STV090x_Px_VTH34(2) +#define STV090x_OFFST_Px_VTH34_FIELD 0 +#define STV090x_WIDTH_Px_VTH34_FIELD 8 + +#define STV090x_Px_VTH56(__x) (0xf537 - (__x - 1) * 0x200) +#define STV090x_P1_VTH56 STV090x_Px_VTH56(1) +#define STV090x_P2_VTH56 STV090x_Px_VTH56(2) +#define STV090x_OFFST_Px_VTH56_FIELD 0 +#define STV090x_WIDTH_Px_VTH56_FIELD 8 + +#define STV090x_Px_VTH67(__x) (0xf538 - (__x - 1) * 0x200) +#define STV090x_P1_VTH67 STV090x_Px_VTH67(1) +#define STV090x_P2_VTH67 STV090x_Px_VTH67(2) +#define STV090x_OFFST_Px_VTH67_FIELD 0 +#define STV090x_WIDTH_Px_VTH67_FIELD 8 + +#define STV090x_Px_VTH78(__x) (0xf539 - (__x - 1) * 0x200) +#define STV090x_P1_VTH78 STV090x_Px_VTH78(1) +#define STV090x_P2_VTH78 STV090x_Px_VTH78(2) +#define STV090x_OFFST_Px_VTH78_FIELD 0 +#define STV090x_WIDTH_Px_VTH78_FIELD 8 + +#define STV090x_Px_VITCURPUN(__x) (0xf53a - (__x - 1) * 0x200) +#define STV090x_P1_VITCURPUN STV090x_Px_VITCURPUN(1) +#define STV090x_P2_VITCURPUN STV090x_Px_VITCURPUN(2) +#define STV090x_OFFST_Px_VIT_CURPUN_FIELD 0 +#define STV090x_WIDTH_Px_VIT_CURPUN_FIELD 5 + +#define STV090x_Px_VERROR(__x) (0xf53b - (__x - 1) * 0x200) +#define STV090x_P1_VERROR STV090x_Px_VERROR(1) +#define STV090x_P2_VERROR STV090x_Px_VERROR(2) +#define STV090x_OFFST_Px_REGERR_VIT_FIELD 0 +#define STV090x_WIDTH_Px_REGERR_VIT_FIELD 8 + +#define STV090x_Px_PRVIT(__x) (0xf53c - (__x - 1) * 0x200) +#define STV090x_P1_PRVIT STV090x_Px_PRVIT(1) +#define STV090x_P2_PRVIT STV090x_Px_PRVIT(2) +#define STV090x_OFFST_Px_DIS_VTHLOCK_FIELD 6 +#define STV090x_WIDTH_Px_DIS_VTHLOCK_FIELD 1 +#define STV090x_OFFST_Px_E7_8VIT_FIELD 5 +#define STV090x_WIDTH_Px_E7_8VIT_FIELD 1 +#define STV090x_OFFST_Px_E6_7VIT_FIELD 4 +#define STV090x_WIDTH_Px_E6_7VIT_FIELD 1 +#define STV090x_OFFST_Px_E5_6VIT_FIELD 3 +#define STV090x_WIDTH_Px_E5_6VIT_FIELD 1 +#define STV090x_OFFST_Px_E3_4VIT_FIELD 2 +#define STV090x_WIDTH_Px_E3_4VIT_FIELD 1 +#define STV090x_OFFST_Px_E2_3VIT_FIELD 1 +#define STV090x_WIDTH_Px_E2_3VIT_FIELD 1 +#define STV090x_OFFST_Px_E1_2VIT_FIELD 0 +#define STV090x_WIDTH_Px_E1_2VIT_FIELD 1 + +#define STV090x_Px_VAVSRVIT(__x) (0xf53d - (__x - 1) * 0x200) +#define STV090x_P1_VAVSRVIT STV090x_Px_VAVSRVIT(1) +#define STV090x_P2_VAVSRVIT STV090x_Px_VAVSRVIT(2) +#define STV090x_OFFST_Px_SNVIT_FIELD 4 +#define STV090x_WIDTH_Px_SNVIT_FIELD 2 +#define STV090x_OFFST_Px_TOVVIT_FIELD 2 +#define STV090x_WIDTH_Px_TOVVIT_FIELD 2 +#define STV090x_OFFST_Px_HYPVIT_FIELD 0 +#define STV090x_WIDTH_Px_HYPVIT_FIELD 2 + +#define STV090x_Px_VSTATUSVIT(__x) (0xf53e - (__x - 1) * 0x200) +#define STV090x_P1_VSTATUSVIT STV090x_Px_VSTATUSVIT(1) +#define STV090x_P2_VSTATUSVIT STV090x_Px_VSTATUSVIT(2) +#define STV090x_OFFST_Px_PRFVIT_FIELD 4 +#define STV090x_WIDTH_Px_PRFVIT_FIELD 1 +#define STV090x_OFFST_Px_LOCKEDVIT_FIELD 3 +#define STV090x_WIDTH_Px_LOCKEDVIT_FIELD 1 + +#define STV090x_Px_VTHINUSE(__x) (0xf53f - (__x - 1) * 0x200) +#define STV090x_P1_VTHINUSE STV090x_Px_VTHINUSE(1) +#define STV090x_P2_VTHINUSE STV090x_Px_VTHINUSE(2) +#define STV090x_OFFST_Px_VIT_INUSE_FIELD 0 +#define STV090x_WIDTH_Px_VIT_INUSE_FIELD 8 + +#define STV090x_Px_KDIV12(__x) (0xf540 - (__x - 1) * 0x200) +#define STV090x_P1_KDIV12 STV090x_Px_KDIV12(1) +#define STV090x_P2_KDIV12 STV090x_Px_KDIV12(2) +#define STV090x_OFFST_Px_K_DIVIDER_12_FIELD 0 +#define STV090x_WIDTH_Px_K_DIVIDER_12_FIELD 7 + +#define STV090x_Px_KDIV23(__x) (0xf541 - (__x - 1) * 0x200) +#define STV090x_P1_KDIV23 STV090x_Px_KDIV23(1) +#define STV090x_P2_KDIV23 STV090x_Px_KDIV23(2) +#define STV090x_OFFST_Px_K_DIVIDER_23_FIELD 0 +#define STV090x_WIDTH_Px_K_DIVIDER_23_FIELD 7 + +#define STV090x_Px_KDIV34(__x) (0xf542 - (__x - 1) * 0x200) +#define STV090x_P1_KDIV34 STV090x_Px_KDIV34(1) +#define STV090x_P2_KDIV34 STV090x_Px_KDIV34(2) +#define STV090x_OFFST_Px_K_DIVIDER_34_FIELD 0 +#define STV090x_WIDTH_Px_K_DIVIDER_34_FIELD 7 + +#define STV090x_Px_KDIV56(__x) (0xf543 - (__x - 1) * 0x200) +#define STV090x_P1_KDIV56 STV090x_Px_KDIV56(1) +#define STV090x_P2_KDIV56 STV090x_Px_KDIV56(2) +#define STV090x_OFFST_Px_K_DIVIDER_56_FIELD 0 +#define STV090x_WIDTH_Px_K_DIVIDER_56_FIELD 7 + +#define STV090x_Px_KDIV67(__x) (0xf544 - (__x - 1) * 0x200) +#define STV090x_P1_KDIV67 STV090x_Px_KDIV67(1) +#define STV090x_P2_KDIV67 STV090x_Px_KDIV67(2) +#define STV090x_OFFST_Px_K_DIVIDER_67_FIELD 0 +#define STV090x_WIDTH_Px_K_DIVIDER_67_FIELD 7 + +#define STV090x_Px_KDIV78(__x) (0xf545 - (__x - 1) * 0x200) +#define STV090x_P1_KDIV78 STV090x_Px_KDIV78(1) +#define STV090x_P2_KDIV78 STV090x_Px_KDIV78(2) +#define STV090x_OFFST_Px_K_DIVIDER_78_FIELD 0 +#define STV090x_WIDTH_Px_K_DIVIDER_78_FIELD 7 + +#define STV090x_Px_PDELCTRL1(__x) (0xf550 - (__x - 1) * 0x200) +#define STV090x_P1_PDELCTRL1 STV090x_Px_PDELCTRL1(1) +#define STV090x_P2_PDELCTRL1 STV090x_Px_PDELCTRL1(2) +#define STV090x_OFFST_Px_INV_MISMASK_FIELD 7 +#define STV090x_WIDTH_Px_INV_MISMASK_FIELD 1 +#define STV090x_OFFST_Px_FILTER_EN_FIELD 5 +#define STV090x_WIDTH_Px_FILTER_EN_FIELD 1 +#define STV090x_OFFST_Px_EN_MIS00_FIELD 1 +#define STV090x_WIDTH_Px_EN_MIS00_FIELD 1 +#define STV090x_OFFST_Px_ALGOSWRST_FIELD 0 +#define STV090x_WIDTH_Px_ALGOSWRST_FIELD 1 + +#define STV090x_Px_PDELCTRL2(__x) (0xf551 - (__x - 1) * 0x200) +#define STV090x_P1_PDELCTRL2 STV090x_Px_PDELCTRL2(1) +#define STV090x_P2_PDELCTRL2 STV090x_Px_PDELCTRL2(2) +#define STV090x_OFFST_Px_FORCE_CONTINUOUS 7 +#define STV090x_WIDTH_Px_FORCE_CONTINUOUS 1 +#define STV090x_OFFST_Px_RESET_UPKO_COUNT 6 +#define STV090x_WIDTH_Px_RESET_UPKO_COUNT 1 +#define STV090x_OFFST_Px_USER_PKTDELIN_NB 5 +#define STV090x_WIDTH_Px_USER_PKTDELIN_NB 1 +#define STV090x_OFFST_Px_FORCE_LOCKED 4 +#define STV090x_WIDTH_Px_FORCE_LOCKED 1 +#define STV090x_OFFST_Px_DATA_UNBBSCRAM 3 +#define STV090x_WIDTH_Px_DATA_UNBBSCRAM 1 +#define STV090x_OFFST_Px_FORCE_LONGPACKET 2 +#define STV090x_WIDTH_Px_FORCE_LONGPACKET 1 +#define STV090x_OFFST_Px_FRAME_MODE_FIELD 1 +#define STV090x_WIDTH_Px_FRAME_MODE_FIELD 1 + +#define STV090x_Px_HYSTTHRESH(__x) (0xf554 - (__x - 1) * 0x200) +#define STV090x_P1_HYSTTHRESH STV090x_Px_HYSTTHRESH(1) +#define STV090x_P2_HYSTTHRESH STV090x_Px_HYSTTHRESH(2) +#define STV090x_OFFST_Px_UNLCK_THRESH_FIELD 4 +#define STV090x_WIDTH_Px_UNLCK_THRESH_FIELD 4 +#define STV090x_OFFST_Px_DELIN_LCK_THRESH_FIELD 0 +#define STV090x_WIDTH_Px_DELIN_LCK_THRESH_FIELD 4 + +#define STV090x_Px_ISIENTRY(__x) (0xf55e - (__x - 1) * 0x200) +#define STV090x_P1_ISIENTRY STV090x_Px_ISIENTRY(1) +#define STV090x_P2_ISIENTRY STV090x_Px_ISIENTRY(2) +#define STV090x_OFFST_Px_ISI_ENTRY_FIELD 0 +#define STV090x_WIDTH_Px_ISI_ENTRY_FIELD 8 + +#define STV090x_Px_ISIBITENA(__x) (0xf55f - (__x - 1) * 0x200) +#define STV090x_P1_ISIBITENA STV090x_Px_ISIBITENA(1) +#define STV090x_P2_ISIBITENA STV090x_Px_ISIBITENA(2) +#define STV090x_OFFST_Px_ISI_BIT_EN_FIELD 0 +#define STV090x_WIDTH_Px_ISI_BIT_EN_FIELD 8 + +#define STV090x_Px_MATSTRy(__x, __y) (0xf561 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_MATSTR0 STV090x_Px_MATSTRy(1, 0) +#define STV090x_P1_MATSTR1 STV090x_Px_MATSTRy(1, 1) +#define STV090x_P2_MATSTR0 STV090x_Px_MATSTRy(2, 0) +#define STV090x_P2_MATSTR1 STV090x_Px_MATSTRy(2, 1) +#define STV090x_OFFST_Px_MATYPE_CURRENT_FIELD 0 +#define STV090x_WIDTH_Px_MATYPE_CURRENT_FIELD 8 + +#define STV090x_Px_UPLSTRy(__x, __y) (0xf563 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_UPLSTR0 STV090x_Px_UPLSTRy(1, 0) +#define STV090x_P1_UPLSTR1 STV090x_Px_UPLSTRy(1, 1) +#define STV090x_P2_UPLSTR0 STV090x_Px_UPLSTRy(2, 0) +#define STV090x_P2_UPLSTR1 STV090x_Px_UPLSTRy(2, 1) +#define STV090x_OFFST_Px_UPL_CURRENT_FIELD 0 +#define STV090x_WIDTH_Px_UPL_CURRENT_FIELD 8 + +#define STV090x_Px_DFLSTRy(__x, __y) (0xf565 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_DFLSTR0 STV090x_Px_DFLSTRy(1, 0) +#define STV090x_P1_DFLSTR1 STV090x_Px_DFLSTRy(1, 1) +#define STV090x_P2_DFLSTR0 STV090x_Px_DFLSTRy(2, 0) +#define STV090x_P2_DFLSTR1 STV090x_Px_DFLSTRy(2, 1) +#define STV090x_OFFST_Px_DFL_CURRENT_FIELD 0 +#define STV090x_WIDTH_Px_DFL_CURRENT_FIELD 8 + +#define STV090x_Px_SYNCSTR(__x) (0xf566 - (__x - 1) * 0x200) +#define STV090x_P1_SYNCSTR STV090x_Px_SYNCSTR(1) +#define STV090x_P2_SYNCSTR STV090x_Px_SYNCSTR(2) +#define STV090x_OFFST_Px_SYNC_CURRENT_FIELD 0 +#define STV090x_WIDTH_Px_SYNC_CURRENT_FIELD 8 + +#define STV090x_Px_SYNCDSTRy(__x, __y) (0xf568 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_SYNCDSTR0 STV090x_Px_SYNCDSTRy(1, 0) +#define STV090x_P1_SYNCDSTR1 STV090x_Px_SYNCDSTRy(1, 1) +#define STV090x_P2_SYNCDSTR0 STV090x_Px_SYNCDSTRy(2, 0) +#define STV090x_P2_SYNCDSTR1 STV090x_Px_SYNCDSTRy(2, 1) +#define STV090x_OFFST_Px_SYNCD_CURRENT_FIELD 0 +#define STV090x_WIDTH_Px_SYNCD_CURRENT_FIELD 8 + +#define STV090x_Px_PDELSTATUS1(__x) (0xf569 - (__x - 1) * 0x200) +#define STV090x_P1_PDELSTATUS1 STV090x_Px_PDELSTATUS1(1) +#define STV090x_P2_PDELSTATUS1 STV090x_Px_PDELSTATUS1(2) +#define STV090x_OFFST_Px_PKTDELIN_LOCK_FIELD 1 +#define STV090x_WIDTH_Px_PKTDELIN_LOCK_FIELD 1 +#define STV090x_OFFST_Px_FIRST_LOCK_FIELD 0 +#define STV090x_WIDTH_Px_FIRST_LOCK_FIELD 1 + +#define STV090x_Px_PDELSTATUS2(__x) (0xf56a - (__x - 1) * 0x200) +#define STV090x_P1_PDELSTATUS2 STV090x_Px_PDELSTATUS2(1) +#define STV090x_P2_PDELSTATUS2 STV090x_Px_PDELSTATUS2(2) +#define STV090x_OFFST_Px_FRAME_MODCOD_FIELD 2 +#define STV090x_WIDTH_Px_FRAME_MODCOD_FIELD 5 +#define STV090x_OFFST_Px_FRAME_TYPE_FIELD 0 +#define STV090x_WIDTH_Px_FRAME_TYPE_FIELD 2 + +#define STV090x_Px_BBFCRCKO1(__x) (0xf56b - (__x - 1) * 0x200) +#define STV090x_P1_BBFCRCKO1 STV090x_Px_BBFCRCKO1(1) +#define STV090x_P2_BBFCRCKO1 STV090x_Px_BBFCRCKO1(2) +#define STV090x_OFFST_Px_BBHCRC_KOCNT_FIELD 0 +#define STV090x_WIDTH_Px_BBHCRC_KOCNT_FIELD 8 + +#define STV090x_Px_BBFCRCKO0(__x) (0xf56c - (__x - 1) * 0x200) +#define STV090x_P1_BBFCRCKO0 STV090x_Px_BBFCRCKO0(1) +#define STV090x_P2_BBFCRCKO0 STV090x_Px_BBFCRCKO0(2) +#define STV090x_OFFST_Px_BBHCRC_KOCNT_FIELD 0 +#define STV090x_WIDTH_Px_BBHCRC_KOCNT_FIELD 8 + +#define STV090x_Px_UPCRCKO1(__x) (0xf56d - (__x - 1) * 0x200) +#define STV090x_P1_UPCRCKO1 STV090x_Px_UPCRCKO1(1) +#define STV090x_P2_UPCRCKO1 STV090x_Px_UPCRCKO1(2) +#define STV090x_OFFST_Px_PKTCRC_KOCNT_FIELD 0 +#define STV090x_WIDTH_Px_PKTCRC_KOCNT_FIELD 8 + +#define STV090x_Px_UPCRCKO0(__x) (0xf56e - (__x - 1) * 0x200) +#define STV090x_P1_UPCRCKO0 STV090x_Px_UPCRCKO0(1) +#define STV090x_P2_UPCRCKO0 STV090x_Px_UPCRCKO0(2) +#define STV090x_OFFST_Px_PKTCRC_KOCNT_FIELD 0 +#define STV090x_WIDTH_Px_PKTCRC_KOCNT_FIELD 8 + +#define STV090x_NBITER_NFx(__x) (0xFA03 + (__x - 4) * 0x1) +#define STV090x_NBITER_NF4 STV090x_NBITER_NFx(4) +#define STV090x_NBITER_NF5 STV090x_NBITER_NFx(5) +#define STV090x_NBITER_NF6 STV090x_NBITER_NFx(6) +#define STV090x_NBITER_NF7 STV090x_NBITER_NFx(7) +#define STV090x_NBITER_NF8 STV090x_NBITER_NFx(8) +#define STV090x_NBITER_NF9 STV090x_NBITER_NFx(9) +#define STV090x_NBITER_NF10 STV090x_NBITER_NFx(10) +#define STV090x_NBITER_NF11 STV090x_NBITER_NFx(11) +#define STV090x_NBITER_NF12 STV090x_NBITER_NFx(12) +#define STV090x_NBITER_NF13 STV090x_NBITER_NFx(13) +#define STV090x_NBITER_NF14 STV090x_NBITER_NFx(14) +#define STV090x_NBITER_NF15 STV090x_NBITER_NFx(15) +#define STV090x_NBITER_NF16 STV090x_NBITER_NFx(16) +#define STV090x_NBITER_NF17 STV090x_NBITER_NFx(17) + +#define STV090x_NBITERNOERR 0xFA3F +#define STV090x_OFFST_NBITER_STOP_CRIT_FIELD 0 +#define STV090x_WIDTH_NBITER_STOP_CRIT_FIELD 4 + +#define STV090x_GAINLLR_NFx(__x) (0xFA43 + (__x - 4) * 0x1) +#define STV090x_GAINLLR_NF4 STV090x_GAINLLR_NFx(4) +#define STV090x_OFFST_GAINLLR_NF_QP_1_2_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_1_2_FIELD 7 + +#define STV090x_GAINLLR_NF5 STV090x_GAINLLR_NFx(5) +#define STV090x_OFFST_GAINLLR_NF_QP_3_5_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_3_5_FIELD 7 + +#define STV090x_GAINLLR_NF6 STV090x_GAINLLR_NFx(6) +#define STV090x_OFFST_GAINLLR_NF_QP_2_3_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_2_3_FIELD 7 + +#define STV090x_GAINLLR_NF7 STV090x_GAINLLR_NFx(7) +#define STV090x_OFFST_GAINLLR_NF_QP_3_4_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_3_4_FIELD 7 + +#define STV090x_GAINLLR_NF8 STV090x_GAINLLR_NFx(8) +#define STV090x_OFFST_GAINLLR_NF_QP_4_5_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_4_5_FIELD 7 + +#define STV090x_GAINLLR_NF9 STV090x_GAINLLR_NFx(9) +#define STV090x_OFFST_GAINLLR_NF_QP_5_6_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_5_6_FIELD 7 + +#define STV090x_GAINLLR_NF10 STV090x_GAINLLR_NFx(10) +#define STV090x_OFFST_GAINLLR_NF_QP_8_9_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_8_9_FIELD 7 + +#define STV090x_GAINLLR_NF11 STV090x_GAINLLR_NFx(11) +#define STV090x_OFFST_GAINLLR_NF_QP_9_10_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_QP_9_10_FIELD 7 + +#define STV090x_GAINLLR_NF12 STV090x_GAINLLR_NFx(12) +#define STV090x_OFFST_GAINLLR_NF_8P_3_5_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_8P_3_5_FIELD 7 + +#define STV090x_GAINLLR_NF13 STV090x_GAINLLR_NFx(13) +#define STV090x_OFFST_GAINLLR_NF_8P_2_3_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_8P_2_3_FIELD 7 + +#define STV090x_GAINLLR_NF14 STV090x_GAINLLR_NFx(14) +#define STV090x_OFFST_GAINLLR_NF_8P_3_4_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_8P_3_4_FIELD 7 + +#define STV090x_GAINLLR_NF15 STV090x_GAINLLR_NFx(15) +#define STV090x_OFFST_GAINLLR_NF_8P_5_6_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_8P_5_6_FIELD 7 + +#define STV090x_GAINLLR_NF16 STV090x_GAINLLR_NFx(16) +#define STV090x_OFFST_GAINLLR_NF_8P_8_9_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_8P_8_9_FIELD 7 + +#define STV090x_GAINLLR_NF17 STV090x_GAINLLR_NFx(17) +#define STV090x_OFFST_GAINLLR_NF_8P_9_10_FIELD 0 +#define STV090x_WIDTH_GAINLLR_NF_8P_9_10_FIELD 7 + +#define STV090x_GENCFG 0xFA86 +#define STV090x_OFFST_BROADCAST_FIELD 4 +#define STV090x_WIDTH_BROADCAST_FIELD 1 +#define STV090x_OFFST_PRIORITY_FIELD 1 +#define STV090x_WIDTH_PRIORITY_FIELD 1 +#define STV090x_OFFST_DDEMOD_FIELD 0 +#define STV090x_WIDTH_DDEMOD_FIELD 1 + +#define STV090x_LDPCERRx(__x) (0xFA97 - (__x * 0x1)) +#define STV090x_LDPCERR0 STV090x_LDPCERRx(0) +#define STV090x_LDPCERR1 STV090x_LDPCERRx(1) +#define STV090x_OFFST_Px_LDPC_ERRORS_COUNTER_FIELD 0 +#define STV090x_WIDTH_Px_LDPC_ERRORS_COUNTER_FIELD 8 + +#define STV090x_BCHERR 0xFA98 +#define STV090x_OFFST_Px_ERRORFLAG_FIELD 4 +#define STV090x_WIDTH_Px_ERRORFLAG_FIELD 1 +#define STV090x_OFFST_Px_BCH_ERRORS_COUNTER_FIELD 0 +#define STV090x_WIDTH_Px_BCH_ERRORS_COUNTER_FIELD 4 + +#define STV090x_Px_TSSTATEM(__x) (0xF570 - (__x - 1) * 0x200) +#define STV090x_P1_TSSTATEM STV090x_Px_TSSTATEM(1) +#define STV090x_P2_TSSTATEM STV090x_Px_TSSTATEM(2) +#define STV090x_OFFST_Px_TSDIL_ON_FIELD 7 +#define STV090x_WIDTH_Px_TSDIL_ON_FIELD 1 +#define STV090x_OFFST_Px_TSRS_ON_FIELD 5 +#define STV090x_WIDTH_Px_TSRS_ON_FIELD 1 + +#define STV090x_Px_TSCFGH(__x) (0xF572 - (__x - 1) * 0x200) +#define STV090x_P1_TSCFGH STV090x_Px_TSCFGH(1) +#define STV090x_P2_TSCFGH STV090x_Px_TSCFGH(2) +#define STV090x_OFFST_Px_TSFIFO_DVBCI_FIELD 7 +#define STV090x_WIDTH_Px_TSFIFO_DVBCI_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_SERIAL_FIELD 6 +#define STV090x_WIDTH_Px_TSFIFO_SERIAL_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_TEIUPDATE_FIELD 5 +#define STV090x_WIDTH_Px_TSFIFO_TEIUPDATE_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_DUTY50_FIELD 4 +#define STV090x_WIDTH_Px_TSFIFO_DUTY50_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_HSGNLOUT_FIELD 3 +#define STV090x_WIDTH_Px_TSFIFO_HSGNLOUT_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_ERRORMODE_FIELD 1 +#define STV090x_WIDTH_Px_TSFIFO_ERRORMODE_FIELD 2 +#define STV090x_OFFST_Px_RST_HWARE_FIELD 0 +#define STV090x_WIDTH_Px_RST_HWARE_FIELD 1 + +#define STV090x_Px_TSCFGM(__x) (0xF573 - (__x - 1) * 0x200) +#define STV090x_P1_TSCFGM STV090x_Px_TSCFGM(1) +#define STV090x_P2_TSCFGM STV090x_Px_TSCFGM(2) +#define STV090x_OFFST_Px_TSFIFO_MANSPEED_FIELD 6 +#define STV090x_WIDTH_Px_TSFIFO_MANSPEED_FIELD 2 +#define STV090x_OFFST_Px_TSFIFO_PERMDATA_FIELD 5 +#define STV090x_WIDTH_Px_TSFIFO_PERMDATA_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_INVDATA_FIELD 0 +#define STV090x_WIDTH_Px_TSFIFO_INVDATA_FIELD 1 + +#define STV090x_Px_TSCFGL(__x) (0xF574 - (__x - 1) * 0x200) +#define STV090x_P1_TSCFGL STV090x_Px_TSCFGL(1) +#define STV090x_P2_TSCFGL STV090x_Px_TSCFGL(2) +#define STV090x_OFFST_Px_TSFIFO_BCLKDEL1CK_FIELD 6 +#define STV090x_WIDTH_Px_TSFIFO_BCLKDEL1CK_FIELD 2 +#define STV090x_OFFST_Px_BCHERROR_MODE_FIELD 4 +#define STV090x_WIDTH_Px_BCHERROR_MODE_FIELD 2 +#define STV090x_OFFST_Px_TSFIFO_NSGNL2DATA_FIELD 3 +#define STV090x_WIDTH_Px_TSFIFO_NSGNL2DATA_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_EMBINDVB_FIELD 2 +#define STV090x_WIDTH_Px_TSFIFO_EMBINDVB_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_DPUNACT_FIELD 1 +#define STV090x_WIDTH_Px_TSFIFO_DPUNACT_FIELD 1 + +#define STV090x_Px_TSINSDELH(__x) (0xF576 - (__x - 1) * 0x200) +#define STV090x_P1_TSINSDELH STV090x_Px_TSINSDELH(1) +#define STV090x_P2_TSINSDELH STV090x_Px_TSINSDELH(2) +#define STV090x_OFFST_Px_TSDEL_SYNCBYTE_FIELD 7 +#define STV090x_WIDTH_Px_TSDEL_SYNCBYTE_FIELD 1 +#define STV090x_OFFST_Px_TSDEL_XXHEADER_FIELD 6 +#define STV090x_WIDTH_Px_TSDEL_XXHEADER_FIELD 1 + +#define STV090x_Px_TSSPEED(__x) (0xF580 - (__x - 1) * 0x200) +#define STV090x_P1_TSSPEED STV090x_Px_TSSPEED(1) +#define STV090x_P2_TSSPEED STV090x_Px_TSSPEED(2) +#define STV090x_OFFST_Px_TSFIFO_OUTSPEED_FIELD 0 +#define STV090x_WIDTH_Px_TSFIFO_OUTSPEED_FIELD 8 + +#define STV090x_Px_TSSTATUS(__x) (0xF581 - (__x - 1) * 0x200) +#define STV090x_P1_TSSTATUS STV090x_Px_TSSTATUS(1) +#define STV090x_P2_TSSTATUS STV090x_Px_TSSTATUS(2) +#define STV090x_OFFST_Px_TSFIFO_LINEOK_FIELD 7 +#define STV090x_WIDTH_Px_TSFIFO_LINEOK_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_ERROR_FIELD 6 +#define STV090x_WIDTH_Px_TSFIFO_ERROR_FIELD 1 + +#define STV090x_Px_TSSTATUS2(__x) (0xF582 - (__x - 1) * 0x200) +#define STV090x_P1_TSSTATUS2 STV090x_Px_TSSTATUS2(1) +#define STV090x_P2_TSSTATUS2 STV090x_Px_TSSTATUS2(2) +#define STV090x_OFFST_Px_TSFIFO_DEMODSEL_FIELD 7 +#define STV090x_WIDTH_Px_TSFIFO_DEMODSEL_FIELD 1 +#define STV090x_OFFST_Px_TSFIFOSPEED_STORE_FIELD 6 +#define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD 1 +#define STV090x_OFFST_Px_DILXX_RESET_FIELD 5 +#define STV090x_WIDTH_Px_DILXX_RESET_FIELD 1 +#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 4 +#define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD 1 +#define STV090x_OFFST_Px_SCRAMBDETECT_FIELD 1 +#define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD 1 + +#define STV090x_Px_TSBITRATEy(__x, __y) (0xF584 - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_TSBITRATE0 STV090x_Px_TSBITRATEy(1, 0) +#define STV090x_P1_TSBITRATE1 STV090x_Px_TSBITRATEy(1, 1) +#define STV090x_P2_TSBITRATE0 STV090x_Px_TSBITRATEy(2, 0) +#define STV090x_P2_TSBITRATE1 STV090x_Px_TSBITRATEy(2, 1) +#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 0 +#define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD 8 + +#define STV090x_Px_ERRCTRL1(__x) (0xF598 - (__x - 1) * 0x200) +#define STV090x_P1_ERRCTRL1 STV090x_Px_ERRCTRL1(1) +#define STV090x_P2_ERRCTRL1 STV090x_Px_ERRCTRL1(2) +#define STV090x_OFFST_Px_ERR_SOURCE_FIELD 4 +#define STV090x_WIDTH_Px_ERR_SOURCE_FIELD 4 +#define STV090x_OFFST_Px_NUM_EVENT_FIELD 0 +#define STV090x_WIDTH_Px_NUM_EVENT_FIELD 3 + +#define STV090x_Px_ERRCNT12(__x) (0xF599 - (__x - 1) * 0x200) +#define STV090x_P1_ERRCNT12 STV090x_Px_ERRCNT12(1) +#define STV090x_P2_ERRCNT12 STV090x_Px_ERRCNT12(2) +#define STV090x_OFFST_Px_ERRCNT1_OLDVALUE_FIELD 7 +#define STV090x_WIDTH_Px_ERRCNT1_OLDVALUE_FIELD 1 +#define STV090x_OFFST_Px_ERR_CNT12_FIELD 0 +#define STV090x_WIDTH_Px_ERR_CNT12_FIELD 7 + +#define STV090x_Px_ERRCNT11(__x) (0xF59A - (__x - 1) * 0x200) +#define STV090x_P1_ERRCNT11 STV090x_Px_ERRCNT11(1) +#define STV090x_P2_ERRCNT11 STV090x_Px_ERRCNT11(2) +#define STV090x_OFFST_Px_ERR_CNT11_FIELD 0 +#define STV090x_WIDTH_Px_ERR_CNT11_FIELD 8 + +#define STV090x_Px_ERRCNT10(__x) (0xF59B - (__x - 1) * 0x200) +#define STV090x_P1_ERRCNT10 STV090x_Px_ERRCNT10(1) +#define STV090x_P2_ERRCNT10 STV090x_Px_ERRCNT10(2) +#define STV090x_OFFST_Px_ERR_CNT10_FIELD 0 +#define STV090x_WIDTH_Px_ERR_CNT10_FIELD 8 + +#define STV090x_Px_ERRCTRL2(__x) (0xF59C - (__x - 1) * 0x200) +#define STV090x_P1_ERRCTRL2 STV090x_Px_ERRCTRL2(1) +#define STV090x_P2_ERRCTRL2 STV090x_Px_ERRCTRL2(2) +#define STV090x_OFFST_Px_ERR_SOURCE2_FIELD 4 +#define STV090x_WIDTH_Px_ERR_SOURCE2_FIELD 4 +#define STV090x_OFFST_Px_NUM_EVENT2_FIELD 0 +#define STV090x_WIDTH_Px_NUM_EVENT2_FIELD 3 + +#define STV090x_Px_ERRCNT22(__x) (0xF59D - (__x - 1) * 0x200) +#define STV090x_P1_ERRCNT22 STV090x_Px_ERRCNT22(1) +#define STV090x_P2_ERRCNT22 STV090x_Px_ERRCNT22(2) +#define STV090x_OFFST_Px_ERRCNT2_OLDVALUE_FIELD 7 +#define STV090x_WIDTH_Px_ERRCNT2_OLDVALUE_FIELD 1 +#define STV090x_OFFST_Px_ERR_CNT2_FIELD 0 +#define STV090x_WIDTH_Px_ERR_CNT2_FIELD 7 + +#define STV090x_Px_ERRCNT21(__x) (0xF59E - (__x - 1) * 0x200) +#define STV090x_P1_ERRCNT21 STV090x_Px_ERRCNT21(1) +#define STV090x_P2_ERRCNT21 STV090x_Px_ERRCNT21(2) +#define STV090x_OFFST_Px_ERR_CNT21_FIELD 0 +#define STV090x_WIDTH_Px_ERR_CNT21_FIELD 8 + +#define STV090x_Px_ERRCNT20(__x) (0xF59F - (__x - 1) * 0x200) +#define STV090x_P1_ERRCNT20 STV090x_Px_ERRCNT20(1) +#define STV090x_P2_ERRCNT20 STV090x_Px_ERRCNT20(2) +#define STV090x_OFFST_Px_ERR_CNT20_FIELD 0 +#define STV090x_WIDTH_Px_ERR_CNT20_FIELD 8 + +#define STV090x_Px_FECSPY(__x) (0xF5A0 - (__x - 1) * 0x200) +#define STV090x_P1_FECSPY STV090x_Px_FECSPY(1) +#define STV090x_P2_FECSPY STV090x_Px_FECSPY(2) +#define STV090x_OFFST_Px_SPY_ENABLE_FIELD 7 +#define STV090x_WIDTH_Px_SPY_ENABLE_FIELD 1 +#define STV090x_OFFST_Px_BERMETER_DATAMAODE_FIELD 2 +#define STV090x_WIDTH_Px_BERMETER_DATAMAODE_FIELD 2 + +#define STV090x_Px_FSPYCFG(__x) (0xF5A1 - (__x - 1) * 0x200) +#define STV090x_P1_FSPYCFG STV090x_Px_FSPYCFG(1) +#define STV090x_P2_FSPYCFG STV090x_Px_FSPYCFG(2) +#define STV090x_OFFST_Px_RST_ON_ERROR_FIELD 5 +#define STV090x_WIDTH_Px_RST_ON_ERROR_FIELD 1 +#define STV090x_OFFST_Px_ONE_SHOT_FIELD 4 +#define STV090x_WIDTH_Px_ONE_SHOT_FIELD 1 +#define STV090x_OFFST_Px_I2C_MODE_FIELD 2 +#define STV090x_WIDTH_Px_I2C_MODE_FIELD 2 + +#define STV090x_Px_FSPYDATA(__x) (0xF5A2 - (__x - 1) * 0x200) +#define STV090x_P1_FSPYDATA STV090x_Px_FSPYDATA(1) +#define STV090x_P2_FSPYDATA STV090x_Px_FSPYDATA(2) +#define STV090x_OFFST_Px_SPY_STUFFING_FIELD 7 +#define STV090x_WIDTH_Px_SPY_STUFFING_FIELD 1 +#define STV090x_OFFST_Px_SPY_CNULLPKT_FIELD 5 +#define STV090x_WIDTH_Px_SPY_CNULLPKT_FIELD 1 +#define STV090x_OFFST_Px_SPY_OUTDATA_MODE_FIELD 0 +#define STV090x_WIDTH_Px_SPY_OUTDATA_MODE_FIELD 5 + +#define STV090x_Px_FSPYOUT(__x) (0xF5A3 - (__x - 1) * 0x200) +#define STV090x_P1_FSPYOUT STV090x_Px_FSPYOUT(1) +#define STV090x_P2_FSPYOUT STV090x_Px_FSPYOUT(2) +#define STV090x_OFFST_Px_FSPY_DIRECT_FIELD 7 +#define STV090x_WIDTH_Px_FSPY_DIRECT_FIELD 1 +#define STV090x_OFFST_Px_STUFF_MODE_FIELD 0 +#define STV090x_WIDTH_Px_STUFF_MODE_FIELD 3 + +#define STV090x_Px_FSTATUS(__x) (0xF5A4 - (__x - 1) * 0x200) +#define STV090x_P1_FSTATUS STV090x_Px_FSTATUS(1) +#define STV090x_P2_FSTATUS STV090x_Px_FSTATUS(2) +#define STV090x_OFFST_Px_SPY_ENDSIM_FIELD 7 +#define STV090x_WIDTH_Px_SPY_ENDSIM_FIELD 1 +#define STV090x_OFFST_Px_VALID_SIM_FIELD 6 +#define STV090x_WIDTH_Px_VALID_SIM_FIELD 1 +#define STV090x_OFFST_Px_FOUND_SIGNAL_FIELD 5 +#define STV090x_WIDTH_Px_FOUND_SIGNAL_FIELD 1 +#define STV090x_OFFST_Px_DSS_SYNCBYTE_FIELD 4 +#define STV090x_WIDTH_Px_DSS_SYNCBYTE_FIELD 1 +#define STV090x_OFFST_Px_RESULT_STATE_FIELD 0 +#define STV090x_WIDTH_Px_RESULT_STATE_FIELD 4 + +#define STV090x_Px_FBERCPT4(__x) (0xF5A8 - (__x - 1) * 0x200) +#define STV090x_P1_FBERCPT4 STV090x_Px_FBERCPT4(1) +#define STV090x_P2_FBERCPT4 STV090x_Px_FBERCPT4(2) +#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 + +#define STV090x_Px_FBERCPT3(__x) (0xF5A9 - (__x - 1) * 0x200) +#define STV090x_P1_FBERCPT3 STV090x_Px_FBERCPT3(1) +#define STV090x_P2_FBERCPT3 STV090x_Px_FBERCPT3(2) +#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 + +#define STV090x_Px_FBERCPT2(__x) (0xF5AA - (__x - 1) * 0x200) +#define STV090x_P1_FBERCPT2 STV090x_Px_FBERCPT2(1) +#define STV090x_P2_FBERCPT2 STV090x_Px_FBERCPT2(2) +#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 + +#define STV090x_Px_FBERCPT1(__x) (0xF5AB - (__x - 1) * 0x200) +#define STV090x_P1_FBERCPT1 STV090x_Px_FBERCPT1(1) +#define STV090x_P2_FBERCPT1 STV090x_Px_FBERCPT1(2) +#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 + +#define STV090x_Px_FBERCPT0(__x) (0xF5AC - (__x - 1) * 0x200) +#define STV090x_P1_FBERCPT0 STV090x_Px_FBERCPT0(1) +#define STV090x_P2_FBERCPT0 STV090x_Px_FBERCPT0(2) +#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 + +#define STV090x_Px_FBERERRy(__x, __y) (0xF5AF - (__x - 1) * 0x200 - __y * 0x1) +#define STV090x_P1_FBERERR0 STV090x_Px_FBERERRy(1, 0) +#define STV090x_P1_FBERERR1 STV090x_Px_FBERERRy(1, 1) +#define STV090x_P1_FBERERR2 STV090x_Px_FBERERRy(1, 2) +#define STV090x_P2_FBERERR0 STV090x_Px_FBERERRy(2, 0) +#define STV090x_P2_FBERERR1 STV090x_Px_FBERERRy(2, 1) +#define STV090x_P2_FBERERR2 STV090x_Px_FBERERRy(2, 2) +#define STV090x_OFFST_Px_FBERMETER_CPT_ERR_FIELD 0 +#define STV090x_WIDTH_Px_FBERMETER_CPT_ERR_FIELD 8 + +#define STV090x_Px_FSPYBER(__x) (0xF5B2 - (__x - 1) * 0x200) +#define STV090x_P1_FSPYBER STV090x_Px_FSPYBER(1) +#define STV090x_P2_FSPYBER STV090x_Px_FSPYBER(2) +#define STV090x_OFFST_Px_FSPYBER_SYNCBYTE_FIELD 4 +#define STV090x_WIDTH_Px_FSPYBER_SYNCBYTE_FIELD 1 +#define STV090x_OFFST_Px_FSPYBER_UNSYNC_FIELD 3 +#define STV090x_WIDTH_Px_FSPYBER_UNSYNC_FIELD 1 +#define STV090x_OFFST_Px_FSPYBER_CTIME_FIELD 0 +#define STV090x_WIDTH_Px_FSPYBER_CTIME_FIELD 3 + +#define STV090x_RCCFGH 0xf600 + +#define STV090x_TSGENERAL 0xF630 +#define STV090x_OFFST_Px_MUXSTREAM_OUT_FIELD 3 +#define STV090x_WIDTH_Px_MUXSTREAM_OUT_FIELD 1 +#define STV090x_OFFST_Px_TSFIFO_PERMPARAL_FIELD 1 +#define STV090x_WIDTH_Px_TSFIFO_PERMPARAL_FIELD 2 + +#define STV090x_TSGENERAL1X 0xf670 +#define STV090x_CFGEXT 0xfa80 + +#define STV090x_TSTRES0 0xFF11 +#define STV090x_OFFST_FRESFEC_FIELD 7 +#define STV090x_WIDTH_FRESFEC_FIELD 1 + +#define STV090x_Px_TSTDISRX(__x) (0xFF67 - (__x - 1) * 0x2) +#define STV090x_P1_TSTDISRX STV090x_Px_TSTDISRX(1) +#define STV090x_P2_TSTDISRX STV090x_Px_TSTDISRX(2) +#define STV090x_OFFST_Px_TSTDISRX_SELECT_FIELD 3 +#define STV090x_WIDTH_Px_TSTDISRX_SELECT_FIELD 1 + +#endif /* __STV090x_REG_H */ diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c new file mode 100644 index 0000000000..e517ff7577 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0910.c @@ -0,0 +1,1834 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ST STV0910 DVB-S/S2 demodulator. + * + * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de> + * Marcus Metzler <mocm@metzlerbros.de> + * developed for Digital Devices GmbH + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "stv0910.h" +#include "stv0910_regs.h" + +#define EXT_CLOCK 30000000 +#define TUNING_DELAY 200 +#define BER_SRC_S 0x20 +#define BER_SRC_S2 0x20 + +static LIST_HEAD(stvlist); + +enum receive_mode { RCVMODE_NONE, RCVMODE_DVBS, RCVMODE_DVBS2, RCVMODE_AUTO }; + +enum dvbs2_fectype { DVBS2_64K, DVBS2_16K }; + +enum dvbs2_mod_cod { + DVBS2_DUMMY_PLF, DVBS2_QPSK_1_4, DVBS2_QPSK_1_3, DVBS2_QPSK_2_5, + DVBS2_QPSK_1_2, DVBS2_QPSK_3_5, DVBS2_QPSK_2_3, DVBS2_QPSK_3_4, + DVBS2_QPSK_4_5, DVBS2_QPSK_5_6, DVBS2_QPSK_8_9, DVBS2_QPSK_9_10, + DVBS2_8PSK_3_5, DVBS2_8PSK_2_3, DVBS2_8PSK_3_4, DVBS2_8PSK_5_6, + DVBS2_8PSK_8_9, DVBS2_8PSK_9_10, DVBS2_16APSK_2_3, DVBS2_16APSK_3_4, + DVBS2_16APSK_4_5, DVBS2_16APSK_5_6, DVBS2_16APSK_8_9, DVBS2_16APSK_9_10, + DVBS2_32APSK_3_4, DVBS2_32APSK_4_5, DVBS2_32APSK_5_6, DVBS2_32APSK_8_9, + DVBS2_32APSK_9_10 +}; + +enum fe_stv0910_mod_cod { + FE_DUMMY_PLF, FE_QPSK_14, FE_QPSK_13, FE_QPSK_25, + FE_QPSK_12, FE_QPSK_35, FE_QPSK_23, FE_QPSK_34, + FE_QPSK_45, FE_QPSK_56, FE_QPSK_89, FE_QPSK_910, + FE_8PSK_35, FE_8PSK_23, FE_8PSK_34, FE_8PSK_56, + FE_8PSK_89, FE_8PSK_910, FE_16APSK_23, FE_16APSK_34, + FE_16APSK_45, FE_16APSK_56, FE_16APSK_89, FE_16APSK_910, + FE_32APSK_34, FE_32APSK_45, FE_32APSK_56, FE_32APSK_89, + FE_32APSK_910 +}; + +enum fe_stv0910_roll_off { FE_SAT_35, FE_SAT_25, FE_SAT_20, FE_SAT_15 }; + +static inline u32 muldiv32(u32 a, u32 b, u32 c) +{ + u64 tmp64; + + tmp64 = (u64)a * (u64)b; + do_div(tmp64, c); + + return (u32)tmp64; +} + +struct stv_base { + struct list_head stvlist; + + u8 adr; + struct i2c_adapter *i2c; + struct mutex i2c_lock; /* shared I2C access protect */ + struct mutex reg_lock; /* shared register write protect */ + int count; + + u32 extclk; + u32 mclk; +}; + +struct stv { + struct stv_base *base; + struct dvb_frontend fe; + int nr; + u16 regoff; + u8 i2crpt; + u8 tscfgh; + u8 tsgeneral; + u8 tsspeed; + u8 single; + unsigned long tune_time; + + s32 search_range; + u32 started; + u32 demod_lock_time; + enum receive_mode receive_mode; + u32 demod_timeout; + u32 fec_timeout; + u32 first_time_lock; + u8 demod_bits; + u32 symbol_rate; + + u8 last_viterbi_rate; + enum fe_code_rate puncture_rate; + enum fe_stv0910_mod_cod mod_cod; + enum dvbs2_fectype fectype; + u32 pilots; + enum fe_stv0910_roll_off feroll_off; + + int is_standard_broadcast; + int is_vcm; + + u32 cur_scrambling_code; + + u32 last_bernumerator; + u32 last_berdenominator; + u8 berscale; + + u8 vth[6]; +}; + +struct sinit_table { + u16 address; + u8 data; +}; + +struct slookup { + s16 value; + u32 reg_value; +}; + +static int write_reg(struct stv *state, u16 reg, u8 val) +{ + struct i2c_adapter *adap = state->base->i2c; + u8 data[3] = {reg >> 8, reg & 0xff, val}; + struct i2c_msg msg = {.addr = state->base->adr, .flags = 0, + .buf = data, .len = 3}; + + if (i2c_transfer(adap, &msg, 1) != 1) { + dev_warn(&adap->dev, "i2c write error ([%02x] %04x: %02x)\n", + state->base->adr, reg, val); + return -EIO; + } + return 0; +} + +static inline int i2c_read_regs16(struct i2c_adapter *adapter, u8 adr, + u16 reg, u8 *val, int count) +{ + u8 msg[2] = {reg >> 8, reg & 0xff}; + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = msg, .len = 2}, + {.addr = adr, .flags = I2C_M_RD, + .buf = val, .len = count } }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + dev_warn(&adapter->dev, "i2c read error ([%02x] %04x)\n", + adr, reg); + return -EIO; + } + return 0; +} + +static int read_reg(struct stv *state, u16 reg, u8 *val) +{ + return i2c_read_regs16(state->base->i2c, state->base->adr, + reg, val, 1); +} + +static int read_regs(struct stv *state, u16 reg, u8 *val, int len) +{ + return i2c_read_regs16(state->base->i2c, state->base->adr, + reg, val, len); +} + +static int write_shared_reg(struct stv *state, u16 reg, u8 mask, u8 val) +{ + int status; + u8 tmp; + + mutex_lock(&state->base->reg_lock); + status = read_reg(state, reg, &tmp); + if (!status) + status = write_reg(state, reg, (tmp & ~mask) | (val & mask)); + mutex_unlock(&state->base->reg_lock); + return status; +} + +static int write_field(struct stv *state, u32 field, u8 val) +{ + int status; + u8 shift, mask, old, new; + + status = read_reg(state, field >> 16, &old); + if (status) + return status; + mask = field & 0xff; + shift = (field >> 12) & 0xf; + new = ((val << shift) & mask) | (old & ~mask); + if (new == old) + return 0; + return write_reg(state, field >> 16, new); +} + +#define SET_FIELD(_reg, _val) \ + write_field(state, state->nr ? FSTV0910_P2_##_reg : \ + FSTV0910_P1_##_reg, _val) + +#define SET_REG(_reg, _val) \ + write_reg(state, state->nr ? RSTV0910_P2_##_reg : \ + RSTV0910_P1_##_reg, _val) + +#define GET_REG(_reg, _val) \ + read_reg(state, state->nr ? RSTV0910_P2_##_reg : \ + RSTV0910_P1_##_reg, _val) + +static const struct slookup s1_sn_lookup[] = { + { 0, 9242 }, /* C/N= 0dB */ + { 5, 9105 }, /* C/N= 0.5dB */ + { 10, 8950 }, /* C/N= 1.0dB */ + { 15, 8780 }, /* C/N= 1.5dB */ + { 20, 8566 }, /* C/N= 2.0dB */ + { 25, 8366 }, /* C/N= 2.5dB */ + { 30, 8146 }, /* C/N= 3.0dB */ + { 35, 7908 }, /* C/N= 3.5dB */ + { 40, 7666 }, /* C/N= 4.0dB */ + { 45, 7405 }, /* C/N= 4.5dB */ + { 50, 7136 }, /* C/N= 5.0dB */ + { 55, 6861 }, /* C/N= 5.5dB */ + { 60, 6576 }, /* C/N= 6.0dB */ + { 65, 6330 }, /* C/N= 6.5dB */ + { 70, 6048 }, /* C/N= 7.0dB */ + { 75, 5768 }, /* C/N= 7.5dB */ + { 80, 5492 }, /* C/N= 8.0dB */ + { 85, 5224 }, /* C/N= 8.5dB */ + { 90, 4959 }, /* C/N= 9.0dB */ + { 95, 4709 }, /* C/N= 9.5dB */ + { 100, 4467 }, /* C/N=10.0dB */ + { 105, 4236 }, /* C/N=10.5dB */ + { 110, 4013 }, /* C/N=11.0dB */ + { 115, 3800 }, /* C/N=11.5dB */ + { 120, 3598 }, /* C/N=12.0dB */ + { 125, 3406 }, /* C/N=12.5dB */ + { 130, 3225 }, /* C/N=13.0dB */ + { 135, 3052 }, /* C/N=13.5dB */ + { 140, 2889 }, /* C/N=14.0dB */ + { 145, 2733 }, /* C/N=14.5dB */ + { 150, 2587 }, /* C/N=15.0dB */ + { 160, 2318 }, /* C/N=16.0dB */ + { 170, 2077 }, /* C/N=17.0dB */ + { 180, 1862 }, /* C/N=18.0dB */ + { 190, 1670 }, /* C/N=19.0dB */ + { 200, 1499 }, /* C/N=20.0dB */ + { 210, 1347 }, /* C/N=21.0dB */ + { 220, 1213 }, /* C/N=22.0dB */ + { 230, 1095 }, /* C/N=23.0dB */ + { 240, 992 }, /* C/N=24.0dB */ + { 250, 900 }, /* C/N=25.0dB */ + { 260, 826 }, /* C/N=26.0dB */ + { 270, 758 }, /* C/N=27.0dB */ + { 280, 702 }, /* C/N=28.0dB */ + { 290, 653 }, /* C/N=29.0dB */ + { 300, 613 }, /* C/N=30.0dB */ + { 310, 579 }, /* C/N=31.0dB */ + { 320, 550 }, /* C/N=32.0dB */ + { 330, 526 }, /* C/N=33.0dB */ + { 350, 490 }, /* C/N=33.0dB */ + { 400, 445 }, /* C/N=40.0dB */ + { 450, 430 }, /* C/N=45.0dB */ + { 500, 426 }, /* C/N=50.0dB */ + { 510, 425 } /* C/N=51.0dB */ +}; + +static const struct slookup s2_sn_lookup[] = { + { -30, 13950 }, /* C/N=-2.5dB */ + { -25, 13580 }, /* C/N=-2.5dB */ + { -20, 13150 }, /* C/N=-2.0dB */ + { -15, 12760 }, /* C/N=-1.5dB */ + { -10, 12345 }, /* C/N=-1.0dB */ + { -5, 11900 }, /* C/N=-0.5dB */ + { 0, 11520 }, /* C/N= 0dB */ + { 5, 11080 }, /* C/N= 0.5dB */ + { 10, 10630 }, /* C/N= 1.0dB */ + { 15, 10210 }, /* C/N= 1.5dB */ + { 20, 9790 }, /* C/N= 2.0dB */ + { 25, 9390 }, /* C/N= 2.5dB */ + { 30, 8970 }, /* C/N= 3.0dB */ + { 35, 8575 }, /* C/N= 3.5dB */ + { 40, 8180 }, /* C/N= 4.0dB */ + { 45, 7800 }, /* C/N= 4.5dB */ + { 50, 7430 }, /* C/N= 5.0dB */ + { 55, 7080 }, /* C/N= 5.5dB */ + { 60, 6720 }, /* C/N= 6.0dB */ + { 65, 6320 }, /* C/N= 6.5dB */ + { 70, 6060 }, /* C/N= 7.0dB */ + { 75, 5760 }, /* C/N= 7.5dB */ + { 80, 5480 }, /* C/N= 8.0dB */ + { 85, 5200 }, /* C/N= 8.5dB */ + { 90, 4930 }, /* C/N= 9.0dB */ + { 95, 4680 }, /* C/N= 9.5dB */ + { 100, 4425 }, /* C/N=10.0dB */ + { 105, 4210 }, /* C/N=10.5dB */ + { 110, 3980 }, /* C/N=11.0dB */ + { 115, 3765 }, /* C/N=11.5dB */ + { 120, 3570 }, /* C/N=12.0dB */ + { 125, 3315 }, /* C/N=12.5dB */ + { 130, 3140 }, /* C/N=13.0dB */ + { 135, 2980 }, /* C/N=13.5dB */ + { 140, 2820 }, /* C/N=14.0dB */ + { 145, 2670 }, /* C/N=14.5dB */ + { 150, 2535 }, /* C/N=15.0dB */ + { 160, 2270 }, /* C/N=16.0dB */ + { 170, 2035 }, /* C/N=17.0dB */ + { 180, 1825 }, /* C/N=18.0dB */ + { 190, 1650 }, /* C/N=19.0dB */ + { 200, 1485 }, /* C/N=20.0dB */ + { 210, 1340 }, /* C/N=21.0dB */ + { 220, 1212 }, /* C/N=22.0dB */ + { 230, 1100 }, /* C/N=23.0dB */ + { 240, 1000 }, /* C/N=24.0dB */ + { 250, 910 }, /* C/N=25.0dB */ + { 260, 836 }, /* C/N=26.0dB */ + { 270, 772 }, /* C/N=27.0dB */ + { 280, 718 }, /* C/N=28.0dB */ + { 290, 671 }, /* C/N=29.0dB */ + { 300, 635 }, /* C/N=30.0dB */ + { 310, 602 }, /* C/N=31.0dB */ + { 320, 575 }, /* C/N=32.0dB */ + { 330, 550 }, /* C/N=33.0dB */ + { 350, 517 }, /* C/N=35.0dB */ + { 400, 480 }, /* C/N=40.0dB */ + { 450, 466 }, /* C/N=45.0dB */ + { 500, 464 }, /* C/N=50.0dB */ + { 510, 463 }, /* C/N=51.0dB */ +}; + +static const struct slookup padc_lookup[] = { + { 0, 118000 }, /* PADC= +0dBm */ + { -100, 93600 }, /* PADC= -1dBm */ + { -200, 74500 }, /* PADC= -2dBm */ + { -300, 59100 }, /* PADC= -3dBm */ + { -400, 47000 }, /* PADC= -4dBm */ + { -500, 37300 }, /* PADC= -5dBm */ + { -600, 29650 }, /* PADC= -6dBm */ + { -700, 23520 }, /* PADC= -7dBm */ + { -900, 14850 }, /* PADC= -9dBm */ + { -1100, 9380 }, /* PADC=-11dBm */ + { -1300, 5910 }, /* PADC=-13dBm */ + { -1500, 3730 }, /* PADC=-15dBm */ + { -1700, 2354 }, /* PADC=-17dBm */ + { -1900, 1485 }, /* PADC=-19dBm */ + { -2000, 1179 }, /* PADC=-20dBm */ + { -2100, 1000 }, /* PADC=-21dBm */ +}; + +/********************************************************************* + * Tracking carrier loop carrier QPSK 1/4 to 8PSK 9/10 long Frame + *********************************************************************/ +static const u8 s2car_loop[] = { + /* + * Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff + * 20MPon 20MPoff 30MPon 30MPoff + */ + + /* FE_QPSK_14 */ + 0x0C, 0x3C, 0x0B, 0x3C, 0x2A, 0x2C, 0x2A, 0x1C, 0x3A, 0x3B, + /* FE_QPSK_13 */ + 0x0C, 0x3C, 0x0B, 0x3C, 0x2A, 0x2C, 0x3A, 0x0C, 0x3A, 0x2B, + /* FE_QPSK_25 */ + 0x1C, 0x3C, 0x1B, 0x3C, 0x3A, 0x1C, 0x3A, 0x3B, 0x3A, 0x2B, + /* FE_QPSK_12 */ + 0x0C, 0x1C, 0x2B, 0x1C, 0x0B, 0x2C, 0x0B, 0x0C, 0x2A, 0x2B, + /* FE_QPSK_35 */ + 0x1C, 0x1C, 0x2B, 0x1C, 0x0B, 0x2C, 0x0B, 0x0C, 0x2A, 0x2B, + /* FE_QPSK_23 */ + 0x2C, 0x2C, 0x2B, 0x1C, 0x0B, 0x2C, 0x0B, 0x0C, 0x2A, 0x2B, + /* FE_QPSK_34 */ + 0x3C, 0x2C, 0x3B, 0x2C, 0x1B, 0x1C, 0x1B, 0x3B, 0x3A, 0x1B, + /* FE_QPSK_45 */ + 0x0D, 0x3C, 0x3B, 0x2C, 0x1B, 0x1C, 0x1B, 0x3B, 0x3A, 0x1B, + /* FE_QPSK_56 */ + 0x1D, 0x3C, 0x0C, 0x2C, 0x2B, 0x1C, 0x1B, 0x3B, 0x0B, 0x1B, + /* FE_QPSK_89 */ + 0x3D, 0x0D, 0x0C, 0x2C, 0x2B, 0x0C, 0x2B, 0x2B, 0x0B, 0x0B, + /* FE_QPSK_910 */ + 0x1E, 0x0D, 0x1C, 0x2C, 0x3B, 0x0C, 0x2B, 0x2B, 0x1B, 0x0B, + /* FE_8PSK_35 */ + 0x28, 0x09, 0x28, 0x09, 0x28, 0x09, 0x28, 0x08, 0x28, 0x27, + /* FE_8PSK_23 */ + 0x19, 0x29, 0x19, 0x29, 0x19, 0x29, 0x38, 0x19, 0x28, 0x09, + /* FE_8PSK_34 */ + 0x1A, 0x0B, 0x1A, 0x3A, 0x0A, 0x2A, 0x39, 0x2A, 0x39, 0x1A, + /* FE_8PSK_56 */ + 0x2B, 0x2B, 0x1B, 0x1B, 0x0B, 0x1B, 0x1A, 0x0B, 0x1A, 0x1A, + /* FE_8PSK_89 */ + 0x0C, 0x0C, 0x3B, 0x3B, 0x1B, 0x1B, 0x2A, 0x0B, 0x2A, 0x2A, + /* FE_8PSK_910 */ + 0x0C, 0x1C, 0x0C, 0x3B, 0x2B, 0x1B, 0x3A, 0x0B, 0x2A, 0x2A, + + /********************************************************************** + * Tracking carrier loop carrier 16APSK 2/3 to 32APSK 9/10 long Frame + **********************************************************************/ + + /* + * Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon + * 20MPoff 30MPon 30MPoff + */ + + /* FE_16APSK_23 */ + 0x0A, 0x0A, 0x0A, 0x0A, 0x1A, 0x0A, 0x39, 0x0A, 0x29, 0x0A, + /* FE_16APSK_34 */ + 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0A, 0x2A, 0x0A, 0x1A, 0x0A, + /* FE_16APSK_45 */ + 0x0A, 0x0A, 0x0A, 0x0A, 0x1B, 0x0A, 0x3A, 0x0A, 0x2A, 0x0A, + /* FE_16APSK_56 */ + 0x0A, 0x0A, 0x0A, 0x0A, 0x1B, 0x0A, 0x3A, 0x0A, 0x2A, 0x0A, + /* FE_16APSK_89 */ + 0x0A, 0x0A, 0x0A, 0x0A, 0x2B, 0x0A, 0x0B, 0x0A, 0x3A, 0x0A, + /* FE_16APSK_910 */ + 0x0A, 0x0A, 0x0A, 0x0A, 0x2B, 0x0A, 0x0B, 0x0A, 0x3A, 0x0A, + /* FE_32APSK_34 */ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + /* FE_32APSK_45 */ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + /* FE_32APSK_56 */ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + /* FE_32APSK_89 */ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + /* FE_32APSK_910 */ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +}; + +static u8 get_optim_cloop(struct stv *state, + enum fe_stv0910_mod_cod mod_cod, u32 pilots) +{ + int i = 0; + + if (mod_cod >= FE_32APSK_910) + i = ((int)FE_32APSK_910 - (int)FE_QPSK_14) * 10; + else if (mod_cod >= FE_QPSK_14) + i = ((int)mod_cod - (int)FE_QPSK_14) * 10; + + if (state->symbol_rate <= 3000000) + i += 0; + else if (state->symbol_rate <= 7000000) + i += 2; + else if (state->symbol_rate <= 15000000) + i += 4; + else if (state->symbol_rate <= 25000000) + i += 6; + else + i += 8; + + if (!pilots) + i += 1; + + return s2car_loop[i]; +} + +static int get_cur_symbol_rate(struct stv *state, u32 *p_symbol_rate) +{ + int status = 0; + u8 symb_freq0; + u8 symb_freq1; + u8 symb_freq2; + u8 symb_freq3; + u8 tim_offs0; + u8 tim_offs1; + u8 tim_offs2; + u32 symbol_rate; + s32 timing_offset; + + *p_symbol_rate = 0; + if (!state->started) + return status; + + read_reg(state, RSTV0910_P2_SFR3 + state->regoff, &symb_freq3); + read_reg(state, RSTV0910_P2_SFR2 + state->regoff, &symb_freq2); + read_reg(state, RSTV0910_P2_SFR1 + state->regoff, &symb_freq1); + read_reg(state, RSTV0910_P2_SFR0 + state->regoff, &symb_freq0); + read_reg(state, RSTV0910_P2_TMGREG2 + state->regoff, &tim_offs2); + read_reg(state, RSTV0910_P2_TMGREG1 + state->regoff, &tim_offs1); + read_reg(state, RSTV0910_P2_TMGREG0 + state->regoff, &tim_offs0); + + symbol_rate = ((u32)symb_freq3 << 24) | ((u32)symb_freq2 << 16) | + ((u32)symb_freq1 << 8) | (u32)symb_freq0; + timing_offset = ((u32)tim_offs2 << 16) | ((u32)tim_offs1 << 8) | + (u32)tim_offs0; + + if ((timing_offset & (1 << 23)) != 0) + timing_offset |= 0xFF000000; /* Sign extent */ + + symbol_rate = (u32)(((u64)symbol_rate * state->base->mclk) >> 32); + timing_offset = (s32)(((s64)symbol_rate * (s64)timing_offset) >> 29); + + *p_symbol_rate = symbol_rate + timing_offset; + + return 0; +} + +static int get_signal_parameters(struct stv *state) +{ + u8 tmp; + + if (!state->started) + return -EINVAL; + + if (state->receive_mode == RCVMODE_DVBS2) { + read_reg(state, RSTV0910_P2_DMDMODCOD + state->regoff, &tmp); + state->mod_cod = (enum fe_stv0910_mod_cod)((tmp & 0x7c) >> 2); + state->pilots = (tmp & 0x01) != 0; + state->fectype = (enum dvbs2_fectype)((tmp & 0x02) >> 1); + + } else if (state->receive_mode == RCVMODE_DVBS) { + read_reg(state, RSTV0910_P2_VITCURPUN + state->regoff, &tmp); + state->puncture_rate = FEC_NONE; + switch (tmp & 0x1F) { + case 0x0d: + state->puncture_rate = FEC_1_2; + break; + case 0x12: + state->puncture_rate = FEC_2_3; + break; + case 0x15: + state->puncture_rate = FEC_3_4; + break; + case 0x18: + state->puncture_rate = FEC_5_6; + break; + case 0x1a: + state->puncture_rate = FEC_7_8; + break; + } + state->is_vcm = 0; + state->is_standard_broadcast = 1; + state->feroll_off = FE_SAT_35; + } + return 0; +} + +static int tracking_optimization(struct stv *state) +{ + u8 tmp; + + read_reg(state, RSTV0910_P2_DMDCFGMD + state->regoff, &tmp); + tmp &= ~0xC0; + + switch (state->receive_mode) { + case RCVMODE_DVBS: + tmp |= 0x40; + break; + case RCVMODE_DVBS2: + tmp |= 0x80; + break; + default: + tmp |= 0xC0; + break; + } + write_reg(state, RSTV0910_P2_DMDCFGMD + state->regoff, tmp); + + if (state->receive_mode == RCVMODE_DVBS2) { + /* Disable Reed-Solomon */ + write_shared_reg(state, + RSTV0910_TSTTSRS, state->nr ? 0x02 : 0x01, + 0x03); + + if (state->fectype == DVBS2_64K) { + u8 aclc = get_optim_cloop(state, state->mod_cod, + state->pilots); + + if (state->mod_cod <= FE_QPSK_910) { + write_reg(state, RSTV0910_P2_ACLC2S2Q + + state->regoff, aclc); + } else if (state->mod_cod <= FE_8PSK_910) { + write_reg(state, RSTV0910_P2_ACLC2S2Q + + state->regoff, 0x2a); + write_reg(state, RSTV0910_P2_ACLC2S28 + + state->regoff, aclc); + } else if (state->mod_cod <= FE_16APSK_910) { + write_reg(state, RSTV0910_P2_ACLC2S2Q + + state->regoff, 0x2a); + write_reg(state, RSTV0910_P2_ACLC2S216A + + state->regoff, aclc); + } else if (state->mod_cod <= FE_32APSK_910) { + write_reg(state, RSTV0910_P2_ACLC2S2Q + + state->regoff, 0x2a); + write_reg(state, RSTV0910_P2_ACLC2S232A + + state->regoff, aclc); + } + } + } + return 0; +} + +static s32 table_lookup(const struct slookup *table, + int table_size, u32 reg_value) +{ + s32 value; + int imin = 0; + int imax = table_size - 1; + int i; + s32 reg_diff; + + /* Assumes Table[0].RegValue > Table[imax].RegValue */ + if (reg_value >= table[0].reg_value) { + value = table[0].value; + } else if (reg_value <= table[imax].reg_value) { + value = table[imax].value; + } else { + while ((imax - imin) > 1) { + i = (imax + imin) / 2; + if ((table[imin].reg_value >= reg_value) && + (reg_value >= table[i].reg_value)) + imax = i; + else + imin = i; + } + + reg_diff = table[imax].reg_value - table[imin].reg_value; + value = table[imin].value; + if (reg_diff != 0) + value += ((s32)(reg_value - table[imin].reg_value) * + (s32)(table[imax].value + - table[imin].value)) + / (reg_diff); + } + + return value; +} + +static int get_signal_to_noise(struct stv *state, s32 *signal_to_noise) +{ + u8 data0; + u8 data1; + u16 data; + int n_lookup; + const struct slookup *lookup; + + *signal_to_noise = 0; + + if (!state->started) + return -EINVAL; + + if (state->receive_mode == RCVMODE_DVBS2) { + read_reg(state, RSTV0910_P2_NNOSPLHT1 + state->regoff, + &data1); + read_reg(state, RSTV0910_P2_NNOSPLHT0 + state->regoff, + &data0); + n_lookup = ARRAY_SIZE(s2_sn_lookup); + lookup = s2_sn_lookup; + } else { + read_reg(state, RSTV0910_P2_NNOSDATAT1 + state->regoff, + &data1); + read_reg(state, RSTV0910_P2_NNOSDATAT0 + state->regoff, + &data0); + n_lookup = ARRAY_SIZE(s1_sn_lookup); + lookup = s1_sn_lookup; + } + data = (((u16)data1) << 8) | (u16)data0; + *signal_to_noise = table_lookup(lookup, n_lookup, data); + return 0; +} + +static int get_bit_error_rate_s(struct stv *state, u32 *bernumerator, + u32 *berdenominator) +{ + u8 regs[3]; + + int status = read_regs(state, + RSTV0910_P2_ERRCNT12 + state->regoff, + regs, 3); + + if (status) + return -EINVAL; + + if ((regs[0] & 0x80) == 0) { + state->last_berdenominator = 1ULL << ((state->berscale * 2) + + 10 + 3); + state->last_bernumerator = ((u32)(regs[0] & 0x7F) << 16) | + ((u32)regs[1] << 8) | regs[2]; + if (state->last_bernumerator < 256 && state->berscale < 6) { + state->berscale += 1; + status = write_reg(state, RSTV0910_P2_ERRCTRL1 + + state->regoff, + 0x20 | state->berscale); + } else if (state->last_bernumerator > 1024 && + state->berscale > 2) { + state->berscale -= 1; + status = write_reg(state, RSTV0910_P2_ERRCTRL1 + + state->regoff, 0x20 | + state->berscale); + } + } + *bernumerator = state->last_bernumerator; + *berdenominator = state->last_berdenominator; + return 0; +} + +static u32 dvbs2_nbch(enum dvbs2_mod_cod mod_cod, enum dvbs2_fectype fectype) +{ + static const u32 nbch[][2] = { + { 0, 0}, /* DUMMY_PLF */ + {16200, 3240}, /* QPSK_1_4, */ + {21600, 5400}, /* QPSK_1_3, */ + {25920, 6480}, /* QPSK_2_5, */ + {32400, 7200}, /* QPSK_1_2, */ + {38880, 9720}, /* QPSK_3_5, */ + {43200, 10800}, /* QPSK_2_3, */ + {48600, 11880}, /* QPSK_3_4, */ + {51840, 12600}, /* QPSK_4_5, */ + {54000, 13320}, /* QPSK_5_6, */ + {57600, 14400}, /* QPSK_8_9, */ + {58320, 16000}, /* QPSK_9_10, */ + {43200, 9720}, /* 8PSK_3_5, */ + {48600, 10800}, /* 8PSK_2_3, */ + {51840, 11880}, /* 8PSK_3_4, */ + {54000, 13320}, /* 8PSK_5_6, */ + {57600, 14400}, /* 8PSK_8_9, */ + {58320, 16000}, /* 8PSK_9_10, */ + {43200, 10800}, /* 16APSK_2_3, */ + {48600, 11880}, /* 16APSK_3_4, */ + {51840, 12600}, /* 16APSK_4_5, */ + {54000, 13320}, /* 16APSK_5_6, */ + {57600, 14400}, /* 16APSK_8_9, */ + {58320, 16000}, /* 16APSK_9_10 */ + {48600, 11880}, /* 32APSK_3_4, */ + {51840, 12600}, /* 32APSK_4_5, */ + {54000, 13320}, /* 32APSK_5_6, */ + {57600, 14400}, /* 32APSK_8_9, */ + {58320, 16000}, /* 32APSK_9_10 */ + }; + + if (mod_cod >= DVBS2_QPSK_1_4 && + mod_cod <= DVBS2_32APSK_9_10 && fectype <= DVBS2_16K) + return nbch[mod_cod][fectype]; + return 64800; +} + +static int get_bit_error_rate_s2(struct stv *state, u32 *bernumerator, + u32 *berdenominator) +{ + u8 regs[3]; + + int status = read_regs(state, RSTV0910_P2_ERRCNT12 + state->regoff, + regs, 3); + + if (status) + return -EINVAL; + + if ((regs[0] & 0x80) == 0) { + state->last_berdenominator = + dvbs2_nbch((enum dvbs2_mod_cod)state->mod_cod, + state->fectype) << + (state->berscale * 2); + state->last_bernumerator = (((u32)regs[0] & 0x7F) << 16) | + ((u32)regs[1] << 8) | regs[2]; + if (state->last_bernumerator < 256 && state->berscale < 6) { + state->berscale += 1; + write_reg(state, RSTV0910_P2_ERRCTRL1 + state->regoff, + 0x20 | state->berscale); + } else if (state->last_bernumerator > 1024 && + state->berscale > 2) { + state->berscale -= 1; + write_reg(state, RSTV0910_P2_ERRCTRL1 + state->regoff, + 0x20 | state->berscale); + } + } + *bernumerator = state->last_bernumerator; + *berdenominator = state->last_berdenominator; + return status; +} + +static int get_bit_error_rate(struct stv *state, u32 *bernumerator, + u32 *berdenominator) +{ + *bernumerator = 0; + *berdenominator = 1; + + switch (state->receive_mode) { + case RCVMODE_DVBS: + return get_bit_error_rate_s(state, + bernumerator, berdenominator); + case RCVMODE_DVBS2: + return get_bit_error_rate_s2(state, + bernumerator, berdenominator); + default: + break; + } + return 0; +} + +static int set_mclock(struct stv *state, u32 master_clock) +{ + u32 idf = 1; + u32 odf = 4; + u32 quartz = state->base->extclk / 1000000; + u32 fphi = master_clock / 1000000; + u32 ndiv = (fphi * odf * idf) / quartz; + u32 cp = 7; + u32 fvco; + + if (ndiv >= 7 && ndiv <= 71) + cp = 7; + else if (ndiv >= 72 && ndiv <= 79) + cp = 8; + else if (ndiv >= 80 && ndiv <= 87) + cp = 9; + else if (ndiv >= 88 && ndiv <= 95) + cp = 10; + else if (ndiv >= 96 && ndiv <= 103) + cp = 11; + else if (ndiv >= 104 && ndiv <= 111) + cp = 12; + else if (ndiv >= 112 && ndiv <= 119) + cp = 13; + else if (ndiv >= 120 && ndiv <= 127) + cp = 14; + else if (ndiv >= 128 && ndiv <= 135) + cp = 15; + else if (ndiv >= 136 && ndiv <= 143) + cp = 16; + else if (ndiv >= 144 && ndiv <= 151) + cp = 17; + else if (ndiv >= 152 && ndiv <= 159) + cp = 18; + else if (ndiv >= 160 && ndiv <= 167) + cp = 19; + else if (ndiv >= 168 && ndiv <= 175) + cp = 20; + else if (ndiv >= 176 && ndiv <= 183) + cp = 21; + else if (ndiv >= 184 && ndiv <= 191) + cp = 22; + else if (ndiv >= 192 && ndiv <= 199) + cp = 23; + else if (ndiv >= 200 && ndiv <= 207) + cp = 24; + else if (ndiv >= 208 && ndiv <= 215) + cp = 25; + else if (ndiv >= 216 && ndiv <= 223) + cp = 26; + else if (ndiv >= 224 && ndiv <= 225) + cp = 27; + + write_reg(state, RSTV0910_NCOARSE, (cp << 3) | idf); + write_reg(state, RSTV0910_NCOARSE2, odf); + write_reg(state, RSTV0910_NCOARSE1, ndiv); + + fvco = (quartz * 2 * ndiv) / idf; + state->base->mclk = fvco / (2 * odf) * 1000000; + + return 0; +} + +static int stop(struct stv *state) +{ + if (state->started) { + u8 tmp; + + write_reg(state, RSTV0910_P2_TSCFGH + state->regoff, + state->tscfgh | 0x01); + read_reg(state, RSTV0910_P2_PDELCTRL1 + state->regoff, &tmp); + tmp &= ~0x01; /* release reset DVBS2 packet delin */ + write_reg(state, RSTV0910_P2_PDELCTRL1 + state->regoff, tmp); + /* Blind optim*/ + write_reg(state, RSTV0910_P2_AGC2O + state->regoff, 0x5B); + /* Stop the demod */ + write_reg(state, RSTV0910_P2_DMDISTATE + state->regoff, 0x5c); + state->started = 0; + } + state->receive_mode = RCVMODE_NONE; + return 0; +} + +static void set_pls(struct stv *state, u32 pls_code) +{ + if (pls_code == state->cur_scrambling_code) + return; + + /* PLROOT2 bit 2 = gold code */ + write_reg(state, RSTV0910_P2_PLROOT0 + state->regoff, + pls_code & 0xff); + write_reg(state, RSTV0910_P2_PLROOT1 + state->regoff, + (pls_code >> 8) & 0xff); + write_reg(state, RSTV0910_P2_PLROOT2 + state->regoff, + 0x04 | ((pls_code >> 16) & 0x03)); + state->cur_scrambling_code = pls_code; +} + +static void set_isi(struct stv *state, u32 isi) +{ + if (isi == NO_STREAM_ID_FILTER) + return; + if (isi == 0x80000000) { + SET_FIELD(FORCE_CONTINUOUS, 1); + SET_FIELD(TSOUT_NOSYNC, 1); + } else { + SET_FIELD(FILTER_EN, 1); + write_reg(state, RSTV0910_P2_ISIENTRY + state->regoff, + isi & 0xff); + write_reg(state, RSTV0910_P2_ISIBITENA + state->regoff, 0xff); + } + SET_FIELD(ALGOSWRST, 1); + SET_FIELD(ALGOSWRST, 0); +} + +static void set_stream_modes(struct stv *state, + struct dtv_frontend_properties *p) +{ + set_isi(state, p->stream_id); + set_pls(state, p->scrambling_sequence_index); +} + +static int init_search_param(struct stv *state, + struct dtv_frontend_properties *p) +{ + SET_FIELD(FORCE_CONTINUOUS, 0); + SET_FIELD(FRAME_MODE, 0); + SET_FIELD(FILTER_EN, 0); + SET_FIELD(TSOUT_NOSYNC, 0); + SET_FIELD(TSFIFO_EMBINDVB, 0); + SET_FIELD(TSDEL_SYNCBYTE, 0); + SET_REG(UPLCCST0, 0xe0); + SET_FIELD(TSINS_TOKEN, 0); + SET_FIELD(HYSTERESIS_THRESHOLD, 0); + SET_FIELD(ISIOBS_MODE, 1); + + set_stream_modes(state, p); + return 0; +} + +static int enable_puncture_rate(struct stv *state, enum fe_code_rate rate) +{ + u8 val; + + switch (rate) { + case FEC_1_2: + val = 0x01; + break; + case FEC_2_3: + val = 0x02; + break; + case FEC_3_4: + val = 0x04; + break; + case FEC_5_6: + val = 0x08; + break; + case FEC_7_8: + val = 0x20; + break; + case FEC_NONE: + default: + val = 0x2f; + break; + } + + return write_reg(state, RSTV0910_P2_PRVIT + state->regoff, val); +} + +static int set_vth_default(struct stv *state) +{ + state->vth[0] = 0xd7; + state->vth[1] = 0x85; + state->vth[2] = 0x58; + state->vth[3] = 0x3a; + state->vth[4] = 0x34; + state->vth[5] = 0x28; + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 0, state->vth[0]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 1, state->vth[1]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 2, state->vth[2]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 3, state->vth[3]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 4, state->vth[4]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 5, state->vth[5]); + return 0; +} + +static int set_vth(struct stv *state) +{ + static const struct slookup vthlookup_table[] = { + {250, 8780}, /* C/N= 1.5dB */ + {100, 7405}, /* C/N= 4.5dB */ + {40, 6330}, /* C/N= 6.5dB */ + {12, 5224}, /* C/N= 8.5dB */ + {5, 4236} /* C/N=10.5dB */ + }; + + int i; + u8 tmp[2]; + int status = read_regs(state, + RSTV0910_P2_NNOSDATAT1 + state->regoff, + tmp, 2); + u16 reg_value = (tmp[0] << 8) | tmp[1]; + s32 vth = table_lookup(vthlookup_table, ARRAY_SIZE(vthlookup_table), + reg_value); + + for (i = 0; i < 6; i += 1) + if (state->vth[i] > vth) + state->vth[i] = vth; + + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 0, state->vth[0]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 1, state->vth[1]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 2, state->vth[2]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 3, state->vth[3]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 4, state->vth[4]); + write_reg(state, RSTV0910_P2_VTH12 + state->regoff + 5, state->vth[5]); + return status; +} + +static int start(struct stv *state, struct dtv_frontend_properties *p) +{ + s32 freq; + u8 reg_dmdcfgmd; + u16 symb; + + if (p->symbol_rate < 100000 || p->symbol_rate > 70000000) + return -EINVAL; + + state->receive_mode = RCVMODE_NONE; + state->demod_lock_time = 0; + + /* Demod Stop */ + if (state->started) + write_reg(state, RSTV0910_P2_DMDISTATE + state->regoff, 0x5C); + + init_search_param(state, p); + + if (p->symbol_rate <= 1000000) { /* SR <=1Msps */ + state->demod_timeout = 3000; + state->fec_timeout = 2000; + } else if (p->symbol_rate <= 2000000) { /* 1Msps < SR <=2Msps */ + state->demod_timeout = 2500; + state->fec_timeout = 1300; + } else if (p->symbol_rate <= 5000000) { /* 2Msps< SR <=5Msps */ + state->demod_timeout = 1000; + state->fec_timeout = 650; + } else if (p->symbol_rate <= 10000000) { /* 5Msps< SR <=10Msps */ + state->demod_timeout = 700; + state->fec_timeout = 350; + } else if (p->symbol_rate < 20000000) { /* 10Msps< SR <=20Msps */ + state->demod_timeout = 400; + state->fec_timeout = 200; + } else { /* SR >=20Msps */ + state->demod_timeout = 300; + state->fec_timeout = 200; + } + + /* Set the Init Symbol rate */ + symb = muldiv32(p->symbol_rate, 65536, state->base->mclk); + write_reg(state, RSTV0910_P2_SFRINIT1 + state->regoff, + ((symb >> 8) & 0x7F)); + write_reg(state, RSTV0910_P2_SFRINIT0 + state->regoff, (symb & 0xFF)); + + state->demod_bits |= 0x80; + write_reg(state, RSTV0910_P2_DEMOD + state->regoff, state->demod_bits); + + /* FE_STV0910_SetSearchStandard */ + read_reg(state, RSTV0910_P2_DMDCFGMD + state->regoff, ®_dmdcfgmd); + write_reg(state, RSTV0910_P2_DMDCFGMD + state->regoff, + reg_dmdcfgmd |= 0xC0); + + write_shared_reg(state, + RSTV0910_TSTTSRS, state->nr ? 0x02 : 0x01, 0x00); + + /* Disable DSS */ + write_reg(state, RSTV0910_P2_FECM + state->regoff, 0x00); + write_reg(state, RSTV0910_P2_PRVIT + state->regoff, 0x2F); + + enable_puncture_rate(state, FEC_NONE); + + /* 8PSK 3/5, 8PSK 2/3 Poff tracking optimization WA */ + write_reg(state, RSTV0910_P2_ACLC2S2Q + state->regoff, 0x0B); + write_reg(state, RSTV0910_P2_ACLC2S28 + state->regoff, 0x0A); + write_reg(state, RSTV0910_P2_BCLC2S2Q + state->regoff, 0x84); + write_reg(state, RSTV0910_P2_BCLC2S28 + state->regoff, 0x84); + write_reg(state, RSTV0910_P2_CARHDR + state->regoff, 0x1C); + write_reg(state, RSTV0910_P2_CARFREQ + state->regoff, 0x79); + + write_reg(state, RSTV0910_P2_ACLC2S216A + state->regoff, 0x29); + write_reg(state, RSTV0910_P2_ACLC2S232A + state->regoff, 0x09); + write_reg(state, RSTV0910_P2_BCLC2S216A + state->regoff, 0x84); + write_reg(state, RSTV0910_P2_BCLC2S232A + state->regoff, 0x84); + + /* + * Reset CAR3, bug DVBS2->DVBS1 lock + * Note: The bit is only pulsed -> no lock on shared register needed + */ + write_reg(state, RSTV0910_TSTRES0, state->nr ? 0x04 : 0x08); + write_reg(state, RSTV0910_TSTRES0, 0); + + set_vth_default(state); + /* Reset demod */ + write_reg(state, RSTV0910_P2_DMDISTATE + state->regoff, 0x1F); + + write_reg(state, RSTV0910_P2_CARCFG + state->regoff, 0x46); + + if (p->symbol_rate <= 5000000) + freq = (state->search_range / 2000) + 80; + else + freq = (state->search_range / 2000) + 1600; + freq = (freq << 16) / (state->base->mclk / 1000); + + write_reg(state, RSTV0910_P2_CFRUP1 + state->regoff, + (freq >> 8) & 0xff); + write_reg(state, RSTV0910_P2_CFRUP0 + state->regoff, (freq & 0xff)); + /* CFR Low Setting */ + freq = -freq; + write_reg(state, RSTV0910_P2_CFRLOW1 + state->regoff, + (freq >> 8) & 0xff); + write_reg(state, RSTV0910_P2_CFRLOW0 + state->regoff, (freq & 0xff)); + + /* init the demod frequency offset to 0 */ + write_reg(state, RSTV0910_P2_CFRINIT1 + state->regoff, 0); + write_reg(state, RSTV0910_P2_CFRINIT0 + state->regoff, 0); + + write_reg(state, RSTV0910_P2_DMDISTATE + state->regoff, 0x1F); + /* Trigger acq */ + write_reg(state, RSTV0910_P2_DMDISTATE + state->regoff, 0x15); + + state->demod_lock_time += TUNING_DELAY; + state->started = 1; + + return 0; +} + +static int init_diseqc(struct stv *state) +{ + u16 offs = state->nr ? 0x40 : 0; /* Address offset */ + u8 freq = ((state->base->mclk + 11000 * 32) / (22000 * 32)); + + /* Disable receiver */ + write_reg(state, RSTV0910_P1_DISRXCFG + offs, 0x00); + write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0xBA); /* Reset = 1 */ + write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3A); /* Reset = 0 */ + write_reg(state, RSTV0910_P1_DISTXF22 + offs, freq); + return 0; +} + +static int probe(struct stv *state) +{ + u8 id; + + state->receive_mode = RCVMODE_NONE; + state->started = 0; + + if (read_reg(state, RSTV0910_MID, &id) < 0) + return -ENODEV; + + if (id != 0x51) + return -EINVAL; + + /* Configure the I2C repeater to off */ + write_reg(state, RSTV0910_P1_I2CRPT, 0x24); + /* Configure the I2C repeater to off */ + write_reg(state, RSTV0910_P2_I2CRPT, 0x24); + /* Set the I2C to oversampling ratio */ + write_reg(state, RSTV0910_I2CCFG, 0x88); /* state->i2ccfg */ + + write_reg(state, RSTV0910_OUTCFG, 0x00); /* OUTCFG */ + write_reg(state, RSTV0910_PADCFG, 0x05); /* RFAGC Pads Dev = 05 */ + write_reg(state, RSTV0910_SYNTCTRL, 0x02); /* SYNTCTRL */ + write_reg(state, RSTV0910_TSGENERAL, state->tsgeneral); /* TSGENERAL */ + write_reg(state, RSTV0910_CFGEXT, 0x02); /* CFGEXT */ + + if (state->single) + write_reg(state, RSTV0910_GENCFG, 0x14); /* GENCFG */ + else + write_reg(state, RSTV0910_GENCFG, 0x15); /* GENCFG */ + + write_reg(state, RSTV0910_P1_TNRCFG2, 0x02); /* IQSWAP = 0 */ + write_reg(state, RSTV0910_P2_TNRCFG2, 0x82); /* IQSWAP = 1 */ + + write_reg(state, RSTV0910_P1_CAR3CFG, 0x02); + write_reg(state, RSTV0910_P2_CAR3CFG, 0x02); + write_reg(state, RSTV0910_P1_DMDCFG4, 0x04); + write_reg(state, RSTV0910_P2_DMDCFG4, 0x04); + + write_reg(state, RSTV0910_TSTRES0, 0x80); /* LDPC Reset */ + write_reg(state, RSTV0910_TSTRES0, 0x00); + + write_reg(state, RSTV0910_P1_TSPIDFLT1, 0x00); + write_reg(state, RSTV0910_P2_TSPIDFLT1, 0x00); + + write_reg(state, RSTV0910_P1_TMGCFG2, 0x80); + write_reg(state, RSTV0910_P2_TMGCFG2, 0x80); + + set_mclock(state, 135000000); + + /* TS output */ + write_reg(state, RSTV0910_P1_TSCFGH, state->tscfgh | 0x01); + write_reg(state, RSTV0910_P1_TSCFGH, state->tscfgh); + write_reg(state, RSTV0910_P1_TSCFGM, 0xC0); /* Manual speed */ + write_reg(state, RSTV0910_P1_TSCFGL, 0x20); + + write_reg(state, RSTV0910_P1_TSSPEED, state->tsspeed); + + write_reg(state, RSTV0910_P2_TSCFGH, state->tscfgh | 0x01); + write_reg(state, RSTV0910_P2_TSCFGH, state->tscfgh); + write_reg(state, RSTV0910_P2_TSCFGM, 0xC0); /* Manual speed */ + write_reg(state, RSTV0910_P2_TSCFGL, 0x20); + + write_reg(state, RSTV0910_P2_TSSPEED, state->tsspeed); + + /* Reset stream merger */ + write_reg(state, RSTV0910_P1_TSCFGH, state->tscfgh | 0x01); + write_reg(state, RSTV0910_P2_TSCFGH, state->tscfgh | 0x01); + write_reg(state, RSTV0910_P1_TSCFGH, state->tscfgh); + write_reg(state, RSTV0910_P2_TSCFGH, state->tscfgh); + + write_reg(state, RSTV0910_P1_I2CRPT, state->i2crpt); + write_reg(state, RSTV0910_P2_I2CRPT, state->i2crpt); + + write_reg(state, RSTV0910_P1_TSINSDELM, 0x17); + write_reg(state, RSTV0910_P1_TSINSDELL, 0xff); + + write_reg(state, RSTV0910_P2_TSINSDELM, 0x17); + write_reg(state, RSTV0910_P2_TSINSDELL, 0xff); + + init_diseqc(state); + return 0; +} + +static int gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv *state = fe->demodulator_priv; + u8 i2crpt = state->i2crpt & ~0x86; + + /* + * mutex_lock note: Concurrent I2C gate bus accesses must be + * prevented (STV0910 = dual demod on a single IC with a single I2C + * gate/bus, and two tuners attached), similar to most (if not all) + * other I2C host interfaces/buses. + * + * enable=1 (open I2C gate) will grab the lock + * enable=0 (close I2C gate) releases the lock + */ + + if (enable) { + mutex_lock(&state->base->i2c_lock); + i2crpt |= 0x80; + } else { + i2crpt |= 0x02; + } + + if (write_reg(state, state->nr ? RSTV0910_P2_I2CRPT : + RSTV0910_P1_I2CRPT, i2crpt) < 0) { + /* don't hold the I2C bus lock on failure */ + if (!WARN_ON(!mutex_is_locked(&state->base->i2c_lock))) + mutex_unlock(&state->base->i2c_lock); + dev_err(&state->base->i2c->dev, + "%s() write_reg failure (enable=%d)\n", + __func__, enable); + return -EIO; + } + + state->i2crpt = i2crpt; + + if (!enable) + if (!WARN_ON(!mutex_is_locked(&state->base->i2c_lock))) + mutex_unlock(&state->base->i2c_lock); + return 0; +} + +static void release(struct dvb_frontend *fe) +{ + struct stv *state = fe->demodulator_priv; + + state->base->count--; + if (state->base->count == 0) { + list_del(&state->base->stvlist); + kfree(state->base); + } + kfree(state); +} + +static int set_parameters(struct dvb_frontend *fe) +{ + int stat = 0; + struct stv *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + stop(state); + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + state->symbol_rate = p->symbol_rate; + stat = start(state, p); + return stat; +} + +static int manage_matype_info(struct stv *state) +{ + if (!state->started) + return -EINVAL; + if (state->receive_mode == RCVMODE_DVBS2) { + u8 bbheader[2]; + + read_regs(state, RSTV0910_P2_MATSTR1 + state->regoff, + bbheader, 2); + state->feroll_off = + (enum fe_stv0910_roll_off)(bbheader[0] & 0x03); + state->is_vcm = (bbheader[0] & 0x10) == 0; + state->is_standard_broadcast = (bbheader[0] & 0xFC) == 0xF0; + } else if (state->receive_mode == RCVMODE_DVBS) { + state->is_vcm = 0; + state->is_standard_broadcast = 1; + state->feroll_off = FE_SAT_35; + } + return 0; +} + +static int read_snr(struct dvb_frontend *fe) +{ + struct stv *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 snrval; + + if (!get_signal_to_noise(state, &snrval)) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = 100 * snrval; /* fix scale */ + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +} + +static int read_ber(struct dvb_frontend *fe) +{ + struct stv *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 n, d; + + get_bit_error_rate(state, &n, &d); + + p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_error.stat[0].uvalue = n; + p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + p->pre_bit_count.stat[0].uvalue = d; + + return 0; +} + +static void read_signal_strength(struct dvb_frontend *fe) +{ + struct stv *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &state->fe.dtv_property_cache; + u8 reg[2]; + u16 agc; + s32 padc, power = 0; + int i; + + read_regs(state, RSTV0910_P2_AGCIQIN1 + state->regoff, reg, 2); + + agc = (((u32)reg[0]) << 8) | reg[1]; + + for (i = 0; i < 5; i += 1) { + read_regs(state, RSTV0910_P2_POWERI + state->regoff, reg, 2); + power += (u32)reg[0] * (u32)reg[0] + + (u32)reg[1] * (u32)reg[1]; + usleep_range(3000, 4000); + } + power /= 5; + + padc = table_lookup(padc_lookup, ARRAY_SIZE(padc_lookup), power) + 352; + + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].svalue = (padc - agc); +} + +static int read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct stv *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u8 dmd_state = 0; + u8 dstatus = 0; + enum receive_mode cur_receive_mode = RCVMODE_NONE; + u32 feclock = 0; + + *status = 0; + + read_reg(state, RSTV0910_P2_DMDSTATE + state->regoff, &dmd_state); + + if (dmd_state & 0x40) { + read_reg(state, RSTV0910_P2_DSTATUS + state->regoff, &dstatus); + if (dstatus & 0x08) + cur_receive_mode = (dmd_state & 0x20) ? + RCVMODE_DVBS : RCVMODE_DVBS2; + } + if (cur_receive_mode == RCVMODE_NONE) { + set_vth(state); + + /* reset signal statistics */ + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; + } + + *status |= (FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC); + + if (state->receive_mode == RCVMODE_NONE) { + state->receive_mode = cur_receive_mode; + state->demod_lock_time = jiffies; + state->first_time_lock = 1; + + get_signal_parameters(state); + tracking_optimization(state); + + write_reg(state, RSTV0910_P2_TSCFGH + state->regoff, + state->tscfgh); + usleep_range(3000, 4000); + write_reg(state, RSTV0910_P2_TSCFGH + state->regoff, + state->tscfgh | 0x01); + write_reg(state, RSTV0910_P2_TSCFGH + state->regoff, + state->tscfgh); + } + if (dmd_state & 0x40) { + if (state->receive_mode == RCVMODE_DVBS2) { + u8 pdelstatus; + + read_reg(state, + RSTV0910_P2_PDELSTATUS1 + state->regoff, + &pdelstatus); + feclock = (pdelstatus & 0x02) != 0; + } else { + u8 vstatus; + + read_reg(state, + RSTV0910_P2_VSTATUSVIT + state->regoff, + &vstatus); + feclock = (vstatus & 0x08) != 0; + } + } + + if (feclock) { + *status |= FE_HAS_LOCK; + + if (state->first_time_lock) { + u8 tmp; + + state->first_time_lock = 0; + + manage_matype_info(state); + + if (state->receive_mode == RCVMODE_DVBS2) { + /* + * FSTV0910_P2_MANUALSX_ROLLOFF, + * FSTV0910_P2_MANUALS2_ROLLOFF = 0 + */ + state->demod_bits &= ~0x84; + write_reg(state, + RSTV0910_P2_DEMOD + state->regoff, + state->demod_bits); + read_reg(state, + RSTV0910_P2_PDELCTRL2 + state->regoff, + &tmp); + /* reset DVBS2 packet delinator error counter */ + tmp |= 0x40; + write_reg(state, + RSTV0910_P2_PDELCTRL2 + state->regoff, + tmp); + /* reset DVBS2 packet delinator error counter */ + tmp &= ~0x40; + write_reg(state, + RSTV0910_P2_PDELCTRL2 + state->regoff, + tmp); + + state->berscale = 2; + state->last_bernumerator = 0; + state->last_berdenominator = 1; + /* force to PRE BCH Rate */ + write_reg(state, + RSTV0910_P2_ERRCTRL1 + state->regoff, + BER_SRC_S2 | state->berscale); + } else { + state->berscale = 2; + state->last_bernumerator = 0; + state->last_berdenominator = 1; + /* force to PRE RS Rate */ + write_reg(state, + RSTV0910_P2_ERRCTRL1 + state->regoff, + BER_SRC_S | state->berscale); + } + /* Reset the Total packet counter */ + write_reg(state, + RSTV0910_P2_FBERCPT4 + state->regoff, 0x00); + /* + * Reset the packet Error counter2 (and Set it to + * infinite error count mode) + */ + write_reg(state, + RSTV0910_P2_ERRCTRL2 + state->regoff, 0xc1); + + set_vth_default(state); + if (state->receive_mode == RCVMODE_DVBS) + enable_puncture_rate(state, + state->puncture_rate); + } + + /* Use highest signaled ModCod for quality */ + if (state->is_vcm) { + u8 tmp; + enum fe_stv0910_mod_cod mod_cod; + + read_reg(state, RSTV0910_P2_DMDMODCOD + state->regoff, + &tmp); + mod_cod = (enum fe_stv0910_mod_cod)((tmp & 0x7c) >> 2); + + if (mod_cod > state->mod_cod) + state->mod_cod = mod_cod; + } + } + + /* read signal statistics */ + + /* read signal strength */ + read_signal_strength(fe); + + /* read carrier/noise on FE_HAS_CARRIER */ + if (*status & FE_HAS_CARRIER) + read_snr(fe); + else + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* read ber */ + if (*status & FE_HAS_VITERBI) { + read_ber(fe); + } else { + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return 0; +} + +static int get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct stv *state = fe->demodulator_priv; + u8 tmp; + u32 symbolrate; + + if (state->receive_mode == RCVMODE_DVBS2) { + u32 mc; + const enum fe_modulation modcod2mod[0x20] = { + QPSK, QPSK, QPSK, QPSK, + QPSK, QPSK, QPSK, QPSK, + QPSK, QPSK, QPSK, QPSK, + PSK_8, PSK_8, PSK_8, PSK_8, + PSK_8, PSK_8, APSK_16, APSK_16, + APSK_16, APSK_16, APSK_16, APSK_16, + APSK_32, APSK_32, APSK_32, APSK_32, + APSK_32, + }; + const enum fe_code_rate modcod2fec[0x20] = { + FEC_NONE, FEC_NONE, FEC_NONE, FEC_2_5, + FEC_1_2, FEC_3_5, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_8_9, FEC_9_10, + FEC_3_5, FEC_2_3, FEC_3_4, FEC_5_6, + FEC_8_9, FEC_9_10, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_8_9, FEC_9_10, + FEC_3_4, FEC_4_5, FEC_5_6, FEC_8_9, + FEC_9_10 + }; + read_reg(state, RSTV0910_P2_DMDMODCOD + state->regoff, &tmp); + mc = ((tmp & 0x7c) >> 2); + p->pilot = (tmp & 0x01) ? PILOT_ON : PILOT_OFF; + p->modulation = modcod2mod[mc]; + p->fec_inner = modcod2fec[mc]; + } else if (state->receive_mode == RCVMODE_DVBS) { + read_reg(state, RSTV0910_P2_VITCURPUN + state->regoff, &tmp); + switch (tmp & 0x1F) { + case 0x0d: + p->fec_inner = FEC_1_2; + break; + case 0x12: + p->fec_inner = FEC_2_3; + break; + case 0x15: + p->fec_inner = FEC_3_4; + break; + case 0x18: + p->fec_inner = FEC_5_6; + break; + case 0x1a: + p->fec_inner = FEC_7_8; + break; + default: + p->fec_inner = FEC_NONE; + break; + } + p->rolloff = ROLLOFF_35; + } + + if (state->receive_mode != RCVMODE_NONE) { + get_cur_symbol_rate(state, &symbolrate); + p->symbol_rate = symbolrate; + } + return 0; +} + +static int tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, + unsigned int *delay, enum fe_status *status) +{ + struct stv *state = fe->demodulator_priv; + int r; + + if (re_tune) { + r = set_parameters(fe); + if (r) + return r; + state->tune_time = jiffies; + } + + r = read_status(fe, status); + if (r) + return r; + + if (*status & FE_HAS_LOCK) + return 0; + *delay = HZ; + + return 0; +} + +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct stv *state = fe->demodulator_priv; + u16 offs = state->nr ? 0x40 : 0; + + switch (tone) { + case SEC_TONE_ON: + return write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x38); + case SEC_TONE_OFF: + return write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3a); + default: + break; + } + return -EINVAL; +} + +static int wait_dis(struct stv *state, u8 flag, u8 val) +{ + int i; + u8 stat; + u16 offs = state->nr ? 0x40 : 0; + + for (i = 0; i < 10; i++) { + read_reg(state, RSTV0910_P1_DISTXSTATUS + offs, &stat); + if ((stat & flag) == val) + return 0; + usleep_range(10000, 11000); + } + return -ETIMEDOUT; +} + +static int send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct stv *state = fe->demodulator_priv; + int i; + + SET_FIELD(DISEQC_MODE, 2); + SET_FIELD(DIS_PRECHARGE, 1); + for (i = 0; i < cmd->msg_len; i++) { + wait_dis(state, 0x40, 0x00); + SET_REG(DISTXFIFO, cmd->msg[i]); + } + SET_FIELD(DIS_PRECHARGE, 0); + wait_dis(state, 0x20, 0x20); + return 0; +} + +static int send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst) +{ + struct stv *state = fe->demodulator_priv; + u8 value; + + if (burst == SEC_MINI_A) { + SET_FIELD(DISEQC_MODE, 3); + value = 0x00; + } else { + SET_FIELD(DISEQC_MODE, 2); + value = 0xFF; + } + + SET_FIELD(DIS_PRECHARGE, 1); + wait_dis(state, 0x40, 0x00); + SET_REG(DISTXFIFO, value); + SET_FIELD(DIS_PRECHARGE, 0); + wait_dis(state, 0x20, 0x20); + + return 0; +} + +static int sleep(struct dvb_frontend *fe) +{ + struct stv *state = fe->demodulator_priv; + + stop(state); + return 0; +} + +static const struct dvb_frontend_ops stv0910_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, + .info = { + .name = "ST STV0910", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .symbol_rate_min = 100000, + .symbol_rate_max = 70000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM + }, + .sleep = sleep, + .release = release, + .i2c_gate_ctrl = gate_ctrl, + .set_frontend = set_parameters, + .get_frontend_algo = get_algo, + .get_frontend = get_frontend, + .tune = tune, + .read_status = read_status, + .set_tone = set_tone, + + .diseqc_send_master_cmd = send_master_cmd, + .diseqc_send_burst = send_burst, +}; + +static struct stv_base *match_base(struct i2c_adapter *i2c, u8 adr) +{ + struct stv_base *p; + + list_for_each_entry(p, &stvlist, stvlist) + if (p->i2c == i2c && p->adr == adr) + return p; + return NULL; +} + +static void stv0910_init_stats(struct stv *state) +{ + struct dtv_frontend_properties *p = &state->fe.dtv_property_cache; + + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_error.len = 1; + p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->pre_bit_count.len = 1; + p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; +} + +struct dvb_frontend *stv0910_attach(struct i2c_adapter *i2c, + struct stv0910_cfg *cfg, + int nr) +{ + struct stv *state; + struct stv_base *base; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + state->tscfgh = 0x20 | (cfg->parallel ? 0 : 0x40); + state->tsgeneral = (cfg->parallel == 2) ? 0x02 : 0x00; + state->i2crpt = 0x0A | ((cfg->rptlvl & 0x07) << 4); + /* use safe tsspeed value if unspecified through stv0910_cfg */ + state->tsspeed = (cfg->tsspeed ? cfg->tsspeed : 0x28); + state->nr = nr; + state->regoff = state->nr ? 0 : 0x200; + state->search_range = 16000000; + state->demod_bits = 0x10; /* Inversion : Auto with reset to 0 */ + state->receive_mode = RCVMODE_NONE; + state->cur_scrambling_code = (~0U); + state->single = cfg->single ? 1 : 0; + + base = match_base(i2c, cfg->adr); + if (base) { + base->count++; + state->base = base; + } else { + base = kzalloc(sizeof(*base), GFP_KERNEL); + if (!base) + goto fail; + base->i2c = i2c; + base->adr = cfg->adr; + base->count = 1; + base->extclk = cfg->clk ? cfg->clk : 30000000; + + mutex_init(&base->i2c_lock); + mutex_init(&base->reg_lock); + state->base = base; + if (probe(state) < 0) { + dev_info(&i2c->dev, "No demod found at adr %02X on %s\n", + cfg->adr, dev_name(&i2c->dev)); + kfree(base); + goto fail; + } + list_add(&base->stvlist, &stvlist); + } + state->fe.ops = stv0910_ops; + state->fe.demodulator_priv = state; + state->nr = nr; + + dev_info(&i2c->dev, "%s demod found at adr %02X on %s\n", + state->fe.ops.info.name, cfg->adr, dev_name(&i2c->dev)); + + stv0910_init_stats(state); + + return &state->fe; + +fail: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(stv0910_attach); + +MODULE_DESCRIPTION("ST STV0910 multistandard frontend driver"); +MODULE_AUTHOR("Ralph and Marcus Metzler, Manfred Voelkel"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/stv0910.h b/drivers/media/dvb-frontends/stv0910.h new file mode 100644 index 0000000000..0b6f02ad79 --- /dev/null +++ b/drivers/media/dvb-frontends/stv0910.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the ST STV0910 DVB-S/S2 demodulator. + * + * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de> + * Marcus Metzler <mocm@metzlerbros.de> + * developed for Digital Devices GmbH + */ + +#ifndef _STV0910_H_ +#define _STV0910_H_ + +#include <linux/types.h> +#include <linux/i2c.h> + +struct stv0910_cfg { + u32 clk; + u8 adr; + u8 parallel; + u8 rptlvl; + u8 single; + u8 tsspeed; +}; + +#if IS_REACHABLE(CONFIG_DVB_STV0910) + +struct dvb_frontend *stv0910_attach(struct i2c_adapter *i2c, + struct stv0910_cfg *cfg, int nr); + +#else + +static inline struct dvb_frontend *stv0910_attach(struct i2c_adapter *i2c, + struct stv0910_cfg *cfg, + int nr) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_STV0910 */ + +#endif /* _STV0910_H_ */ diff --git a/drivers/media/dvb-frontends/stv0910_regs.h b/drivers/media/dvb-frontends/stv0910_regs.h new file mode 100644 index 0000000000..448c89b8cd --- /dev/null +++ b/drivers/media/dvb-frontends/stv0910_regs.h @@ -0,0 +1,4761 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * @DVB-S/DVB-S2 STMicroelectronics STV0900 register definitions + * Author Manfred Voelkel, August 2013 + * (c) 2013 Digital Devices GmbH Germany. All rights reserved + * + * ======================================================================= + * Registers Declaration (Internal ST, All Applications ) + * ------------------------- + * Each register (RSTV0910__XXXXX) is defined by its address (2 bytes). + * Each field (FSTV0910__XXXXX) is defined as follow: + * [register address -- 2bytes][field offset -- 4 bits][unused -- 3 bits] + * [field sign -- 1 bit][field mask -- 1byte] + * ======================================================================= + */ + +/* MID */ +#define RSTV0910_MID 0xf100 +#define FSTV0910_MCHIP_IDENT 0xf10040f0 +#define FSTV0910_MRELEASE 0xf100000f + +/* DID */ +#define RSTV0910_DID 0xf101 +#define FSTV0910_DEVICE_ID 0xf10100ff + +/* DACR1 */ +#define RSTV0910_DACR1 0xf113 +#define FSTV0910_DAC_MODE 0xf11350e0 +#define FSTV0910_DAC_VALUE1 0xf113000f + +/* DACR2 */ +#define RSTV0910_DACR2 0xf114 +#define FSTV0910_DAC_VALUE0 0xf11400ff + +/* PADCFG */ +#define RSTV0910_PADCFG 0xf11a +#define FSTV0910_AGCRF2_OPD 0xf11a3008 +#define FSTV0910_AGCRF2_XOR 0xf11a2004 +#define FSTV0910_AGCRF1_OPD 0xf11a1002 +#define FSTV0910_AGCRF1_XOR 0xf11a0001 + +/* OUTCFG2 */ +#define RSTV0910_OUTCFG2 0xf11b +#define FSTV0910_TS2_ERROR_XOR 0xf11b7080 +#define FSTV0910_TS2_DPN_XOR 0xf11b6040 +#define FSTV0910_TS2_STROUT_XOR 0xf11b5020 +#define FSTV0910_TS2_CLOCKOUT_XOR 0xf11b4010 +#define FSTV0910_TS1_ERROR_XOR 0xf11b3008 +#define FSTV0910_TS1_DPN_XOR 0xf11b2004 +#define FSTV0910_TS1_STROUT_XOR 0xf11b1002 +#define FSTV0910_TS1_CLOCKOUT_XOR 0xf11b0001 + +/* OUTCFG */ +#define RSTV0910_OUTCFG 0xf11c +#define FSTV0910_TS2_OUTSER_HZ 0xf11c5020 +#define FSTV0910_TS1_OUTSER_HZ 0xf11c4010 +#define FSTV0910_TS2_OUTPAR_HZ 0xf11c3008 +#define FSTV0910_TS1_OUTPAR_HZ 0xf11c2004 +#define FSTV0910_TS_SERDATA0 0xf11c1002 + +/* IRQSTATUS3 */ +#define RSTV0910_IRQSTATUS3 0xf120 +#define FSTV0910_SPLL_LOCK 0xf1205020 +#define FSTV0910_SSTREAM_LCK_1 0xf1204010 +#define FSTV0910_SSTREAM_LCK_2 0xf1203008 +#define FSTV0910_SDVBS1_PRF_2 0xf1201002 +#define FSTV0910_SDVBS1_PRF_1 0xf1200001 + +/* IRQSTATUS2 */ +#define RSTV0910_IRQSTATUS2 0xf121 +#define FSTV0910_SSPY_ENDSIM_1 0xf1217080 +#define FSTV0910_SSPY_ENDSIM_2 0xf1216040 +#define FSTV0910_SPKTDEL_ERROR_2 0xf1214010 +#define FSTV0910_SPKTDEL_LOCKB_2 0xf1213008 +#define FSTV0910_SPKTDEL_LOCK_2 0xf1212004 +#define FSTV0910_SPKTDEL_ERROR_1 0xf1211002 +#define FSTV0910_SPKTDEL_LOCKB_1 0xf1210001 + +/* IRQSTATUS1 */ +#define RSTV0910_IRQSTATUS1 0xf122 +#define FSTV0910_SPKTDEL_LOCK_1 0xf1227080 +#define FSTV0910_SFEC_LOCKB_2 0xf1226040 +#define FSTV0910_SFEC_LOCK_2 0xf1225020 +#define FSTV0910_SFEC_LOCKB_1 0xf1224010 +#define FSTV0910_SFEC_LOCK_1 0xf1223008 +#define FSTV0910_SDEMOD_LOCKB_2 0xf1222004 +#define FSTV0910_SDEMOD_LOCK_2 0xf1221002 +#define FSTV0910_SDEMOD_IRQ_2 0xf1220001 + +/* IRQSTATUS0 */ +#define RSTV0910_IRQSTATUS0 0xf123 +#define FSTV0910_SDEMOD_LOCKB_1 0xf1237080 +#define FSTV0910_SDEMOD_LOCK_1 0xf1236040 +#define FSTV0910_SDEMOD_IRQ_1 0xf1235020 +#define FSTV0910_SBCH_ERRFLAG 0xf1234010 +#define FSTV0910_SDISEQC2_IRQ 0xf1232004 +#define FSTV0910_SDISEQC1_IRQ 0xf1230001 + +/* IRQMASK3 */ +#define RSTV0910_IRQMASK3 0xf124 +#define FSTV0910_MPLL_LOCK 0xf1245020 +#define FSTV0910_MSTREAM_LCK_1 0xf1244010 +#define FSTV0910_MSTREAM_LCK_2 0xf1243008 +#define FSTV0910_MDVBS1_PRF_2 0xf1241002 +#define FSTV0910_MDVBS1_PRF_1 0xf1240001 + +/* IRQMASK2 */ +#define RSTV0910_IRQMASK2 0xf125 +#define FSTV0910_MSPY_ENDSIM_1 0xf1257080 +#define FSTV0910_MSPY_ENDSIM_2 0xf1256040 +#define FSTV0910_MPKTDEL_ERROR_2 0xf1254010 +#define FSTV0910_MPKTDEL_LOCKB_2 0xf1253008 +#define FSTV0910_MPKTDEL_LOCK_2 0xf1252004 +#define FSTV0910_MPKTDEL_ERROR_1 0xf1251002 +#define FSTV0910_MPKTDEL_LOCKB_1 0xf1250001 + +/* IRQMASK1 */ +#define RSTV0910_IRQMASK1 0xf126 +#define FSTV0910_MPKTDEL_LOCK_1 0xf1267080 +#define FSTV0910_MFEC_LOCKB_2 0xf1266040 +#define FSTV0910_MFEC_LOCK_2 0xf1265020 +#define FSTV0910_MFEC_LOCKB_1 0xf1264010 +#define FSTV0910_MFEC_LOCK_1 0xf1263008 +#define FSTV0910_MDEMOD_LOCKB_2 0xf1262004 +#define FSTV0910_MDEMOD_LOCK_2 0xf1261002 +#define FSTV0910_MDEMOD_IRQ_2 0xf1260001 + +/* IRQMASK0 */ +#define RSTV0910_IRQMASK0 0xf127 +#define FSTV0910_MDEMOD_LOCKB_1 0xf1277080 +#define FSTV0910_MDEMOD_LOCK_1 0xf1276040 +#define FSTV0910_MDEMOD_IRQ_1 0xf1275020 +#define FSTV0910_MBCH_ERRFLAG 0xf1274010 +#define FSTV0910_MDISEQC2_IRQ 0xf1272004 +#define FSTV0910_MDISEQC1_IRQ 0xf1270001 + +/* I2CCFG */ +#define RSTV0910_I2CCFG 0xf129 +#define FSTV0910_I2C_FASTMODE 0xf1293008 +#define FSTV0910_I2CADDR_INC 0xf1290003 + +/* P1_I2CRPT */ +#define RSTV0910_P1_I2CRPT 0xf12a +#define FSTV0910_P1_I2CT_ON 0xf12a7080 +#define FSTV0910_P1_ENARPT_LEVEL 0xf12a4070 +#define FSTV0910_P1_SCLT_DELAY 0xf12a3008 +#define FSTV0910_P1_STOP_ENABLE 0xf12a2004 +#define FSTV0910_P1_STOP_SDAT2SDA 0xf12a1002 + +/* P2_I2CRPT */ +#define RSTV0910_P2_I2CRPT 0xf12b +#define FSTV0910_P2_I2CT_ON 0xf12b7080 +#define FSTV0910_P2_ENARPT_LEVEL 0xf12b4070 +#define FSTV0910_P2_SCLT_DELAY 0xf12b3008 +#define FSTV0910_P2_STOP_ENABLE 0xf12b2004 +#define FSTV0910_P2_STOP_SDAT2SDA 0xf12b1002 + +/* GPIO0CFG */ +#define RSTV0910_GPIO0CFG 0xf140 +#define FSTV0910_GPIO0_OPD 0xf1407080 +#define FSTV0910_GPIO0_CONFIG 0xf140107e +#define FSTV0910_GPIO0_XOR 0xf1400001 + +/* GPIO1CFG */ +#define RSTV0910_GPIO1CFG 0xf141 +#define FSTV0910_GPIO1_OPD 0xf1417080 +#define FSTV0910_GPIO1_CONFIG 0xf141107e +#define FSTV0910_GPIO1_XOR 0xf1410001 + +/* GPIO2CFG */ +#define RSTV0910_GPIO2CFG 0xf142 +#define FSTV0910_GPIO2_OPD 0xf1427080 +#define FSTV0910_GPIO2_CONFIG 0xf142107e +#define FSTV0910_GPIO2_XOR 0xf1420001 + +/* GPIO3CFG */ +#define RSTV0910_GPIO3CFG 0xf143 +#define FSTV0910_GPIO3_OPD 0xf1437080 +#define FSTV0910_GPIO3_CONFIG 0xf143107e +#define FSTV0910_GPIO3_XOR 0xf1430001 + +/* GPIO4CFG */ +#define RSTV0910_GPIO4CFG 0xf144 +#define FSTV0910_GPIO4_OPD 0xf1447080 +#define FSTV0910_GPIO4_CONFIG 0xf144107e +#define FSTV0910_GPIO4_XOR 0xf1440001 + +/* GPIO5CFG */ +#define RSTV0910_GPIO5CFG 0xf145 +#define FSTV0910_GPIO5_OPD 0xf1457080 +#define FSTV0910_GPIO5_CONFIG 0xf145107e +#define FSTV0910_GPIO5_XOR 0xf1450001 + +/* GPIO6CFG */ +#define RSTV0910_GPIO6CFG 0xf146 +#define FSTV0910_GPIO6_OPD 0xf1467080 +#define FSTV0910_GPIO6_CONFIG 0xf146107e +#define FSTV0910_GPIO6_XOR 0xf1460001 + +/* GPIO7CFG */ +#define RSTV0910_GPIO7CFG 0xf147 +#define FSTV0910_GPIO7_OPD 0xf1477080 +#define FSTV0910_GPIO7_CONFIG 0xf147107e +#define FSTV0910_GPIO7_XOR 0xf1470001 + +/* GPIO8CFG */ +#define RSTV0910_GPIO8CFG 0xf148 +#define FSTV0910_GPIO8_OPD 0xf1487080 +#define FSTV0910_GPIO8_CONFIG 0xf148107e +#define FSTV0910_GPIO8_XOR 0xf1480001 + +/* GPIO9CFG */ +#define RSTV0910_GPIO9CFG 0xf149 +#define FSTV0910_GPIO9_OPD 0xf1497080 +#define FSTV0910_GPIO9_CONFIG 0xf149107e +#define FSTV0910_GPIO9_XOR 0xf1490001 + +/* GPIO10CFG */ +#define RSTV0910_GPIO10CFG 0xf14a +#define FSTV0910_GPIO10_OPD 0xf14a7080 +#define FSTV0910_GPIO10_CONFIG 0xf14a107e +#define FSTV0910_GPIO10_XOR 0xf14a0001 + +/* GPIO11CFG */ +#define RSTV0910_GPIO11CFG 0xf14b +#define FSTV0910_GPIO11_OPD 0xf14b7080 +#define FSTV0910_GPIO11_CONFIG 0xf14b107e +#define FSTV0910_GPIO11_XOR 0xf14b0001 + +/* GPIO12CFG */ +#define RSTV0910_GPIO12CFG 0xf14c +#define FSTV0910_GPIO12_OPD 0xf14c7080 +#define FSTV0910_GPIO12_CONFIG 0xf14c107e +#define FSTV0910_GPIO12_XOR 0xf14c0001 + +/* GPIO13CFG */ +#define RSTV0910_GPIO13CFG 0xf14d +#define FSTV0910_GPIO13_OPD 0xf14d7080 +#define FSTV0910_GPIO13_CONFIG 0xf14d107e +#define FSTV0910_GPIO13_XOR 0xf14d0001 + +/* GPIO14CFG */ +#define RSTV0910_GPIO14CFG 0xf14e +#define FSTV0910_GPIO14_OPD 0xf14e7080 +#define FSTV0910_GPIO14_CONFIG 0xf14e107e +#define FSTV0910_GPIO14_XOR 0xf14e0001 + +/* GPIO15CFG */ +#define RSTV0910_GPIO15CFG 0xf14f +#define FSTV0910_GPIO15_OPD 0xf14f7080 +#define FSTV0910_GPIO15_CONFIG 0xf14f107e +#define FSTV0910_GPIO15_XOR 0xf14f0001 + +/* GPIO16CFG */ +#define RSTV0910_GPIO16CFG 0xf150 +#define FSTV0910_GPIO16_OPD 0xf1507080 +#define FSTV0910_GPIO16_CONFIG 0xf150107e +#define FSTV0910_GPIO16_XOR 0xf1500001 + +/* GPIO17CFG */ +#define RSTV0910_GPIO17CFG 0xf151 +#define FSTV0910_GPIO17_OPD 0xf1517080 +#define FSTV0910_GPIO17_CONFIG 0xf151107e +#define FSTV0910_GPIO17_XOR 0xf1510001 + +/* GPIO18CFG */ +#define RSTV0910_GPIO18CFG 0xf152 +#define FSTV0910_GPIO18_OPD 0xf1527080 +#define FSTV0910_GPIO18_CONFIG 0xf152107e +#define FSTV0910_GPIO18_XOR 0xf1520001 + +/* GPIO19CFG */ +#define RSTV0910_GPIO19CFG 0xf153 +#define FSTV0910_GPIO19_OPD 0xf1537080 +#define FSTV0910_GPIO19_CONFIG 0xf153107e +#define FSTV0910_GPIO19_XOR 0xf1530001 + +/* GPIO20CFG */ +#define RSTV0910_GPIO20CFG 0xf154 +#define FSTV0910_GPIO20_OPD 0xf1547080 +#define FSTV0910_GPIO20_CONFIG 0xf154107e +#define FSTV0910_GPIO20_XOR 0xf1540001 + +/* GPIO21CFG */ +#define RSTV0910_GPIO21CFG 0xf155 +#define FSTV0910_GPIO21_OPD 0xf1557080 +#define FSTV0910_GPIO21_CONFIG 0xf155107e +#define FSTV0910_GPIO21_XOR 0xf1550001 + +/* GPIO22CFG */ +#define RSTV0910_GPIO22CFG 0xf156 +#define FSTV0910_GPIO22_OPD 0xf1567080 +#define FSTV0910_GPIO22_CONFIG 0xf156107e +#define FSTV0910_GPIO22_XOR 0xf1560001 + +/* STRSTATUS1 */ +#define RSTV0910_STRSTATUS1 0xf16a +#define FSTV0910_STRSTATUS_SEL2 0xf16a40f0 +#define FSTV0910_STRSTATUS_SEL1 0xf16a000f + +/* STRSTATUS2 */ +#define RSTV0910_STRSTATUS2 0xf16b +#define FSTV0910_STRSTATUS_SEL4 0xf16b40f0 +#define FSTV0910_STRSTATUS_SEL3 0xf16b000f + +/* STRSTATUS3 */ +#define RSTV0910_STRSTATUS3 0xf16c +#define FSTV0910_STRSTATUS_SEL6 0xf16c40f0 +#define FSTV0910_STRSTATUS_SEL5 0xf16c000f + +/* FSKTFC2 */ +#define RSTV0910_FSKTFC2 0xf170 +#define FSTV0910_FSKT_KMOD 0xf17020fc +#define FSTV0910_FSKT_CAR2 0xf1700003 + +/* FSKTFC1 */ +#define RSTV0910_FSKTFC1 0xf171 +#define FSTV0910_FSKT_CAR1 0xf17100ff + +/* FSKTFC0 */ +#define RSTV0910_FSKTFC0 0xf172 +#define FSTV0910_FSKT_CAR0 0xf17200ff + +/* FSKTDELTAF1 */ +#define RSTV0910_FSKTDELTAF1 0xf173 +#define FSTV0910_FSKT_DELTAF1 0xf173000f + +/* FSKTDELTAF0 */ +#define RSTV0910_FSKTDELTAF0 0xf174 +#define FSTV0910_FSKT_DELTAF0 0xf17400ff + +/* FSKTCTRL */ +#define RSTV0910_FSKTCTRL 0xf175 +#define FSTV0910_FSKT_PINSEL 0xf1757080 +#define FSTV0910_FSKT_EN_SGN 0xf1756040 +#define FSTV0910_FSKT_MOD_SGN 0xf1755020 +#define FSTV0910_FSKT_MOD_EN 0xf175201c +#define FSTV0910_FSKT_DACMODE 0xf1750003 + +/* FSKRFC2 */ +#define RSTV0910_FSKRFC2 0xf176 +#define FSTV0910_FSKR_DETSGN 0xf1766040 +#define FSTV0910_FSKR_OUTSGN 0xf1765020 +#define FSTV0910_FSKR_KAGC 0xf176201c +#define FSTV0910_FSKR_CAR2 0xf1760003 + +/* FSKRFC1 */ +#define RSTV0910_FSKRFC1 0xf177 +#define FSTV0910_FSKR_CAR1 0xf17700ff + +/* FSKRFC0 */ +#define RSTV0910_FSKRFC0 0xf178 +#define FSTV0910_FSKR_CAR0 0xf17800ff + +/* FSKRK1 */ +#define RSTV0910_FSKRK1 0xf179 +#define FSTV0910_FSKR_K1_EXP 0xf17950e0 +#define FSTV0910_FSKR_K1_MANT 0xf179001f + +/* FSKRK2 */ +#define RSTV0910_FSKRK2 0xf17a +#define FSTV0910_FSKR_K2_EXP 0xf17a50e0 +#define FSTV0910_FSKR_K2_MANT 0xf17a001f + +/* FSKRAGCR */ +#define RSTV0910_FSKRAGCR 0xf17b +#define FSTV0910_FSKR_OUTCTL 0xf17b60c0 +#define FSTV0910_FSKR_AGC_REF 0xf17b003f + +/* FSKRAGC */ +#define RSTV0910_FSKRAGC 0xf17c +#define FSTV0910_FSKR_AGC_ACCU 0xf17c00ff + +/* FSKRALPHA */ +#define RSTV0910_FSKRALPHA 0xf17d +#define FSTV0910_FSKR_ALPHA_EXP 0xf17d201c +#define FSTV0910_FSKR_ALPHA_M 0xf17d0003 + +/* FSKRPLTH1 */ +#define RSTV0910_FSKRPLTH1 0xf17e +#define FSTV0910_FSKR_BETA 0xf17e40f0 +#define FSTV0910_FSKR_PLL_TRESH1 0xf17e000f + +/* FSKRPLTH0 */ +#define RSTV0910_FSKRPLTH0 0xf17f +#define FSTV0910_FSKR_PLL_TRESH0 0xf17f00ff + +/* FSKRDF1 */ +#define RSTV0910_FSKRDF1 0xf180 +#define FSTV0910_FSKR_OUT 0xf1807080 +#define FSTV0910_FSKR_STATE 0xf1805060 +#define FSTV0910_FSKR_DELTAF1 0xf180001f + +/* FSKRDF0 */ +#define RSTV0910_FSKRDF0 0xf181 +#define FSTV0910_FSKR_DELTAF0 0xf18100ff + +/* FSKRSTEPP */ +#define RSTV0910_FSKRSTEPP 0xf182 +#define FSTV0910_FSKR_STEP_PLUS 0xf18200ff + +/* FSKRSTEPM */ +#define RSTV0910_FSKRSTEPM 0xf183 +#define FSTV0910_FSKR_STEP_MINUS 0xf18300ff + +/* FSKRDET1 */ +#define RSTV0910_FSKRDET1 0xf184 +#define FSTV0910_FSKR_DETECT 0xf1847080 +#define FSTV0910_FSKR_CARDET_ACCU1 0xf184000f + +/* FSKRDET0 */ +#define RSTV0910_FSKRDET0 0xf185 +#define FSTV0910_FSKR_CARDET_ACCU0 0xf18500ff + +/* FSKRDTH1 */ +#define RSTV0910_FSKRDTH1 0xf186 +#define FSTV0910_FSKR_CARLOSS_THRESH1 0xf18640f0 +#define FSTV0910_FSKR_CARDET_THRESH1 0xf186000f + +/* FSKRDTH0 */ +#define RSTV0910_FSKRDTH0 0xf187 +#define FSTV0910_FSKR_CARDET_THRESH0 0xf18700ff + +/* FSKRLOSS */ +#define RSTV0910_FSKRLOSS 0xf188 +#define FSTV0910_FSKR_CARLOSS_THRESH0 0xf18800ff + +/* NCOARSE */ +#define RSTV0910_NCOARSE 0xf1b3 +#define FSTV0910_CP 0xf1b330f8 +#define FSTV0910_IDF 0xf1b30007 + +/* NCOARSE1 */ +#define RSTV0910_NCOARSE1 0xf1b4 +#define FSTV0910_N_DIV 0xf1b400ff + +/* NCOARSE2 */ +#define RSTV0910_NCOARSE2 0xf1b5 +#define FSTV0910_ODF 0xf1b5003f + +/* SYNTCTRL */ +#define RSTV0910_SYNTCTRL 0xf1b6 +#define FSTV0910_STANDBY 0xf1b67080 +#define FSTV0910_BYPASSPLLCORE 0xf1b66040 +#define FSTV0910_STOP_PLL 0xf1b63008 +#define FSTV0910_OSCI_E 0xf1b61002 + +/* FILTCTRL */ +#define RSTV0910_FILTCTRL 0xf1b7 +#define FSTV0910_INV_CLKFSK 0xf1b71002 +#define FSTV0910_BYPASS_APPLI 0xf1b70001 + +/* PLLSTAT */ +#define RSTV0910_PLLSTAT 0xf1b8 +#define FSTV0910_PLLLOCK 0xf1b80001 + +/* STOPCLK1 */ +#define RSTV0910_STOPCLK1 0xf1c2 +#define FSTV0910_INV_CLKADCI2 0xf1c22004 +#define FSTV0910_INV_CLKADCI1 0xf1c20001 + +/* STOPCLK2 */ +#define RSTV0910_STOPCLK2 0xf1c3 +#define FSTV0910_STOP_DVBS2FEC2 0xf1c35020 +#define FSTV0910_STOP_DVBS2FEC 0xf1c34010 +#define FSTV0910_STOP_DVBS1FEC2 0xf1c33008 +#define FSTV0910_STOP_DVBS1FEC 0xf1c32004 +#define FSTV0910_STOP_DEMOD2 0xf1c31002 +#define FSTV0910_STOP_DEMOD 0xf1c30001 + +/* PREGCTL */ +#define RSTV0910_PREGCTL 0xf1c8 +#define FSTV0910_REG3V3TO2V5_POFF 0xf1c87080 + +/* TSTTNR0 */ +#define RSTV0910_TSTTNR0 0xf1df +#define FSTV0910_FSK_PON 0xf1df2004 + +/* TSTTNR1 */ +#define RSTV0910_TSTTNR1 0xf1e0 +#define FSTV0910_ADC1_PON 0xf1e01002 + +/* TSTTNR2 */ +#define RSTV0910_TSTTNR2 0xf1e1 +#define FSTV0910_I2C_DISEQC_PON 0xf1e15020 +#define FSTV0910_DISEQC_CLKDIV 0xf1e1000f + +/* TSTTNR3 */ +#define RSTV0910_TSTTNR3 0xf1e2 +#define FSTV0910_ADC2_PON 0xf1e21002 + +/* P2_IQCONST */ +#define RSTV0910_P2_IQCONST 0xf200 +#define FSTV0910_P2_CONSTEL_SELECT 0xf2005060 +#define FSTV0910_P2_IQSYMB_SEL 0xf200001f + +/* P2_NOSCFG */ +#define RSTV0910_P2_NOSCFG 0xf201 +#define FSTV0910_P2_DUMMYPL_NOSDATA 0xf2015020 +#define FSTV0910_P2_NOSPLH_BETA 0xf2013018 +#define FSTV0910_P2_NOSDATA_BETA 0xf2010007 + +/* P2_ISYMB */ +#define RSTV0910_P2_ISYMB 0xf202 +#define FSTV0910_P2_I_SYMBOL 0xf20201ff + +/* P2_QSYMB */ +#define RSTV0910_P2_QSYMB 0xf203 +#define FSTV0910_P2_Q_SYMBOL 0xf20301ff + +/* P2_AGC1CFG */ +#define RSTV0910_P2_AGC1CFG 0xf204 +#define FSTV0910_P2_DC_FROZEN 0xf2047080 +#define FSTV0910_P2_DC_CORRECT 0xf2046040 +#define FSTV0910_P2_AMM_FROZEN 0xf2045020 +#define FSTV0910_P2_AMM_CORRECT 0xf2044010 +#define FSTV0910_P2_QUAD_FROZEN 0xf2043008 +#define FSTV0910_P2_QUAD_CORRECT 0xf2042004 + +/* P2_AGC1CN */ +#define RSTV0910_P2_AGC1CN 0xf206 +#define FSTV0910_P2_AGC1_LOCKED 0xf2067080 +#define FSTV0910_P2_AGC1_MINPOWER 0xf2064010 +#define FSTV0910_P2_AGCOUT_FAST 0xf2063008 +#define FSTV0910_P2_AGCIQ_BETA 0xf2060007 + +/* P2_AGC1REF */ +#define RSTV0910_P2_AGC1REF 0xf207 +#define FSTV0910_P2_AGCIQ_REF 0xf20700ff + +/* P2_IDCCOMP */ +#define RSTV0910_P2_IDCCOMP 0xf208 +#define FSTV0910_P2_IAVERAGE_ADJ 0xf20801ff + +/* P2_QDCCOMP */ +#define RSTV0910_P2_QDCCOMP 0xf209 +#define FSTV0910_P2_QAVERAGE_ADJ 0xf20901ff + +/* P2_POWERI */ +#define RSTV0910_P2_POWERI 0xf20a +#define FSTV0910_P2_POWER_I 0xf20a00ff + +/* P2_POWERQ */ +#define RSTV0910_P2_POWERQ 0xf20b +#define FSTV0910_P2_POWER_Q 0xf20b00ff + +/* P2_AGC1AMM */ +#define RSTV0910_P2_AGC1AMM 0xf20c +#define FSTV0910_P2_AMM_VALUE 0xf20c00ff + +/* P2_AGC1QUAD */ +#define RSTV0910_P2_AGC1QUAD 0xf20d +#define FSTV0910_P2_QUAD_VALUE 0xf20d01ff + +/* P2_AGCIQIN1 */ +#define RSTV0910_P2_AGCIQIN1 0xf20e +#define FSTV0910_P2_AGCIQ_VALUE1 0xf20e00ff + +/* P2_AGCIQIN0 */ +#define RSTV0910_P2_AGCIQIN0 0xf20f +#define FSTV0910_P2_AGCIQ_VALUE0 0xf20f00ff + +/* P2_DEMOD */ +#define RSTV0910_P2_DEMOD 0xf210 +#define FSTV0910_P2_MANUALS2_ROLLOFF 0xf2107080 +#define FSTV0910_P2_SPECINV_CONTROL 0xf2104030 +#define FSTV0910_P2_MANUALSX_ROLLOFF 0xf2102004 +#define FSTV0910_P2_ROLLOFF_CONTROL 0xf2100003 + +/* P2_DMDMODCOD */ +#define RSTV0910_P2_DMDMODCOD 0xf211 +#define FSTV0910_P2_MANUAL_MODCOD 0xf2117080 +#define FSTV0910_P2_DEMOD_MODCOD 0xf211207c +#define FSTV0910_P2_DEMOD_TYPE 0xf2110003 + +/* P2_DSTATUS */ +#define RSTV0910_P2_DSTATUS 0xf212 +#define FSTV0910_P2_CAR_LOCK 0xf2127080 +#define FSTV0910_P2_TMGLOCK_QUALITY 0xf2125060 +#define FSTV0910_P2_LOCK_DEFINITIF 0xf2123008 +#define FSTV0910_P2_OVADC_DETECT 0xf2120001 + +/* P2_DSTATUS2 */ +#define RSTV0910_P2_DSTATUS2 0xf213 +#define FSTV0910_P2_DEMOD_DELOCK 0xf2137080 +#define FSTV0910_P2_MODCODRQ_SYNCTAG 0xf2135020 +#define FSTV0910_P2_POLYPH_SATEVENT 0xf2134010 +#define FSTV0910_P2_AGC1_NOSIGNALACK 0xf2133008 +#define FSTV0910_P2_AGC2_OVERFLOW 0xf2132004 +#define FSTV0910_P2_CFR_OVERFLOW 0xf2131002 +#define FSTV0910_P2_GAMMA_OVERUNDER 0xf2130001 + +/* P2_DMDCFGMD */ +#define RSTV0910_P2_DMDCFGMD 0xf214 +#define FSTV0910_P2_DVBS2_ENABLE 0xf2147080 +#define FSTV0910_P2_DVBS1_ENABLE 0xf2146040 +#define FSTV0910_P2_SCAN_ENABLE 0xf2144010 +#define FSTV0910_P2_CFR_AUTOSCAN 0xf2143008 +#define FSTV0910_P2_TUN_RNG 0xf2140003 + +/* P2_DMDCFG2 */ +#define RSTV0910_P2_DMDCFG2 0xf215 +#define FSTV0910_P2_S1S2_SEQUENTIAL 0xf2156040 +#define FSTV0910_P2_INFINITE_RELOCK 0xf2154010 + +/* P2_DMDISTATE */ +#define RSTV0910_P2_DMDISTATE 0xf216 +#define FSTV0910_P2_I2C_NORESETDMODE 0xf2167080 +#define FSTV0910_P2_I2C_DEMOD_MODE 0xf216001f + +/* P2_DMDT0M */ +#define RSTV0910_P2_DMDT0M 0xf217 +#define FSTV0910_P2_DMDT0_MIN 0xf21700ff + +/* P2_DMDSTATE */ +#define RSTV0910_P2_DMDSTATE 0xf21b +#define FSTV0910_P2_HEADER_MODE 0xf21b5060 + +/* P2_DMDFLYW */ +#define RSTV0910_P2_DMDFLYW 0xf21c +#define FSTV0910_P2_I2C_IRQVAL 0xf21c40f0 +#define FSTV0910_P2_FLYWHEEL_CPT 0xf21c000f + +/* P2_DSTATUS3 */ +#define RSTV0910_P2_DSTATUS3 0xf21d +#define FSTV0910_P2_CFR_ZIGZAG 0xf21d7080 +#define FSTV0910_P2_DEMOD_CFGMODE 0xf21d5060 +#define FSTV0910_P2_GAMMA_LOWBAUDRATE 0xf21d4010 + +/* P2_DMDCFG3 */ +#define RSTV0910_P2_DMDCFG3 0xf21e +#define FSTV0910_P2_NOSTOP_FIFOFULL 0xf21e3008 + +/* P2_DMDCFG4 */ +#define RSTV0910_P2_DMDCFG4 0xf21f +#define FSTV0910_P2_DIS_VITLOCK 0xf21f7080 +#define FSTV0910_P2_DIS_CLKENABLE 0xf21f2004 + +/* P2_CORRELMANT */ +#define RSTV0910_P2_CORRELMANT 0xf220 +#define FSTV0910_P2_CORREL_MANT 0xf22000ff + +/* P2_CORRELABS */ +#define RSTV0910_P2_CORRELABS 0xf221 +#define FSTV0910_P2_CORREL_ABS 0xf22100ff + +/* P2_CORRELEXP */ +#define RSTV0910_P2_CORRELEXP 0xf222 +#define FSTV0910_P2_CORREL_ABSEXP 0xf22240f0 +#define FSTV0910_P2_CORREL_EXP 0xf222000f + +/* P2_PLHMODCOD */ +#define RSTV0910_P2_PLHMODCOD 0xf224 +#define FSTV0910_P2_SPECINV_DEMOD 0xf2247080 +#define FSTV0910_P2_PLH_MODCOD 0xf224207c +#define FSTV0910_P2_PLH_TYPE 0xf2240003 + +/* P2_DMDREG */ +#define RSTV0910_P2_DMDREG 0xf225 +#define FSTV0910_P2_DECIM_PLFRAMES 0xf2250001 + +/* P2_AGCNADJ */ +#define RSTV0910_P2_AGCNADJ 0xf226 +#define FSTV0910_P2_RADJOFF_AGC2 0xf2267080 +#define FSTV0910_P2_RADJOFF_AGC1 0xf2266040 +#define FSTV0910_P2_AGC_NADJ 0xf226013f + +/* P2_AGCKS */ +#define RSTV0910_P2_AGCKS 0xf227 +#define FSTV0910_P2_RSADJ_MANUALCFG 0xf2277080 +#define FSTV0910_P2_RSADJ_CCMMODE 0xf2276040 +#define FSTV0910_P2_RADJ_SPSK 0xf227013f + +/* P2_AGCKQ */ +#define RSTV0910_P2_AGCKQ 0xf228 +#define FSTV0910_P2_RADJON_DVBS1 0xf2286040 +#define FSTV0910_P2_RADJ_QPSK 0xf228013f + +/* P2_AGCK8 */ +#define RSTV0910_P2_AGCK8 0xf229 +#define FSTV0910_P2_RADJ_8PSK 0xf229013f + +/* P2_AGCK16 */ +#define RSTV0910_P2_AGCK16 0xf22a +#define FSTV0910_P2_R2ADJOFF_16APSK 0xf22a6040 +#define FSTV0910_P2_R1ADJOFF_16APSK 0xf22a5020 +#define FSTV0910_P2_RADJ_16APSK 0xf22a011f + +/* P2_AGCK32 */ +#define RSTV0910_P2_AGCK32 0xf22b +#define FSTV0910_P2_R3ADJOFF_32APSK 0xf22b7080 +#define FSTV0910_P2_R2ADJOFF_32APSK 0xf22b6040 +#define FSTV0910_P2_R1ADJOFF_32APSK 0xf22b5020 +#define FSTV0910_P2_RADJ_32APSK 0xf22b011f + +/* P2_AGC2O */ +#define RSTV0910_P2_AGC2O 0xf22c +#define FSTV0910_P2_CSTENV_MODE 0xf22c60c0 +#define FSTV0910_P2_AGC2_COEF 0xf22c0007 + +/* P2_AGC2REF */ +#define RSTV0910_P2_AGC2REF 0xf22d +#define FSTV0910_P2_AGC2_REF 0xf22d00ff + +/* P2_AGC1ADJ */ +#define RSTV0910_P2_AGC1ADJ 0xf22e +#define FSTV0910_P2_AGC1_ADJUSTED 0xf22e007f + +/* P2_AGCRSADJ */ +#define RSTV0910_P2_AGCRSADJ 0xf22f +#define FSTV0910_P2_RS_ADJUSTED 0xf22f007f + +/* P2_AGCRQADJ */ +#define RSTV0910_P2_AGCRQADJ 0xf230 +#define FSTV0910_P2_RQ_ADJUSTED 0xf230007f + +/* P2_AGCR8ADJ */ +#define RSTV0910_P2_AGCR8ADJ 0xf231 +#define FSTV0910_P2_R8_ADJUSTED 0xf231007f + +/* P2_AGCR1ADJ */ +#define RSTV0910_P2_AGCR1ADJ 0xf232 +#define FSTV0910_P2_R1_ADJUSTED 0xf232007f + +/* P2_AGCR2ADJ */ +#define RSTV0910_P2_AGCR2ADJ 0xf233 +#define FSTV0910_P2_R2_ADJUSTED 0xf233007f + +/* P2_AGCR3ADJ */ +#define RSTV0910_P2_AGCR3ADJ 0xf234 +#define FSTV0910_P2_R3_ADJUSTED 0xf234007f + +/* P2_AGCREFADJ */ +#define RSTV0910_P2_AGCREFADJ 0xf235 +#define FSTV0910_P2_AGC2REF_ADJUSTED 0xf235007f + +/* P2_AGC2I1 */ +#define RSTV0910_P2_AGC2I1 0xf236 +#define FSTV0910_P2_AGC2_INTEGRATOR1 0xf23600ff + +/* P2_AGC2I0 */ +#define RSTV0910_P2_AGC2I0 0xf237 +#define FSTV0910_P2_AGC2_INTEGRATOR0 0xf23700ff + +/* P2_CARCFG */ +#define RSTV0910_P2_CARCFG 0xf238 +#define FSTV0910_P2_ROTAON 0xf2382004 +#define FSTV0910_P2_PH_DET_ALGO 0xf2380003 + +/* P2_ACLC */ +#define RSTV0910_P2_ACLC 0xf239 +#define FSTV0910_P2_CAR_ALPHA_MANT 0xf2394030 +#define FSTV0910_P2_CAR_ALPHA_EXP 0xf239000f + +/* P2_BCLC */ +#define RSTV0910_P2_BCLC 0xf23a +#define FSTV0910_P2_CAR_BETA_MANT 0xf23a4030 +#define FSTV0910_P2_CAR_BETA_EXP 0xf23a000f + +/* P2_ACLCS2 */ +#define RSTV0910_P2_ACLCS2 0xf23b +#define FSTV0910_P2_CARS2_APLHA_MANTISSE 0xf23b4030 +#define FSTV0910_P2_CARS2_ALPHA_EXP 0xf23b000f + +/* P2_BCLCS2 */ +#define RSTV0910_P2_BCLCS2 0xf23c +#define FSTV0910_P2_CARS2_BETA_MANTISSE 0xf23c4030 +#define FSTV0910_P2_CARS2_BETA_EXP 0xf23c000f + +/* P2_CARFREQ */ +#define RSTV0910_P2_CARFREQ 0xf23d +#define FSTV0910_P2_KC_COARSE_EXP 0xf23d40f0 +#define FSTV0910_P2_BETA_FREQ 0xf23d000f + +/* P2_CARHDR */ +#define RSTV0910_P2_CARHDR 0xf23e +#define FSTV0910_P2_K_FREQ_HDR 0xf23e00ff + +/* P2_LDT */ +#define RSTV0910_P2_LDT 0xf23f +#define FSTV0910_P2_CARLOCK_THRES 0xf23f01ff + +/* P2_LDT2 */ +#define RSTV0910_P2_LDT2 0xf240 +#define FSTV0910_P2_CARLOCK_THRES2 0xf24001ff + +/* P2_CFRICFG */ +#define RSTV0910_P2_CFRICFG 0xf241 +#define FSTV0910_P2_NEG_CFRSTEP 0xf2410001 + +/* P2_CFRUP1 */ +#define RSTV0910_P2_CFRUP1 0xf242 +#define FSTV0910_P2_CFR_UP1 0xf24201ff + +/* P2_CFRUP0 */ +#define RSTV0910_P2_CFRUP0 0xf243 +#define FSTV0910_P2_CFR_UP0 0xf24300ff + +/* P2_CFRIBASE1 */ +#define RSTV0910_P2_CFRIBASE1 0xf244 +#define FSTV0910_P2_CFRINIT_BASE1 0xf24400ff + +/* P2_CFRIBASE0 */ +#define RSTV0910_P2_CFRIBASE0 0xf245 +#define FSTV0910_P2_CFRINIT_BASE0 0xf24500ff + +/* P2_CFRLOW1 */ +#define RSTV0910_P2_CFRLOW1 0xf246 +#define FSTV0910_P2_CFR_LOW1 0xf24601ff + +/* P2_CFRLOW0 */ +#define RSTV0910_P2_CFRLOW0 0xf247 +#define FSTV0910_P2_CFR_LOW0 0xf24700ff + +/* P2_CFRINIT1 */ +#define RSTV0910_P2_CFRINIT1 0xf248 +#define FSTV0910_P2_CFR_INIT1 0xf24801ff + +/* P2_CFRINIT0 */ +#define RSTV0910_P2_CFRINIT0 0xf249 +#define FSTV0910_P2_CFR_INIT0 0xf24900ff + +/* P2_CFRINC1 */ +#define RSTV0910_P2_CFRINC1 0xf24a +#define FSTV0910_P2_MANUAL_CFRINC 0xf24a7080 +#define FSTV0910_P2_CFR_INC1 0xf24a003f + +/* P2_CFRINC0 */ +#define RSTV0910_P2_CFRINC0 0xf24b +#define FSTV0910_P2_CFR_INC0 0xf24b00ff + +/* P2_CFR2 */ +#define RSTV0910_P2_CFR2 0xf24c +#define FSTV0910_P2_CAR_FREQ2 0xf24c01ff + +/* P2_CFR1 */ +#define RSTV0910_P2_CFR1 0xf24d +#define FSTV0910_P2_CAR_FREQ1 0xf24d00ff + +/* P2_CFR0 */ +#define RSTV0910_P2_CFR0 0xf24e +#define FSTV0910_P2_CAR_FREQ0 0xf24e00ff + +/* P2_LDI */ +#define RSTV0910_P2_LDI 0xf24f +#define FSTV0910_P2_LOCK_DET_INTEGR 0xf24f01ff + +/* P2_TMGCFG */ +#define RSTV0910_P2_TMGCFG 0xf250 +#define FSTV0910_P2_TMGLOCK_BETA 0xf25060c0 +#define FSTV0910_P2_DO_TIMING_CORR 0xf2504010 +#define FSTV0910_P2_TMG_MINFREQ 0xf2500003 + +/* P2_RTC */ +#define RSTV0910_P2_RTC 0xf251 +#define FSTV0910_P2_TMGALPHA_EXP 0xf25140f0 +#define FSTV0910_P2_TMGBETA_EXP 0xf251000f + +/* P2_RTCS2 */ +#define RSTV0910_P2_RTCS2 0xf252 +#define FSTV0910_P2_TMGALPHAS2_EXP 0xf25240f0 +#define FSTV0910_P2_TMGBETAS2_EXP 0xf252000f + +/* P2_TMGTHRISE */ +#define RSTV0910_P2_TMGTHRISE 0xf253 +#define FSTV0910_P2_TMGLOCK_THRISE 0xf25300ff + +/* P2_TMGTHFALL */ +#define RSTV0910_P2_TMGTHFALL 0xf254 +#define FSTV0910_P2_TMGLOCK_THFALL 0xf25400ff + +/* P2_SFRUPRATIO */ +#define RSTV0910_P2_SFRUPRATIO 0xf255 +#define FSTV0910_P2_SFR_UPRATIO 0xf25500ff + +/* P2_SFRLOWRATIO */ +#define RSTV0910_P2_SFRLOWRATIO 0xf256 +#define FSTV0910_P2_SFR_LOWRATIO 0xf25600ff + +/* P2_KTTMG */ +#define RSTV0910_P2_KTTMG 0xf257 +#define FSTV0910_P2_KT_TMG_EXP 0xf25740f0 + +/* P2_KREFTMG */ +#define RSTV0910_P2_KREFTMG 0xf258 +#define FSTV0910_P2_KREF_TMG 0xf25800ff + +/* P2_SFRSTEP */ +#define RSTV0910_P2_SFRSTEP 0xf259 +#define FSTV0910_P2_SFR_SCANSTEP 0xf25940f0 +#define FSTV0910_P2_SFR_CENTERSTEP 0xf259000f + +/* P2_TMGCFG2 */ +#define RSTV0910_P2_TMGCFG2 0xf25a +#define FSTV0910_P2_DIS_AUTOSAMP 0xf25a3008 +#define FSTV0910_P2_SFRRATIO_FINE 0xf25a0001 + +/* P2_KREFTMG2 */ +#define RSTV0910_P2_KREFTMG2 0xf25b +#define FSTV0910_P2_KREF_TMG2 0xf25b00ff + +/* P2_TMGCFG3 */ +#define RSTV0910_P2_TMGCFG3 0xf25d +#define FSTV0910_P2_CONT_TMGCENTER 0xf25d3008 +#define FSTV0910_P2_AUTO_GUP 0xf25d2004 +#define FSTV0910_P2_AUTO_GLOW 0xf25d1002 + +/* P2_SFRINIT1 */ +#define RSTV0910_P2_SFRINIT1 0xf25e +#define FSTV0910_P2_SFR_INIT1 0xf25e00ff + +/* P2_SFRINIT0 */ +#define RSTV0910_P2_SFRINIT0 0xf25f +#define FSTV0910_P2_SFR_INIT0 0xf25f00ff + +/* P2_SFRUP1 */ +#define RSTV0910_P2_SFRUP1 0xf260 +#define FSTV0910_P2_SYMB_FREQ_UP1 0xf26000ff + +/* P2_SFRUP0 */ +#define RSTV0910_P2_SFRUP0 0xf261 +#define FSTV0910_P2_SYMB_FREQ_UP0 0xf26100ff + +/* P2_SFRLOW1 */ +#define RSTV0910_P2_SFRLOW1 0xf262 +#define FSTV0910_P2_SYMB_FREQ_LOW1 0xf26200ff + +/* P2_SFRLOW0 */ +#define RSTV0910_P2_SFRLOW0 0xf263 +#define FSTV0910_P2_SYMB_FREQ_LOW0 0xf26300ff + +/* P2_SFR3 */ +#define RSTV0910_P2_SFR3 0xf264 +#define FSTV0910_P2_SYMB_FREQ3 0xf26400ff + +/* P2_SFR2 */ +#define RSTV0910_P2_SFR2 0xf265 +#define FSTV0910_P2_SYMB_FREQ2 0xf26500ff + +/* P2_SFR1 */ +#define RSTV0910_P2_SFR1 0xf266 +#define FSTV0910_P2_SYMB_FREQ1 0xf26600ff + +/* P2_SFR0 */ +#define RSTV0910_P2_SFR0 0xf267 +#define FSTV0910_P2_SYMB_FREQ0 0xf26700ff + +/* P2_TMGREG2 */ +#define RSTV0910_P2_TMGREG2 0xf268 +#define FSTV0910_P2_TMGREG2 0xf26800ff + +/* P2_TMGREG1 */ +#define RSTV0910_P2_TMGREG1 0xf269 +#define FSTV0910_P2_TMGREG1 0xf26900ff + +/* P2_TMGREG0 */ +#define RSTV0910_P2_TMGREG0 0xf26a +#define FSTV0910_P2_TMGREG0 0xf26a00ff + +/* P2_TMGLOCK1 */ +#define RSTV0910_P2_TMGLOCK1 0xf26b +#define FSTV0910_P2_TMGLOCK_LEVEL1 0xf26b01ff + +/* P2_TMGLOCK0 */ +#define RSTV0910_P2_TMGLOCK0 0xf26c +#define FSTV0910_P2_TMGLOCK_LEVEL0 0xf26c00ff + +/* P2_TMGOBS */ +#define RSTV0910_P2_TMGOBS 0xf26d +#define FSTV0910_P2_ROLLOFF_STATUS 0xf26d60c0 + +/* P2_EQUALCFG */ +#define RSTV0910_P2_EQUALCFG 0xf26f +#define FSTV0910_P2_EQUAL_ON 0xf26f6040 +#define FSTV0910_P2_MU_EQUALDFE 0xf26f0007 + +/* P2_EQUAI1 */ +#define RSTV0910_P2_EQUAI1 0xf270 +#define FSTV0910_P2_EQUA_ACCI1 0xf27001ff + +/* P2_EQUAQ1 */ +#define RSTV0910_P2_EQUAQ1 0xf271 +#define FSTV0910_P2_EQUA_ACCQ1 0xf27101ff + +/* P2_EQUAI2 */ +#define RSTV0910_P2_EQUAI2 0xf272 +#define FSTV0910_P2_EQUA_ACCI2 0xf27201ff + +/* P2_EQUAQ2 */ +#define RSTV0910_P2_EQUAQ2 0xf273 +#define FSTV0910_P2_EQUA_ACCQ2 0xf27301ff + +/* P2_EQUAI3 */ +#define RSTV0910_P2_EQUAI3 0xf274 +#define FSTV0910_P2_EQUA_ACCI3 0xf27401ff + +/* P2_EQUAQ3 */ +#define RSTV0910_P2_EQUAQ3 0xf275 +#define FSTV0910_P2_EQUA_ACCQ3 0xf27501ff + +/* P2_EQUAI4 */ +#define RSTV0910_P2_EQUAI4 0xf276 +#define FSTV0910_P2_EQUA_ACCI4 0xf27601ff + +/* P2_EQUAQ4 */ +#define RSTV0910_P2_EQUAQ4 0xf277 +#define FSTV0910_P2_EQUA_ACCQ4 0xf27701ff + +/* P2_EQUAI5 */ +#define RSTV0910_P2_EQUAI5 0xf278 +#define FSTV0910_P2_EQUA_ACCI5 0xf27801ff + +/* P2_EQUAQ5 */ +#define RSTV0910_P2_EQUAQ5 0xf279 +#define FSTV0910_P2_EQUA_ACCQ5 0xf27901ff + +/* P2_EQUAI6 */ +#define RSTV0910_P2_EQUAI6 0xf27a +#define FSTV0910_P2_EQUA_ACCI6 0xf27a01ff + +/* P2_EQUAQ6 */ +#define RSTV0910_P2_EQUAQ6 0xf27b +#define FSTV0910_P2_EQUA_ACCQ6 0xf27b01ff + +/* P2_EQUAI7 */ +#define RSTV0910_P2_EQUAI7 0xf27c +#define FSTV0910_P2_EQUA_ACCI7 0xf27c01ff + +/* P2_EQUAQ7 */ +#define RSTV0910_P2_EQUAQ7 0xf27d +#define FSTV0910_P2_EQUA_ACCQ7 0xf27d01ff + +/* P2_EQUAI8 */ +#define RSTV0910_P2_EQUAI8 0xf27e +#define FSTV0910_P2_EQUA_ACCI8 0xf27e01ff + +/* P2_EQUAQ8 */ +#define RSTV0910_P2_EQUAQ8 0xf27f +#define FSTV0910_P2_EQUA_ACCQ8 0xf27f01ff + +/* P2_NNOSDATAT1 */ +#define RSTV0910_P2_NNOSDATAT1 0xf280 +#define FSTV0910_P2_NOSDATAT_NORMED1 0xf28000ff + +/* P2_NNOSDATAT0 */ +#define RSTV0910_P2_NNOSDATAT0 0xf281 +#define FSTV0910_P2_NOSDATAT_NORMED0 0xf28100ff + +/* P2_NNOSDATA1 */ +#define RSTV0910_P2_NNOSDATA1 0xf282 +#define FSTV0910_P2_NOSDATA_NORMED1 0xf28200ff + +/* P2_NNOSDATA0 */ +#define RSTV0910_P2_NNOSDATA0 0xf283 +#define FSTV0910_P2_NOSDATA_NORMED0 0xf28300ff + +/* P2_NNOSPLHT1 */ +#define RSTV0910_P2_NNOSPLHT1 0xf284 +#define FSTV0910_P2_NOSPLHT_NORMED1 0xf28400ff + +/* P2_NNOSPLHT0 */ +#define RSTV0910_P2_NNOSPLHT0 0xf285 +#define FSTV0910_P2_NOSPLHT_NORMED0 0xf28500ff + +/* P2_NNOSPLH1 */ +#define RSTV0910_P2_NNOSPLH1 0xf286 +#define FSTV0910_P2_NOSPLH_NORMED1 0xf28600ff + +/* P2_NNOSPLH0 */ +#define RSTV0910_P2_NNOSPLH0 0xf287 +#define FSTV0910_P2_NOSPLH_NORMED0 0xf28700ff + +/* P2_NOSDATAT1 */ +#define RSTV0910_P2_NOSDATAT1 0xf288 +#define FSTV0910_P2_NOSDATAT_UNNORMED1 0xf28800ff + +/* P2_NOSDATAT0 */ +#define RSTV0910_P2_NOSDATAT0 0xf289 +#define FSTV0910_P2_NOSDATAT_UNNORMED0 0xf28900ff + +/* P2_NNOSFRAME1 */ +#define RSTV0910_P2_NNOSFRAME1 0xf28a +#define FSTV0910_P2_NOSFRAME_NORMED1 0xf28a00ff + +/* P2_NNOSFRAME0 */ +#define RSTV0910_P2_NNOSFRAME0 0xf28b +#define FSTV0910_P2_NOSFRAME_NORMED0 0xf28b00ff + +/* P2_NNOSRAD1 */ +#define RSTV0910_P2_NNOSRAD1 0xf28c +#define FSTV0910_P2_NOSRADIAL_NORMED1 0xf28c00ff + +/* P2_NNOSRAD0 */ +#define RSTV0910_P2_NNOSRAD0 0xf28d +#define FSTV0910_P2_NOSRADIAL_NORMED0 0xf28d00ff + +/* P2_NOSCFGF1 */ +#define RSTV0910_P2_NOSCFGF1 0xf28e +#define FSTV0910_P2_LOWNOISE_MESURE 0xf28e7080 +#define FSTV0910_P2_NOS_DELFRAME 0xf28e6040 +#define FSTV0910_P2_NOSDATA_MODE 0xf28e4030 +#define FSTV0910_P2_FRAMESEL_TYPESEL 0xf28e200c +#define FSTV0910_P2_FRAMESEL_TYPE 0xf28e0003 + +/* P2_NOSCFGF2 */ +#define RSTV0910_P2_NOSCFGF2 0xf28f +#define FSTV0910_P2_DIS_NOSPILOTS 0xf28f7080 +#define FSTV0910_P2_FRAMESEL_MODCODSEL 0xf28f5060 +#define FSTV0910_P2_FRAMESEL_MODCOD 0xf28f001f + +/* P2_CAR2CFG */ +#define RSTV0910_P2_CAR2CFG 0xf290 +#define FSTV0910_P2_ROTA2ON 0xf2902004 +#define FSTV0910_P2_PH_DET_ALGO2 0xf2900003 + +/* P2_CFR2CFR1 */ +#define RSTV0910_P2_CFR2CFR1 0xf291 +#define FSTV0910_P2_EN_S2CAR2CENTER 0xf2915020 +#define FSTV0910_P2_CFR2TOCFR1_BETA 0xf2910007 + +/* P2_CAR3CFG */ +#define RSTV0910_P2_CAR3CFG 0xf292 +#define FSTV0910_P2_CARRIER23_MODE 0xf29260c0 +#define FSTV0910_P2_CAR3INTERM_DVBS1 0xf2925020 +#define FSTV0910_P2_ABAMPLIF_MODE 0xf2923018 +#define FSTV0910_P2_CARRIER3_ALPHA3DL 0xf2920007 + +/* P2_CFR22 */ +#define RSTV0910_P2_CFR22 0xf293 +#define FSTV0910_P2_CAR2_FREQ2 0xf29301ff + +/* P2_CFR21 */ +#define RSTV0910_P2_CFR21 0xf294 +#define FSTV0910_P2_CAR2_FREQ1 0xf29400ff + +/* P2_CFR20 */ +#define RSTV0910_P2_CFR20 0xf295 +#define FSTV0910_P2_CAR2_FREQ0 0xf29500ff + +/* P2_ACLC2S2Q */ +#define RSTV0910_P2_ACLC2S2Q 0xf297 +#define FSTV0910_P2_ENAB_SPSKSYMB 0xf2977080 +#define FSTV0910_P2_CAR2S2_Q_ALPH_M 0xf2974030 +#define FSTV0910_P2_CAR2S2_Q_ALPH_E 0xf297000f + +/* P2_ACLC2S28 */ +#define RSTV0910_P2_ACLC2S28 0xf298 +#define FSTV0910_P2_CAR2S2_8_ALPH_M 0xf2984030 +#define FSTV0910_P2_CAR2S2_8_ALPH_E 0xf298000f + +/* P2_ACLC2S216A */ +#define RSTV0910_P2_ACLC2S216A 0xf299 +#define FSTV0910_P2_CAR2S2_16A_ALPH_M 0xf2994030 +#define FSTV0910_P2_CAR2S2_16A_ALPH_E 0xf299000f + +/* P2_ACLC2S232A */ +#define RSTV0910_P2_ACLC2S232A 0xf29a +#define FSTV0910_P2_CAR2S2_32A_ALPH_M 0xf29a4030 +#define FSTV0910_P2_CAR2S2_32A_ALPH_E 0xf29a000f + +/* P2_BCLC2S2Q */ +#define RSTV0910_P2_BCLC2S2Q 0xf29c +#define FSTV0910_P2_CAR2S2_Q_BETA_M 0xf29c4030 +#define FSTV0910_P2_CAR2S2_Q_BETA_E 0xf29c000f + +/* P2_BCLC2S28 */ +#define RSTV0910_P2_BCLC2S28 0xf29d +#define FSTV0910_P2_CAR2S2_8_BETA_M 0xf29d4030 +#define FSTV0910_P2_CAR2S2_8_BETA_E 0xf29d000f + +/* P2_BCLC2S216A */ +#define RSTV0910_P2_BCLC2S216A 0xf29e +#define FSTV0910_P2_DVBS2S216A_NIP 0xf29e7080 +#define FSTV0910_P2_CAR2S2_16A_BETA_M 0xf29e4030 +#define FSTV0910_P2_CAR2S2_16A_BETA_E 0xf29e000f + +/* P2_BCLC2S232A */ +#define RSTV0910_P2_BCLC2S232A 0xf29f +#define FSTV0910_P2_DVBS2S232A_NIP 0xf29f7080 +#define FSTV0910_P2_CAR2S2_32A_BETA_M 0xf29f4030 +#define FSTV0910_P2_CAR2S2_32A_BETA_E 0xf29f000f + +/* P2_PLROOT2 */ +#define RSTV0910_P2_PLROOT2 0xf2ac +#define FSTV0910_P2_PLSCRAMB_MODE 0xf2ac200c +#define FSTV0910_P2_PLSCRAMB_ROOT2 0xf2ac0003 + +/* P2_PLROOT1 */ +#define RSTV0910_P2_PLROOT1 0xf2ad +#define FSTV0910_P2_PLSCRAMB_ROOT1 0xf2ad00ff + +/* P2_PLROOT0 */ +#define RSTV0910_P2_PLROOT0 0xf2ae +#define FSTV0910_P2_PLSCRAMB_ROOT0 0xf2ae00ff + +/* P2_MODCODLST0 */ +#define RSTV0910_P2_MODCODLST0 0xf2b0 +#define FSTV0910_P2_NACCES_MODCODCH 0xf2b00001 + +/* P2_MODCODLST1 */ +#define RSTV0910_P2_MODCODLST1 0xf2b1 +#define FSTV0910_P2_SYMBRATE_FILTER 0xf2b13008 +#define FSTV0910_P2_NRESET_MODCODLST 0xf2b12004 +#define FSTV0910_P2_DIS_32PSK_9_10 0xf2b10003 + +/* P2_MODCODLST2 */ +#define RSTV0910_P2_MODCODLST2 0xf2b2 +#define FSTV0910_P2_DIS_32PSK_8_9 0xf2b240f0 +#define FSTV0910_P2_DIS_32PSK_5_6 0xf2b2000f + +/* P2_MODCODLST3 */ +#define RSTV0910_P2_MODCODLST3 0xf2b3 +#define FSTV0910_P2_DIS_32PSK_4_5 0xf2b340f0 +#define FSTV0910_P2_DIS_32PSK_3_4 0xf2b3000f + +/* P2_MODCODLST4 */ +#define RSTV0910_P2_MODCODLST4 0xf2b4 +#define FSTV0910_P2_DUMMYPL_PILOT 0xf2b47080 +#define FSTV0910_P2_DUMMYPL_NOPILOT 0xf2b46040 +#define FSTV0910_P2_DIS_16PSK_9_10 0xf2b44030 +#define FSTV0910_P2_DIS_16PSK_8_9 0xf2b4000f + +/* P2_MODCODLST5 */ +#define RSTV0910_P2_MODCODLST5 0xf2b5 +#define FSTV0910_P2_DIS_16PSK_5_6 0xf2b540f0 +#define FSTV0910_P2_DIS_16PSK_4_5 0xf2b5000f + +/* P2_MODCODLST6 */ +#define RSTV0910_P2_MODCODLST6 0xf2b6 +#define FSTV0910_P2_DIS_16PSK_3_4 0xf2b640f0 +#define FSTV0910_P2_DIS_16PSK_2_3 0xf2b6000f + +/* P2_MODCODLST7 */ +#define RSTV0910_P2_MODCODLST7 0xf2b7 +#define FSTV0910_P2_MODCOD_NNOSFILTER 0xf2b77080 +#define FSTV0910_P2_DIS_8PSK_9_10 0xf2b74030 +#define FSTV0910_P2_DIS_8PSK_8_9 0xf2b7000f + +/* P2_MODCODLST8 */ +#define RSTV0910_P2_MODCODLST8 0xf2b8 +#define FSTV0910_P2_DIS_8PSK_5_6 0xf2b840f0 +#define FSTV0910_P2_DIS_8PSK_3_4 0xf2b8000f + +/* P2_MODCODLST9 */ +#define RSTV0910_P2_MODCODLST9 0xf2b9 +#define FSTV0910_P2_DIS_8PSK_2_3 0xf2b940f0 +#define FSTV0910_P2_DIS_8PSK_3_5 0xf2b9000f + +/* P2_MODCODLSTA */ +#define RSTV0910_P2_MODCODLSTA 0xf2ba +#define FSTV0910_P2_NOSFILTER_LIMITE 0xf2ba7080 +#define FSTV0910_P2_DIS_QPSK_9_10 0xf2ba4030 +#define FSTV0910_P2_DIS_QPSK_8_9 0xf2ba000f + +/* P2_MODCODLSTB */ +#define RSTV0910_P2_MODCODLSTB 0xf2bb +#define FSTV0910_P2_DIS_QPSK_5_6 0xf2bb40f0 +#define FSTV0910_P2_DIS_QPSK_4_5 0xf2bb000f + +/* P2_MODCODLSTC */ +#define RSTV0910_P2_MODCODLSTC 0xf2bc +#define FSTV0910_P2_DIS_QPSK_3_4 0xf2bc40f0 +#define FSTV0910_P2_DIS_QPSK_2_3 0xf2bc000f + +/* P2_MODCODLSTD */ +#define RSTV0910_P2_MODCODLSTD 0xf2bd +#define FSTV0910_P2_DIS_QPSK_3_5 0xf2bd40f0 +#define FSTV0910_P2_DIS_QPSK_1_2 0xf2bd000f + +/* P2_MODCODLSTE */ +#define RSTV0910_P2_MODCODLSTE 0xf2be +#define FSTV0910_P2_DIS_QPSK_2_5 0xf2be40f0 +#define FSTV0910_P2_DIS_QPSK_1_3 0xf2be000f + +/* P2_MODCODLSTF */ +#define RSTV0910_P2_MODCODLSTF 0xf2bf +#define FSTV0910_P2_DIS_QPSK_1_4 0xf2bf40f0 +#define FSTV0910_P2_DEMOD_INVMODLST 0xf2bf3008 +#define FSTV0910_P2_DEMODOUT_ENABLE 0xf2bf2004 +#define FSTV0910_P2_DDEMOD_NSET 0xf2bf1002 +#define FSTV0910_P2_MODCOD_NSTOCK 0xf2bf0001 + +/* P2_GAUSSR0 */ +#define RSTV0910_P2_GAUSSR0 0xf2c0 +#define FSTV0910_P2_EN_CCIMODE 0xf2c07080 +#define FSTV0910_P2_R0_GAUSSIEN 0xf2c0007f + +/* P2_CCIR0 */ +#define RSTV0910_P2_CCIR0 0xf2c1 +#define FSTV0910_P2_CCIDETECT_PLHONLY 0xf2c17080 +#define FSTV0910_P2_R0_CCI 0xf2c1007f + +/* P2_CCIQUANT */ +#define RSTV0910_P2_CCIQUANT 0xf2c2 +#define FSTV0910_P2_CCI_BETA 0xf2c250e0 +#define FSTV0910_P2_CCI_QUANT 0xf2c2001f + +/* P2_CCITHRES */ +#define RSTV0910_P2_CCITHRES 0xf2c3 +#define FSTV0910_P2_CCI_THRESHOLD 0xf2c300ff + +/* P2_CCIACC */ +#define RSTV0910_P2_CCIACC 0xf2c4 +#define FSTV0910_P2_CCI_VALUE 0xf2c400ff + +/* P2_DSTATUS4 */ +#define RSTV0910_P2_DSTATUS4 0xf2c5 +#define FSTV0910_P2_RAINFADE_DETECT 0xf2c57080 +#define FSTV0910_P2_NOTHRES2_FAIL 0xf2c56040 +#define FSTV0910_P2_NOTHRES1_FAIL 0xf2c55020 +#define FSTV0910_P2_DMDPROG_ERROR 0xf2c52004 +#define FSTV0910_P2_CSTENV_DETECT 0xf2c51002 +#define FSTV0910_P2_DETECTION_TRIAX 0xf2c50001 + +/* P2_DMDRESCFG */ +#define RSTV0910_P2_DMDRESCFG 0xf2c6 +#define FSTV0910_P2_DMDRES_RESET 0xf2c67080 +#define FSTV0910_P2_DMDRES_STRALL 0xf2c63008 +#define FSTV0910_P2_DMDRES_NEWONLY 0xf2c62004 +#define FSTV0910_P2_DMDRES_NOSTORE 0xf2c61002 + +/* P2_DMDRESADR */ +#define RSTV0910_P2_DMDRESADR 0xf2c7 +#define FSTV0910_P2_DMDRES_VALIDCFR 0xf2c76040 +#define FSTV0910_P2_DMDRES_MEMFULL 0xf2c74030 +#define FSTV0910_P2_DMDRES_RESNBR 0xf2c7000f + +/* P2_DMDRESDATA7 */ +#define RSTV0910_P2_DMDRESDATA7 0xf2c8 +#define FSTV0910_P2_DMDRES_DATA7 0xf2c800ff + +/* P2_DMDRESDATA6 */ +#define RSTV0910_P2_DMDRESDATA6 0xf2c9 +#define FSTV0910_P2_DMDRES_DATA6 0xf2c900ff + +/* P2_DMDRESDATA5 */ +#define RSTV0910_P2_DMDRESDATA5 0xf2ca +#define FSTV0910_P2_DMDRES_DATA5 0xf2ca00ff + +/* P2_DMDRESDATA4 */ +#define RSTV0910_P2_DMDRESDATA4 0xf2cb +#define FSTV0910_P2_DMDRES_DATA4 0xf2cb00ff + +/* P2_DMDRESDATA3 */ +#define RSTV0910_P2_DMDRESDATA3 0xf2cc +#define FSTV0910_P2_DMDRES_DATA3 0xf2cc00ff + +/* P2_DMDRESDATA2 */ +#define RSTV0910_P2_DMDRESDATA2 0xf2cd +#define FSTV0910_P2_DMDRES_DATA2 0xf2cd00ff + +/* P2_DMDRESDATA1 */ +#define RSTV0910_P2_DMDRESDATA1 0xf2ce +#define FSTV0910_P2_DMDRES_DATA1 0xf2ce00ff + +/* P2_DMDRESDATA0 */ +#define RSTV0910_P2_DMDRESDATA0 0xf2cf +#define FSTV0910_P2_DMDRES_DATA0 0xf2cf00ff + +/* P2_FFEI1 */ +#define RSTV0910_P2_FFEI1 0xf2d0 +#define FSTV0910_P2_FFE_ACCI1 0xf2d001ff + +/* P2_FFEQ1 */ +#define RSTV0910_P2_FFEQ1 0xf2d1 +#define FSTV0910_P2_FFE_ACCQ1 0xf2d101ff + +/* P2_FFEI2 */ +#define RSTV0910_P2_FFEI2 0xf2d2 +#define FSTV0910_P2_FFE_ACCI2 0xf2d201ff + +/* P2_FFEQ2 */ +#define RSTV0910_P2_FFEQ2 0xf2d3 +#define FSTV0910_P2_FFE_ACCQ2 0xf2d301ff + +/* P2_FFEI3 */ +#define RSTV0910_P2_FFEI3 0xf2d4 +#define FSTV0910_P2_FFE_ACCI3 0xf2d401ff + +/* P2_FFEQ3 */ +#define RSTV0910_P2_FFEQ3 0xf2d5 +#define FSTV0910_P2_FFE_ACCQ3 0xf2d501ff + +/* P2_FFEI4 */ +#define RSTV0910_P2_FFEI4 0xf2d6 +#define FSTV0910_P2_FFE_ACCI4 0xf2d601ff + +/* P2_FFEQ4 */ +#define RSTV0910_P2_FFEQ4 0xf2d7 +#define FSTV0910_P2_FFE_ACCQ4 0xf2d701ff + +/* P2_FFECFG */ +#define RSTV0910_P2_FFECFG 0xf2d8 +#define FSTV0910_P2_EQUALFFE_ON 0xf2d86040 +#define FSTV0910_P2_EQUAL_USEDSYMB 0xf2d84030 +#define FSTV0910_P2_MU_EQUALFFE 0xf2d80007 + +/* P2_TNRCFG2 */ +#define RSTV0910_P2_TNRCFG2 0xf2e1 +#define FSTV0910_P2_TUN_IQSWAP 0xf2e17080 + +/* P2_SMAPCOEF7 */ +#define RSTV0910_P2_SMAPCOEF7 0xf300 +#define FSTV0910_P2_DIS_QSCALE 0xf3007080 +#define FSTV0910_P2_SMAPCOEF_Q_LLR12 0xf300017f + +/* P2_SMAPCOEF6 */ +#define RSTV0910_P2_SMAPCOEF6 0xf301 +#define FSTV0910_P2_DIS_AGC2SCALE 0xf3017080 +#define FSTV0910_P2_ADJ_8PSKLLR1 0xf3012004 +#define FSTV0910_P2_OLD_8PSKLLR1 0xf3011002 +#define FSTV0910_P2_DIS_AB8PSK 0xf3010001 + +/* P2_SMAPCOEF5 */ +#define RSTV0910_P2_SMAPCOEF5 0xf302 +#define FSTV0910_P2_DIS_8SCALE 0xf3027080 +#define FSTV0910_P2_SMAPCOEF_8P_LLR23 0xf302017f + +/* P2_SMAPCOEF4 */ +#define RSTV0910_P2_SMAPCOEF4 0xf303 +#define FSTV0910_P2_SMAPCOEF_16APSK_LLR12 0xf303017f + +/* P2_SMAPCOEF3 */ +#define RSTV0910_P2_SMAPCOEF3 0xf304 +#define FSTV0910_P2_SMAPCOEF_16APSK_LLR34 0xf304017f + +/* P2_SMAPCOEF2 */ +#define RSTV0910_P2_SMAPCOEF2 0xf305 +#define FSTV0910_P2_SMAPCOEF_32APSK_R2R3 0xf30541f0 +#define FSTV0910_P2_SMAPCOEF_32APSK_LLR2 0xf305010f + +/* P2_SMAPCOEF1 */ +#define RSTV0910_P2_SMAPCOEF1 0xf306 +#define FSTV0910_P2_DIS_16SCALE 0xf3067080 +#define FSTV0910_P2_SMAPCOEF_32_LLR34 0xf306017f + +/* P2_SMAPCOEF0 */ +#define RSTV0910_P2_SMAPCOEF0 0xf307 +#define FSTV0910_P2_DIS_32SCALE 0xf3077080 +#define FSTV0910_P2_SMAPCOEF_32_LLR15 0xf307017f + +/* P2_NOSTHRES1 */ +#define RSTV0910_P2_NOSTHRES1 0xf309 +#define FSTV0910_P2_NOS_THRESHOLD1 0xf30900ff + +/* P2_NOSTHRES2 */ +#define RSTV0910_P2_NOSTHRES2 0xf30a +#define FSTV0910_P2_NOS_THRESHOLD2 0xf30a00ff + +/* P2_NOSDIFF1 */ +#define RSTV0910_P2_NOSDIFF1 0xf30b +#define FSTV0910_P2_NOSTHRES1_DIFF 0xf30b00ff + +/* P2_RAINFADE */ +#define RSTV0910_P2_RAINFADE 0xf30c +#define FSTV0910_P2_NOSTHRES_DATAT 0xf30c7080 +#define FSTV0910_P2_RAINFADE_CNLIMIT 0xf30c4070 +#define FSTV0910_P2_RAINFADE_TIMEOUT 0xf30c0007 + +/* P2_NOSRAMCFG */ +#define RSTV0910_P2_NOSRAMCFG 0xf30d +#define FSTV0910_P2_NOSRAM_ACTIVATION 0xf30d4030 +#define FSTV0910_P2_NOSRAM_CNRONLY 0xf30d3008 +#define FSTV0910_P2_NOSRAM_LGNCNR1 0xf30d0007 + +/* P2_NOSRAMPOS */ +#define RSTV0910_P2_NOSRAMPOS 0xf30e +#define FSTV0910_P2_NOSRAM_LGNCNR0 0xf30e40f0 +#define FSTV0910_P2_NOSRAM_VALIDE 0xf30e2004 +#define FSTV0910_P2_NOSRAM_CNRVAL1 0xf30e0003 + +/* P2_NOSRAMVAL */ +#define RSTV0910_P2_NOSRAMVAL 0xf30f +#define FSTV0910_P2_NOSRAM_CNRVAL0 0xf30f00ff + +/* P2_DMDPLHSTAT */ +#define RSTV0910_P2_DMDPLHSTAT 0xf320 +#define FSTV0910_P2_PLH_STATISTIC 0xf32000ff + +/* P2_LOCKTIME3 */ +#define RSTV0910_P2_LOCKTIME3 0xf322 +#define FSTV0910_P2_DEMOD_LOCKTIME3 0xf32200ff + +/* P2_LOCKTIME2 */ +#define RSTV0910_P2_LOCKTIME2 0xf323 +#define FSTV0910_P2_DEMOD_LOCKTIME2 0xf32300ff + +/* P2_LOCKTIME1 */ +#define RSTV0910_P2_LOCKTIME1 0xf324 +#define FSTV0910_P2_DEMOD_LOCKTIME1 0xf32400ff + +/* P2_LOCKTIME0 */ +#define RSTV0910_P2_LOCKTIME0 0xf325 +#define FSTV0910_P2_DEMOD_LOCKTIME0 0xf32500ff + +/* P2_VITSCALE */ +#define RSTV0910_P2_VITSCALE 0xf332 +#define FSTV0910_P2_NVTH_NOSRANGE 0xf3327080 +#define FSTV0910_P2_VERROR_MAXMODE 0xf3326040 +#define FSTV0910_P2_NSLOWSN_LOCKED 0xf3323008 +#define FSTV0910_P2_DIS_RSFLOCK 0xf3321002 + +/* P2_FECM */ +#define RSTV0910_P2_FECM 0xf333 +#define FSTV0910_P2_DSS_DVB 0xf3337080 +#define FSTV0910_P2_DSS_SRCH 0xf3334010 +#define FSTV0910_P2_SYNCVIT 0xf3331002 +#define FSTV0910_P2_IQINV 0xf3330001 + +/* P2_VTH12 */ +#define RSTV0910_P2_VTH12 0xf334 +#define FSTV0910_P2_VTH12 0xf33400ff + +/* P2_VTH23 */ +#define RSTV0910_P2_VTH23 0xf335 +#define FSTV0910_P2_VTH23 0xf33500ff + +/* P2_VTH34 */ +#define RSTV0910_P2_VTH34 0xf336 +#define FSTV0910_P2_VTH34 0xf33600ff + +/* P2_VTH56 */ +#define RSTV0910_P2_VTH56 0xf337 +#define FSTV0910_P2_VTH56 0xf33700ff + +/* P2_VTH67 */ +#define RSTV0910_P2_VTH67 0xf338 +#define FSTV0910_P2_VTH67 0xf33800ff + +/* P2_VTH78 */ +#define RSTV0910_P2_VTH78 0xf339 +#define FSTV0910_P2_VTH78 0xf33900ff + +/* P2_VITCURPUN */ +#define RSTV0910_P2_VITCURPUN 0xf33a +#define FSTV0910_P2_VIT_CURPUN 0xf33a001f + +/* P2_VERROR */ +#define RSTV0910_P2_VERROR 0xf33b +#define FSTV0910_P2_REGERR_VIT 0xf33b00ff + +/* P2_PRVIT */ +#define RSTV0910_P2_PRVIT 0xf33c +#define FSTV0910_P2_DIS_VTHLOCK 0xf33c6040 +#define FSTV0910_P2_E7_8VIT 0xf33c5020 +#define FSTV0910_P2_E6_7VIT 0xf33c4010 +#define FSTV0910_P2_E5_6VIT 0xf33c3008 +#define FSTV0910_P2_E3_4VIT 0xf33c2004 +#define FSTV0910_P2_E2_3VIT 0xf33c1002 +#define FSTV0910_P2_E1_2VIT 0xf33c0001 + +/* P2_VAVSRVIT */ +#define RSTV0910_P2_VAVSRVIT 0xf33d +#define FSTV0910_P2_AMVIT 0xf33d7080 +#define FSTV0910_P2_FROZENVIT 0xf33d6040 +#define FSTV0910_P2_SNVIT 0xf33d4030 +#define FSTV0910_P2_TOVVIT 0xf33d200c +#define FSTV0910_P2_HYPVIT 0xf33d0003 + +/* P2_VSTATUSVIT */ +#define RSTV0910_P2_VSTATUSVIT 0xf33e +#define FSTV0910_P2_PRFVIT 0xf33e4010 +#define FSTV0910_P2_LOCKEDVIT 0xf33e3008 + +/* P2_VTHINUSE */ +#define RSTV0910_P2_VTHINUSE 0xf33f +#define FSTV0910_P2_VIT_INUSE 0xf33f00ff + +/* P2_KDIV12 */ +#define RSTV0910_P2_KDIV12 0xf340 +#define FSTV0910_P2_K_DIVIDER_12 0xf340007f + +/* P2_KDIV23 */ +#define RSTV0910_P2_KDIV23 0xf341 +#define FSTV0910_P2_K_DIVIDER_23 0xf341007f + +/* P2_KDIV34 */ +#define RSTV0910_P2_KDIV34 0xf342 +#define FSTV0910_P2_K_DIVIDER_34 0xf342007f + +/* P2_KDIV56 */ +#define RSTV0910_P2_KDIV56 0xf343 +#define FSTV0910_P2_K_DIVIDER_56 0xf343007f + +/* P2_KDIV67 */ +#define RSTV0910_P2_KDIV67 0xf344 +#define FSTV0910_P2_K_DIVIDER_67 0xf344007f + +/* P2_KDIV78 */ +#define RSTV0910_P2_KDIV78 0xf345 +#define FSTV0910_P2_K_DIVIDER_78 0xf345007f + +/* P2_TSPIDFLT1 */ +#define RSTV0910_P2_TSPIDFLT1 0xf346 +#define FSTV0910_P2_PIDFLT_ADDR 0xf34600ff + +/* P2_TSPIDFLT0 */ +#define RSTV0910_P2_TSPIDFLT0 0xf347 +#define FSTV0910_P2_PIDFLT_DATA 0xf34700ff + +/* P2_PDELCTRL0 */ +#define RSTV0910_P2_PDELCTRL0 0xf34f +#define FSTV0910_P2_ISIOBS_MODE 0xf34f4030 + +/* P2_PDELCTRL1 */ +#define RSTV0910_P2_PDELCTRL1 0xf350 +#define FSTV0910_P2_INV_MISMASK 0xf3507080 +#define FSTV0910_P2_FILTER_EN 0xf3505020 +#define FSTV0910_P2_HYSTEN 0xf3503008 +#define FSTV0910_P2_HYSTSWRST 0xf3502004 +#define FSTV0910_P2_EN_MIS00 0xf3501002 +#define FSTV0910_P2_ALGOSWRST 0xf3500001 + +/* P2_PDELCTRL2 */ +#define RSTV0910_P2_PDELCTRL2 0xf351 +#define FSTV0910_P2_FORCE_CONTINUOUS 0xf3517080 +#define FSTV0910_P2_RESET_UPKO_COUNT 0xf3516040 +#define FSTV0910_P2_USER_PKTDELIN_NB 0xf3515020 +#define FSTV0910_P2_FRAME_MODE 0xf3511002 + +/* P2_HYSTTHRESH */ +#define RSTV0910_P2_HYSTTHRESH 0xf354 +#define FSTV0910_P2_DELIN_LOCKTHRES 0xf35440f0 +#define FSTV0910_P2_DELIN_UNLOCKTHRES 0xf354000f + +/* P2_UPLCCST0 */ +#define RSTV0910_P2_UPLCCST0 0xf358 +#define FSTV0910_P2_UPL_CST0 0xf35830f8 +#define FSTV0910_P2_UPL_MODE 0xf3580007 + +/* P2_ISIENTRY */ +#define RSTV0910_P2_ISIENTRY 0xf35e +#define FSTV0910_P2_ISI_ENTRY 0xf35e00ff + +/* P2_ISIBITENA */ +#define RSTV0910_P2_ISIBITENA 0xf35f +#define FSTV0910_P2_ISI_BIT_EN 0xf35f00ff + +/* P2_MATSTR1 */ +#define RSTV0910_P2_MATSTR1 0xf360 +#define FSTV0910_P2_MATYPE_CURRENT1 0xf36000ff + +/* P2_MATSTR0 */ +#define RSTV0910_P2_MATSTR0 0xf361 +#define FSTV0910_P2_MATYPE_CURRENT0 0xf36100ff + +/* P2_UPLSTR1 */ +#define RSTV0910_P2_UPLSTR1 0xf362 +#define FSTV0910_P2_UPL_CURRENT1 0xf36200ff + +/* P2_UPLSTR0 */ +#define RSTV0910_P2_UPLSTR0 0xf363 +#define FSTV0910_P2_UPL_CURRENT0 0xf36300ff + +/* P2_DFLSTR1 */ +#define RSTV0910_P2_DFLSTR1 0xf364 +#define FSTV0910_P2_DFL_CURRENT1 0xf36400ff + +/* P2_DFLSTR0 */ +#define RSTV0910_P2_DFLSTR0 0xf365 +#define FSTV0910_P2_DFL_CURRENT0 0xf36500ff + +/* P2_SYNCSTR */ +#define RSTV0910_P2_SYNCSTR 0xf366 +#define FSTV0910_P2_SYNC_CURRENT 0xf36600ff + +/* P2_SYNCDSTR1 */ +#define RSTV0910_P2_SYNCDSTR1 0xf367 +#define FSTV0910_P2_SYNCD_CURRENT1 0xf36700ff + +/* P2_SYNCDSTR0 */ +#define RSTV0910_P2_SYNCDSTR0 0xf368 +#define FSTV0910_P2_SYNCD_CURRENT0 0xf36800ff + +/* P2_PDELSTATUS1 */ +#define RSTV0910_P2_PDELSTATUS1 0xf369 +#define FSTV0910_P2_PKTDELIN_DELOCK 0xf3697080 +#define FSTV0910_P2_SYNCDUPDFL_BADDFL 0xf3696040 +#define FSTV0910_P2_UNACCEPTED_STREAM 0xf3694010 +#define FSTV0910_P2_BCH_ERROR_FLAG 0xf3693008 +#define FSTV0910_P2_PKTDELIN_LOCK 0xf3691002 +#define FSTV0910_P2_FIRST_LOCK 0xf3690001 + +/* P2_PDELSTATUS2 */ +#define RSTV0910_P2_PDELSTATUS2 0xf36a +#define FSTV0910_P2_FRAME_MODCOD 0xf36a207c +#define FSTV0910_P2_FRAME_TYPE 0xf36a0003 + +/* P2_BBFCRCKO1 */ +#define RSTV0910_P2_BBFCRCKO1 0xf36b +#define FSTV0910_P2_BBHCRC_KOCNT1 0xf36b00ff + +/* P2_BBFCRCKO0 */ +#define RSTV0910_P2_BBFCRCKO0 0xf36c +#define FSTV0910_P2_BBHCRC_KOCNT0 0xf36c00ff + +/* P2_UPCRCKO1 */ +#define RSTV0910_P2_UPCRCKO1 0xf36d +#define FSTV0910_P2_PKTCRC_KOCNT1 0xf36d00ff + +/* P2_UPCRCKO0 */ +#define RSTV0910_P2_UPCRCKO0 0xf36e +#define FSTV0910_P2_PKTCRC_KOCNT0 0xf36e00ff + +/* P2_PDELCTRL3 */ +#define RSTV0910_P2_PDELCTRL3 0xf36f +#define FSTV0910_P2_NOFIFO_BCHERR 0xf36f5020 +#define FSTV0910_P2_PKTDELIN_DELACMERR 0xf36f4010 + +/* P2_TSSTATEM */ +#define RSTV0910_P2_TSSTATEM 0xf370 +#define FSTV0910_P2_TSDIL_ON 0xf3707080 +#define FSTV0910_P2_TSRS_ON 0xf3705020 +#define FSTV0910_P2_TSDESCRAMB_ON 0xf3704010 +#define FSTV0910_P2_TSFRAME_MODE 0xf3703008 +#define FSTV0910_P2_TS_DISABLE 0xf3702004 +#define FSTV0910_P2_TSACM_MODE 0xf3701002 +#define FSTV0910_P2_TSOUT_NOSYNC 0xf3700001 + +/* P2_TSSTATEL */ +#define RSTV0910_P2_TSSTATEL 0xf371 +#define FSTV0910_P2_TSNOSYNCBYTE 0xf3717080 +#define FSTV0910_P2_TSPARITY_ON 0xf3716040 +#define FSTV0910_P2_TSISSYI_ON 0xf3713008 +#define FSTV0910_P2_TSNPD_ON 0xf3712004 +#define FSTV0910_P2_TSCRC8_ON 0xf3711002 +#define FSTV0910_P2_TSDSS_PACKET 0xf3710001 + +/* P2_TSCFGH */ +#define RSTV0910_P2_TSCFGH 0xf372 +#define FSTV0910_P2_TSFIFO_DVBCI 0xf3727080 +#define FSTV0910_P2_TSFIFO_SERIAL 0xf3726040 +#define FSTV0910_P2_TSFIFO_TEIUPDATE 0xf3725020 +#define FSTV0910_P2_TSFIFO_DUTY50 0xf3724010 +#define FSTV0910_P2_TSFIFO_HSGNLOUT 0xf3723008 +#define FSTV0910_P2_TSFIFO_ERRMODE 0xf3721006 +#define FSTV0910_P2_RST_HWARE 0xf3720001 + +/* P2_TSCFGM */ +#define RSTV0910_P2_TSCFGM 0xf373 +#define FSTV0910_P2_TSFIFO_MANSPEED 0xf37360c0 +#define FSTV0910_P2_TSFIFO_PERMDATA 0xf3735020 +#define FSTV0910_P2_TSFIFO_NONEWSGNL 0xf3734010 +#define FSTV0910_P2_TSFIFO_INVDATA 0xf3730001 + +/* P2_TSCFGL */ +#define RSTV0910_P2_TSCFGL 0xf374 +#define FSTV0910_P2_TSFIFO_BCLKDEL1CK 0xf37460c0 +#define FSTV0910_P2_BCHERROR_MODE 0xf3744030 +#define FSTV0910_P2_TSFIFO_NSGNL2DATA 0xf3743008 +#define FSTV0910_P2_TSFIFO_EMBINDVB 0xf3742004 +#define FSTV0910_P2_TSFIFO_BITSPEED 0xf3740003 + +/* P2_TSSYNC */ +#define RSTV0910_P2_TSSYNC 0xf375 +#define FSTV0910_P2_TSFIFO_SYNCMODE 0xf3753018 + +/* P2_TSINSDELH */ +#define RSTV0910_P2_TSINSDELH 0xf376 +#define FSTV0910_P2_TSDEL_SYNCBYTE 0xf3767080 +#define FSTV0910_P2_TSDEL_XXHEADER 0xf3766040 +#define FSTV0910_P2_TSDEL_DATAFIELD 0xf3764010 +#define FSTV0910_P2_TSINSDEL_RSPARITY 0xf3761002 +#define FSTV0910_P2_TSINSDEL_CRC8 0xf3760001 + +/* P2_TSINSDELM */ +#define RSTV0910_P2_TSINSDELM 0xf377 +#define FSTV0910_P2_TSINS_EMODCOD 0xf3774010 +#define FSTV0910_P2_TSINS_TOKEN 0xf3773008 +#define FSTV0910_P2_TSINS_XXXERR 0xf3772004 +#define FSTV0910_P2_TSINS_MATYPE 0xf3771002 +#define FSTV0910_P2_TSINS_UPL 0xf3770001 + +/* P2_TSINSDELL */ +#define RSTV0910_P2_TSINSDELL 0xf378 +#define FSTV0910_P2_TSINS_DFL 0xf3787080 +#define FSTV0910_P2_TSINS_SYNCD 0xf3786040 +#define FSTV0910_P2_TSINS_BLOCLEN 0xf3785020 +#define FSTV0910_P2_TSINS_SIGPCOUNT 0xf3784010 +#define FSTV0910_P2_TSINS_FIFO 0xf3783008 +#define FSTV0910_P2_TSINS_REALPACK 0xf3782004 +#define FSTV0910_P2_TSINS_TSCONFIG 0xf3781002 +#define FSTV0910_P2_TSINS_LATENCY 0xf3780001 + +/* P2_TSDIVN */ +#define RSTV0910_P2_TSDIVN 0xf379 +#define FSTV0910_P2_TSFIFO_SPEEDMODE 0xf37960c0 +#define FSTV0910_P2_TSFIFO_RISEOK 0xf3790007 + +/* P2_TSCFG4 */ +#define RSTV0910_P2_TSCFG4 0xf37a +#define FSTV0910_P2_TSFIFO_TSSPEEDMODE 0xf37a60c0 + +/* P2_TSSPEED */ +#define RSTV0910_P2_TSSPEED 0xf380 +#define FSTV0910_P2_TSFIFO_OUTSPEED 0xf38000ff + +/* P2_TSSTATUS */ +#define RSTV0910_P2_TSSTATUS 0xf381 +#define FSTV0910_P2_TSFIFO_LINEOK 0xf3817080 +#define FSTV0910_P2_TSFIFO_ERROR 0xf3816040 +#define FSTV0910_P2_TSFIFO_NOSYNC 0xf3814010 +#define FSTV0910_P2_TSREGUL_ERROR 0xf3812004 +#define FSTV0910_P2_DIL_READY 0xf3810001 + +/* P2_TSSTATUS2 */ +#define RSTV0910_P2_TSSTATUS2 0xf382 +#define FSTV0910_P2_TSFIFO_DEMODSEL 0xf3827080 +#define FSTV0910_P2_TSFIFOSPEED_STORE 0xf3826040 +#define FSTV0910_P2_DILXX_RESET 0xf3825020 +#define FSTV0910_P2_SCRAMBDETECT 0xf3821002 + +/* P2_TSBITRATE1 */ +#define RSTV0910_P2_TSBITRATE1 0xf383 +#define FSTV0910_P2_TSFIFO_BITRATE1 0xf38300ff + +/* P2_TSBITRATE0 */ +#define RSTV0910_P2_TSBITRATE0 0xf384 +#define FSTV0910_P2_TSFIFO_BITRATE0 0xf38400ff + +/* P2_TSPACKLEN1 */ +#define RSTV0910_P2_TSPACKLEN1 0xf385 +#define FSTV0910_P2_TSFIFO_PACKCPT 0xf38550e0 + +/* P2_TSDLY2 */ +#define RSTV0910_P2_TSDLY2 0xf389 +#define FSTV0910_P2_SOFFIFO_LATENCY2 0xf389000f + +/* P2_TSDLY1 */ +#define RSTV0910_P2_TSDLY1 0xf38a +#define FSTV0910_P2_SOFFIFO_LATENCY1 0xf38a00ff + +/* P2_TSDLY0 */ +#define RSTV0910_P2_TSDLY0 0xf38b +#define FSTV0910_P2_SOFFIFO_LATENCY0 0xf38b00ff + +/* P2_TSNPDAV */ +#define RSTV0910_P2_TSNPDAV 0xf38c +#define FSTV0910_P2_TSNPD_AVERAGE 0xf38c00ff + +/* P2_TSBUFSTAT2 */ +#define RSTV0910_P2_TSBUFSTAT2 0xf38d +#define FSTV0910_P2_TSISCR_3BYTES 0xf38d7080 +#define FSTV0910_P2_TSISCR_NEWDATA 0xf38d6040 +#define FSTV0910_P2_TSISCR_BUFSTAT2 0xf38d003f + +/* P2_TSBUFSTAT1 */ +#define RSTV0910_P2_TSBUFSTAT1 0xf38e +#define FSTV0910_P2_TSISCR_BUFSTAT1 0xf38e00ff + +/* P2_TSBUFSTAT0 */ +#define RSTV0910_P2_TSBUFSTAT0 0xf38f +#define FSTV0910_P2_TSISCR_BUFSTAT0 0xf38f00ff + +/* P2_TSDEBUGL */ +#define RSTV0910_P2_TSDEBUGL 0xf391 +#define FSTV0910_P2_TSFIFO_ERROR_EVNT 0xf3912004 +#define FSTV0910_P2_TSFIFO_OVERFLOWM 0xf3910001 + +/* P2_TSDLYSET2 */ +#define RSTV0910_P2_TSDLYSET2 0xf392 +#define FSTV0910_P2_SOFFIFO_OFFSET 0xf39260c0 +#define FSTV0910_P2_HYSTERESIS_THRESHOLD 0xf3924030 +#define FSTV0910_P2_SOFFIFO_SYMBOFFS2 0xf392000f + +/* P2_TSDLYSET1 */ +#define RSTV0910_P2_TSDLYSET1 0xf393 +#define FSTV0910_P2_SOFFIFO_SYMBOFFS1 0xf39300ff + +/* P2_TSDLYSET0 */ +#define RSTV0910_P2_TSDLYSET0 0xf394 +#define FSTV0910_P2_SOFFIFO_SYMBOFFS0 0xf39400ff + +/* P2_ERRCTRL1 */ +#define RSTV0910_P2_ERRCTRL1 0xf398 +#define FSTV0910_P2_ERR_SOURCE1 0xf39840f0 +#define FSTV0910_P2_NUM_EVENT1 0xf3980007 + +/* P2_ERRCNT12 */ +#define RSTV0910_P2_ERRCNT12 0xf399 +#define FSTV0910_P2_ERRCNT1_OLDVALUE 0xf3997080 +#define FSTV0910_P2_ERR_CNT12 0xf399007f + +/* P2_ERRCNT11 */ +#define RSTV0910_P2_ERRCNT11 0xf39a +#define FSTV0910_P2_ERR_CNT11 0xf39a00ff + +/* P2_ERRCNT10 */ +#define RSTV0910_P2_ERRCNT10 0xf39b +#define FSTV0910_P2_ERR_CNT10 0xf39b00ff + +/* P2_ERRCTRL2 */ +#define RSTV0910_P2_ERRCTRL2 0xf39c +#define FSTV0910_P2_ERR_SOURCE2 0xf39c40f0 +#define FSTV0910_P2_NUM_EVENT2 0xf39c0007 + +/* P2_ERRCNT22 */ +#define RSTV0910_P2_ERRCNT22 0xf39d +#define FSTV0910_P2_ERRCNT2_OLDVALUE 0xf39d7080 +#define FSTV0910_P2_ERR_CNT22 0xf39d007f + +/* P2_ERRCNT21 */ +#define RSTV0910_P2_ERRCNT21 0xf39e +#define FSTV0910_P2_ERR_CNT21 0xf39e00ff + +/* P2_ERRCNT20 */ +#define RSTV0910_P2_ERRCNT20 0xf39f +#define FSTV0910_P2_ERR_CNT20 0xf39f00ff + +/* P2_FECSPY */ +#define RSTV0910_P2_FECSPY 0xf3a0 +#define FSTV0910_P2_SPY_ENABLE 0xf3a07080 +#define FSTV0910_P2_NO_SYNCBYTE 0xf3a06040 +#define FSTV0910_P2_SERIAL_MODE 0xf3a05020 +#define FSTV0910_P2_UNUSUAL_PACKET 0xf3a04010 +#define FSTV0910_P2_BERMETER_DATAMODE 0xf3a0200c +#define FSTV0910_P2_BERMETER_LMODE 0xf3a01002 +#define FSTV0910_P2_BERMETER_RESET 0xf3a00001 + +/* P2_FSPYCFG */ +#define RSTV0910_P2_FSPYCFG 0xf3a1 +#define FSTV0910_P2_FECSPY_INPUT 0xf3a160c0 +#define FSTV0910_P2_RST_ON_ERROR 0xf3a15020 +#define FSTV0910_P2_ONE_SHOT 0xf3a14010 +#define FSTV0910_P2_I2C_MODE 0xf3a1200c +#define FSTV0910_P2_SPY_HYSTERESIS 0xf3a10003 + +/* P2_FSPYDATA */ +#define RSTV0910_P2_FSPYDATA 0xf3a2 +#define FSTV0910_P2_SPY_STUFFING 0xf3a27080 +#define FSTV0910_P2_SPY_CNULLPKT 0xf3a25020 +#define FSTV0910_P2_SPY_OUTDATA_MODE 0xf3a2001f + +/* P2_FSPYOUT */ +#define RSTV0910_P2_FSPYOUT 0xf3a3 +#define FSTV0910_P2_FSPY_DIRECT 0xf3a37080 +#define FSTV0910_P2_STUFF_MODE 0xf3a30007 + +/* P2_FSTATUS */ +#define RSTV0910_P2_FSTATUS 0xf3a4 +#define FSTV0910_P2_SPY_ENDSIM 0xf3a47080 +#define FSTV0910_P2_VALID_SIM 0xf3a46040 +#define FSTV0910_P2_FOUND_SIGNAL 0xf3a45020 +#define FSTV0910_P2_DSS_SYNCBYTE 0xf3a44010 +#define FSTV0910_P2_RESULT_STATE 0xf3a4000f + +/* P2_FBERCPT4 */ +#define RSTV0910_P2_FBERCPT4 0xf3a8 +#define FSTV0910_P2_FBERMETER_CPT4 0xf3a800ff + +/* P2_FBERCPT3 */ +#define RSTV0910_P2_FBERCPT3 0xf3a9 +#define FSTV0910_P2_FBERMETER_CPT3 0xf3a900ff + +/* P2_FBERCPT2 */ +#define RSTV0910_P2_FBERCPT2 0xf3aa +#define FSTV0910_P2_FBERMETER_CPT2 0xf3aa00ff + +/* P2_FBERCPT1 */ +#define RSTV0910_P2_FBERCPT1 0xf3ab +#define FSTV0910_P2_FBERMETER_CPT1 0xf3ab00ff + +/* P2_FBERCPT0 */ +#define RSTV0910_P2_FBERCPT0 0xf3ac +#define FSTV0910_P2_FBERMETER_CPT0 0xf3ac00ff + +/* P2_FBERERR2 */ +#define RSTV0910_P2_FBERERR2 0xf3ad +#define FSTV0910_P2_FBERMETER_ERR2 0xf3ad00ff + +/* P2_FBERERR1 */ +#define RSTV0910_P2_FBERERR1 0xf3ae +#define FSTV0910_P2_FBERMETER_ERR1 0xf3ae00ff + +/* P2_FBERERR0 */ +#define RSTV0910_P2_FBERERR0 0xf3af +#define FSTV0910_P2_FBERMETER_ERR0 0xf3af00ff + +/* P2_FSPYBER */ +#define RSTV0910_P2_FSPYBER 0xf3b2 +#define FSTV0910_P2_FSPYBER_SYNCBYTE 0xf3b24010 +#define FSTV0910_P2_FSPYBER_UNSYNC 0xf3b23008 +#define FSTV0910_P2_FSPYBER_CTIME 0xf3b20007 + +/* P2_SFERROR */ +#define RSTV0910_P2_SFERROR 0xf3c1 +#define FSTV0910_P2_SFEC_REGERR_VIT 0xf3c100ff + +/* P2_SFECSTATUS */ +#define RSTV0910_P2_SFECSTATUS 0xf3c3 +#define FSTV0910_P2_SFEC_ON 0xf3c37080 +#define FSTV0910_P2_SFEC_OFF 0xf3c36040 +#define FSTV0910_P2_LOCKEDSFEC 0xf3c33008 +#define FSTV0910_P2_SFEC_DELOCK 0xf3c32004 +#define FSTV0910_P2_SFEC_DEMODSEL 0xf3c31002 +#define FSTV0910_P2_SFEC_OVFON 0xf3c30001 + +/* P2_SFKDIV12 */ +#define RSTV0910_P2_SFKDIV12 0xf3c4 +#define FSTV0910_P2_SFECKDIV12_MAN 0xf3c47080 + +/* P2_SFKDIV23 */ +#define RSTV0910_P2_SFKDIV23 0xf3c5 +#define FSTV0910_P2_SFECKDIV23_MAN 0xf3c57080 + +/* P2_SFKDIV34 */ +#define RSTV0910_P2_SFKDIV34 0xf3c6 +#define FSTV0910_P2_SFECKDIV34_MAN 0xf3c67080 + +/* P2_SFKDIV56 */ +#define RSTV0910_P2_SFKDIV56 0xf3c7 +#define FSTV0910_P2_SFECKDIV56_MAN 0xf3c77080 + +/* P2_SFKDIV67 */ +#define RSTV0910_P2_SFKDIV67 0xf3c8 +#define FSTV0910_P2_SFECKDIV67_MAN 0xf3c87080 + +/* P2_SFKDIV78 */ +#define RSTV0910_P2_SFKDIV78 0xf3c9 +#define FSTV0910_P2_SFECKDIV78_MAN 0xf3c97080 + +/* P2_SFSTATUS */ +#define RSTV0910_P2_SFSTATUS 0xf3cc +#define FSTV0910_P2_SFEC_LINEOK 0xf3cc7080 +#define FSTV0910_P2_SFEC_ERROR 0xf3cc6040 +#define FSTV0910_P2_SFEC_DATA7 0xf3cc5020 +#define FSTV0910_P2_SFEC_PKTDNBRFAIL 0xf3cc4010 +#define FSTV0910_P2_TSSFEC_DEMODSEL 0xf3cc3008 +#define FSTV0910_P2_SFEC_NOSYNC 0xf3cc2004 +#define FSTV0910_P2_SFEC_UNREGULA 0xf3cc1002 +#define FSTV0910_P2_SFEC_READY 0xf3cc0001 + +/* P2_SFDLYSET2 */ +#define RSTV0910_P2_SFDLYSET2 0xf3d0 +#define FSTV0910_P2_SFEC_DISABLE 0xf3d01002 + +/* P2_SFERRCTRL */ +#define RSTV0910_P2_SFERRCTRL 0xf3d8 +#define FSTV0910_P2_SFEC_ERR_SOURCE 0xf3d840f0 +#define FSTV0910_P2_SFEC_NUM_EVENT 0xf3d80007 + +/* P2_SFERRCNT2 */ +#define RSTV0910_P2_SFERRCNT2 0xf3d9 +#define FSTV0910_P2_SFERRC_OLDVALUE 0xf3d97080 +#define FSTV0910_P2_SFEC_ERR_CNT2 0xf3d9007f + +/* P2_SFERRCNT1 */ +#define RSTV0910_P2_SFERRCNT1 0xf3da +#define FSTV0910_P2_SFEC_ERR_CNT1 0xf3da00ff + +/* P2_SFERRCNT0 */ +#define RSTV0910_P2_SFERRCNT0 0xf3db +#define FSTV0910_P2_SFEC_ERR_CNT0 0xf3db00ff + +/* P1_IQCONST */ +#define RSTV0910_P1_IQCONST 0xf400 +#define FSTV0910_P1_CONSTEL_SELECT 0xf4005060 +#define FSTV0910_P1_IQSYMB_SEL 0xf400001f + +/* P1_NOSCFG */ +#define RSTV0910_P1_NOSCFG 0xf401 +#define FSTV0910_P1_DUMMYPL_NOSDATA 0xf4015020 +#define FSTV0910_P1_NOSPLH_BETA 0xf4013018 +#define FSTV0910_P1_NOSDATA_BETA 0xf4010007 + +/* P1_ISYMB */ +#define RSTV0910_P1_ISYMB 0xf402 +#define FSTV0910_P1_I_SYMBOL 0xf40201ff + +/* P1_QSYMB */ +#define RSTV0910_P1_QSYMB 0xf403 +#define FSTV0910_P1_Q_SYMBOL 0xf40301ff + +/* P1_AGC1CFG */ +#define RSTV0910_P1_AGC1CFG 0xf404 +#define FSTV0910_P1_DC_FROZEN 0xf4047080 +#define FSTV0910_P1_DC_CORRECT 0xf4046040 +#define FSTV0910_P1_AMM_FROZEN 0xf4045020 +#define FSTV0910_P1_AMM_CORRECT 0xf4044010 +#define FSTV0910_P1_QUAD_FROZEN 0xf4043008 +#define FSTV0910_P1_QUAD_CORRECT 0xf4042004 + +/* P1_AGC1CN */ +#define RSTV0910_P1_AGC1CN 0xf406 +#define FSTV0910_P1_AGC1_LOCKED 0xf4067080 +#define FSTV0910_P1_AGC1_MINPOWER 0xf4064010 +#define FSTV0910_P1_AGCOUT_FAST 0xf4063008 +#define FSTV0910_P1_AGCIQ_BETA 0xf4060007 + +/* P1_AGC1REF */ +#define RSTV0910_P1_AGC1REF 0xf407 +#define FSTV0910_P1_AGCIQ_REF 0xf40700ff + +/* P1_IDCCOMP */ +#define RSTV0910_P1_IDCCOMP 0xf408 +#define FSTV0910_P1_IAVERAGE_ADJ 0xf40801ff + +/* P1_QDCCOMP */ +#define RSTV0910_P1_QDCCOMP 0xf409 +#define FSTV0910_P1_QAVERAGE_ADJ 0xf40901ff + +/* P1_POWERI */ +#define RSTV0910_P1_POWERI 0xf40a +#define FSTV0910_P1_POWER_I 0xf40a00ff + +/* P1_POWERQ */ +#define RSTV0910_P1_POWERQ 0xf40b +#define FSTV0910_P1_POWER_Q 0xf40b00ff + +/* P1_AGC1AMM */ +#define RSTV0910_P1_AGC1AMM 0xf40c +#define FSTV0910_P1_AMM_VALUE 0xf40c00ff + +/* P1_AGC1QUAD */ +#define RSTV0910_P1_AGC1QUAD 0xf40d +#define FSTV0910_P1_QUAD_VALUE 0xf40d01ff + +/* P1_AGCIQIN1 */ +#define RSTV0910_P1_AGCIQIN1 0xf40e +#define FSTV0910_P1_AGCIQ_VALUE1 0xf40e00ff + +/* P1_AGCIQIN0 */ +#define RSTV0910_P1_AGCIQIN0 0xf40f +#define FSTV0910_P1_AGCIQ_VALUE0 0xf40f00ff + +/* P1_DEMOD */ +#define RSTV0910_P1_DEMOD 0xf410 +#define FSTV0910_P1_MANUALS2_ROLLOFF 0xf4107080 +#define FSTV0910_P1_SPECINV_CONTROL 0xf4104030 +#define FSTV0910_P1_MANUALSX_ROLLOFF 0xf4102004 +#define FSTV0910_P1_ROLLOFF_CONTROL 0xf4100003 + +/* P1_DMDMODCOD */ +#define RSTV0910_P1_DMDMODCOD 0xf411 +#define FSTV0910_P1_MANUAL_MODCOD 0xf4117080 +#define FSTV0910_P1_DEMOD_MODCOD 0xf411207c +#define FSTV0910_P1_DEMOD_TYPE 0xf4110003 + +/* P1_DSTATUS */ +#define RSTV0910_P1_DSTATUS 0xf412 +#define FSTV0910_P1_CAR_LOCK 0xf4127080 +#define FSTV0910_P1_TMGLOCK_QUALITY 0xf4125060 +#define FSTV0910_P1_LOCK_DEFINITIF 0xf4123008 +#define FSTV0910_P1_OVADC_DETECT 0xf4120001 + +/* P1_DSTATUS2 */ +#define RSTV0910_P1_DSTATUS2 0xf413 +#define FSTV0910_P1_DEMOD_DELOCK 0xf4137080 +#define FSTV0910_P1_MODCODRQ_SYNCTAG 0xf4135020 +#define FSTV0910_P1_POLYPH_SATEVENT 0xf4134010 +#define FSTV0910_P1_AGC1_NOSIGNALACK 0xf4133008 +#define FSTV0910_P1_AGC2_OVERFLOW 0xf4132004 +#define FSTV0910_P1_CFR_OVERFLOW 0xf4131002 +#define FSTV0910_P1_GAMMA_OVERUNDER 0xf4130001 + +/* P1_DMDCFGMD */ +#define RSTV0910_P1_DMDCFGMD 0xf414 +#define FSTV0910_P1_DVBS2_ENABLE 0xf4147080 +#define FSTV0910_P1_DVBS1_ENABLE 0xf4146040 +#define FSTV0910_P1_SCAN_ENABLE 0xf4144010 +#define FSTV0910_P1_CFR_AUTOSCAN 0xf4143008 +#define FSTV0910_P1_TUN_RNG 0xf4140003 + +/* P1_DMDCFG2 */ +#define RSTV0910_P1_DMDCFG2 0xf415 +#define FSTV0910_P1_S1S2_SEQUENTIAL 0xf4156040 +#define FSTV0910_P1_INFINITE_RELOCK 0xf4154010 + +/* P1_DMDISTATE */ +#define RSTV0910_P1_DMDISTATE 0xf416 +#define FSTV0910_P1_I2C_NORESETDMODE 0xf4167080 +#define FSTV0910_P1_I2C_DEMOD_MODE 0xf416001f + +/* P1_DMDT0M */ +#define RSTV0910_P1_DMDT0M 0xf417 +#define FSTV0910_P1_DMDT0_MIN 0xf41700ff + +/* P1_DMDSTATE */ +#define RSTV0910_P1_DMDSTATE 0xf41b +#define FSTV0910_P1_HEADER_MODE 0xf41b5060 + +/* P1_DMDFLYW */ +#define RSTV0910_P1_DMDFLYW 0xf41c +#define FSTV0910_P1_I2C_IRQVAL 0xf41c40f0 +#define FSTV0910_P1_FLYWHEEL_CPT 0xf41c000f + +/* P1_DSTATUS3 */ +#define RSTV0910_P1_DSTATUS3 0xf41d +#define FSTV0910_P1_CFR_ZIGZAG 0xf41d7080 +#define FSTV0910_P1_DEMOD_CFGMODE 0xf41d5060 +#define FSTV0910_P1_GAMMA_LOWBAUDRATE 0xf41d4010 + +/* P1_DMDCFG3 */ +#define RSTV0910_P1_DMDCFG3 0xf41e +#define FSTV0910_P1_NOSTOP_FIFOFULL 0xf41e3008 + +/* P1_DMDCFG4 */ +#define RSTV0910_P1_DMDCFG4 0xf41f +#define FSTV0910_P1_DIS_VITLOCK 0xf41f7080 +#define FSTV0910_P1_DIS_CLKENABLE 0xf41f2004 + +/* P1_CORRELMANT */ +#define RSTV0910_P1_CORRELMANT 0xf420 +#define FSTV0910_P1_CORREL_MANT 0xf42000ff + +/* P1_CORRELABS */ +#define RSTV0910_P1_CORRELABS 0xf421 +#define FSTV0910_P1_CORREL_ABS 0xf42100ff + +/* P1_CORRELEXP */ +#define RSTV0910_P1_CORRELEXP 0xf422 +#define FSTV0910_P1_CORREL_ABSEXP 0xf42240f0 +#define FSTV0910_P1_CORREL_EXP 0xf422000f + +/* P1_PLHMODCOD */ +#define RSTV0910_P1_PLHMODCOD 0xf424 +#define FSTV0910_P1_SPECINV_DEMOD 0xf4247080 +#define FSTV0910_P1_PLH_MODCOD 0xf424207c +#define FSTV0910_P1_PLH_TYPE 0xf4240003 + +/* P1_DMDREG */ +#define RSTV0910_P1_DMDREG 0xf425 +#define FSTV0910_P1_DECIM_PLFRAMES 0xf4250001 + +/* P1_AGCNADJ */ +#define RSTV0910_P1_AGCNADJ 0xf426 +#define FSTV0910_P1_RADJOFF_AGC2 0xf4267080 +#define FSTV0910_P1_RADJOFF_AGC1 0xf4266040 +#define FSTV0910_P1_AGC_NADJ 0xf426013f + +/* P1_AGCKS */ +#define RSTV0910_P1_AGCKS 0xf427 +#define FSTV0910_P1_RSADJ_MANUALCFG 0xf4277080 +#define FSTV0910_P1_RSADJ_CCMMODE 0xf4276040 +#define FSTV0910_P1_RADJ_SPSK 0xf427013f + +/* P1_AGCKQ */ +#define RSTV0910_P1_AGCKQ 0xf428 +#define FSTV0910_P1_RADJON_DVBS1 0xf4286040 +#define FSTV0910_P1_RADJ_QPSK 0xf428013f + +/* P1_AGCK8 */ +#define RSTV0910_P1_AGCK8 0xf429 +#define FSTV0910_P1_RADJ_8PSK 0xf429013f + +/* P1_AGCK16 */ +#define RSTV0910_P1_AGCK16 0xf42a +#define FSTV0910_P1_R2ADJOFF_16APSK 0xf42a6040 +#define FSTV0910_P1_R1ADJOFF_16APSK 0xf42a5020 +#define FSTV0910_P1_RADJ_16APSK 0xf42a011f + +/* P1_AGCK32 */ +#define RSTV0910_P1_AGCK32 0xf42b +#define FSTV0910_P1_R3ADJOFF_32APSK 0xf42b7080 +#define FSTV0910_P1_R2ADJOFF_32APSK 0xf42b6040 +#define FSTV0910_P1_R1ADJOFF_32APSK 0xf42b5020 +#define FSTV0910_P1_RADJ_32APSK 0xf42b011f + +/* P1_AGC2O */ +#define RSTV0910_P1_AGC2O 0xf42c +#define FSTV0910_P1_CSTENV_MODE 0xf42c60c0 +#define FSTV0910_P1_AGC2_COEF 0xf42c0007 + +/* P1_AGC2REF */ +#define RSTV0910_P1_AGC2REF 0xf42d +#define FSTV0910_P1_AGC2_REF 0xf42d00ff + +/* P1_AGC1ADJ */ +#define RSTV0910_P1_AGC1ADJ 0xf42e +#define FSTV0910_P1_AGC1_ADJUSTED 0xf42e007f + +/* P1_AGCRSADJ */ +#define RSTV0910_P1_AGCRSADJ 0xf42f +#define FSTV0910_P1_RS_ADJUSTED 0xf42f007f + +/* P1_AGCRQADJ */ +#define RSTV0910_P1_AGCRQADJ 0xf430 +#define FSTV0910_P1_RQ_ADJUSTED 0xf430007f + +/* P1_AGCR8ADJ */ +#define RSTV0910_P1_AGCR8ADJ 0xf431 +#define FSTV0910_P1_R8_ADJUSTED 0xf431007f + +/* P1_AGCR1ADJ */ +#define RSTV0910_P1_AGCR1ADJ 0xf432 +#define FSTV0910_P1_R1_ADJUSTED 0xf432007f + +/* P1_AGCR2ADJ */ +#define RSTV0910_P1_AGCR2ADJ 0xf433 +#define FSTV0910_P1_R2_ADJUSTED 0xf433007f + +/* P1_AGCR3ADJ */ +#define RSTV0910_P1_AGCR3ADJ 0xf434 +#define FSTV0910_P1_R3_ADJUSTED 0xf434007f + +/* P1_AGCREFADJ */ +#define RSTV0910_P1_AGCREFADJ 0xf435 +#define FSTV0910_P1_AGC2REF_ADJUSTED 0xf435007f + +/* P1_AGC2I1 */ +#define RSTV0910_P1_AGC2I1 0xf436 +#define FSTV0910_P1_AGC2_INTEGRATOR1 0xf43600ff + +/* P1_AGC2I0 */ +#define RSTV0910_P1_AGC2I0 0xf437 +#define FSTV0910_P1_AGC2_INTEGRATOR0 0xf43700ff + +/* P1_CARCFG */ +#define RSTV0910_P1_CARCFG 0xf438 +#define FSTV0910_P1_ROTAON 0xf4382004 +#define FSTV0910_P1_PH_DET_ALGO 0xf4380003 + +/* P1_ACLC */ +#define RSTV0910_P1_ACLC 0xf439 +#define FSTV0910_P1_CAR_ALPHA_MANT 0xf4394030 +#define FSTV0910_P1_CAR_ALPHA_EXP 0xf439000f + +/* P1_BCLC */ +#define RSTV0910_P1_BCLC 0xf43a +#define FSTV0910_P1_CAR_BETA_MANT 0xf43a4030 +#define FSTV0910_P1_CAR_BETA_EXP 0xf43a000f + +/* P1_ACLCS2 */ +#define RSTV0910_P1_ACLCS2 0xf43b +#define FSTV0910_P1_CARS2_APLHA_MANTISSE 0xf43b4030 +#define FSTV0910_P1_CARS2_ALPHA_EXP 0xf43b000f + +/* P1_BCLCS2 */ +#define RSTV0910_P1_BCLCS2 0xf43c +#define FSTV0910_P1_CARS2_BETA_MANTISSE 0xf43c4030 +#define FSTV0910_P1_CARS2_BETA_EXP 0xf43c000f + +/* P1_CARFREQ */ +#define RSTV0910_P1_CARFREQ 0xf43d +#define FSTV0910_P1_KC_COARSE_EXP 0xf43d40f0 +#define FSTV0910_P1_BETA_FREQ 0xf43d000f + +/* P1_CARHDR */ +#define RSTV0910_P1_CARHDR 0xf43e +#define FSTV0910_P1_K_FREQ_HDR 0xf43e00ff + +/* P1_LDT */ +#define RSTV0910_P1_LDT 0xf43f +#define FSTV0910_P1_CARLOCK_THRES 0xf43f01ff + +/* P1_LDT2 */ +#define RSTV0910_P1_LDT2 0xf440 +#define FSTV0910_P1_CARLOCK_THRES2 0xf44001ff + +/* P1_CFRICFG */ +#define RSTV0910_P1_CFRICFG 0xf441 +#define FSTV0910_P1_NEG_CFRSTEP 0xf4410001 + +/* P1_CFRUP1 */ +#define RSTV0910_P1_CFRUP1 0xf442 +#define FSTV0910_P1_CFR_UP1 0xf44201ff + +/* P1_CFRUP0 */ +#define RSTV0910_P1_CFRUP0 0xf443 +#define FSTV0910_P1_CFR_UP0 0xf44300ff + +/* P1_CFRIBASE1 */ +#define RSTV0910_P1_CFRIBASE1 0xf444 +#define FSTV0910_P1_CFRINIT_BASE1 0xf44400ff + +/* P1_CFRIBASE0 */ +#define RSTV0910_P1_CFRIBASE0 0xf445 +#define FSTV0910_P1_CFRINIT_BASE0 0xf44500ff + +/* P1_CFRLOW1 */ +#define RSTV0910_P1_CFRLOW1 0xf446 +#define FSTV0910_P1_CFR_LOW1 0xf44601ff + +/* P1_CFRLOW0 */ +#define RSTV0910_P1_CFRLOW0 0xf447 +#define FSTV0910_P1_CFR_LOW0 0xf44700ff + +/* P1_CFRINIT1 */ +#define RSTV0910_P1_CFRINIT1 0xf448 +#define FSTV0910_P1_CFR_INIT1 0xf44801ff + +/* P1_CFRINIT0 */ +#define RSTV0910_P1_CFRINIT0 0xf449 +#define FSTV0910_P1_CFR_INIT0 0xf44900ff + +/* P1_CFRINC1 */ +#define RSTV0910_P1_CFRINC1 0xf44a +#define FSTV0910_P1_MANUAL_CFRINC 0xf44a7080 +#define FSTV0910_P1_CFR_INC1 0xf44a003f + +/* P1_CFRINC0 */ +#define RSTV0910_P1_CFRINC0 0xf44b +#define FSTV0910_P1_CFR_INC0 0xf44b00ff + +/* P1_CFR2 */ +#define RSTV0910_P1_CFR2 0xf44c +#define FSTV0910_P1_CAR_FREQ2 0xf44c01ff + +/* P1_CFR1 */ +#define RSTV0910_P1_CFR1 0xf44d +#define FSTV0910_P1_CAR_FREQ1 0xf44d00ff + +/* P1_CFR0 */ +#define RSTV0910_P1_CFR0 0xf44e +#define FSTV0910_P1_CAR_FREQ0 0xf44e00ff + +/* P1_LDI */ +#define RSTV0910_P1_LDI 0xf44f +#define FSTV0910_P1_LOCK_DET_INTEGR 0xf44f01ff + +/* P1_TMGCFG */ +#define RSTV0910_P1_TMGCFG 0xf450 +#define FSTV0910_P1_TMGLOCK_BETA 0xf45060c0 +#define FSTV0910_P1_DO_TIMING_CORR 0xf4504010 +#define FSTV0910_P1_TMG_MINFREQ 0xf4500003 + +/* P1_RTC */ +#define RSTV0910_P1_RTC 0xf451 +#define FSTV0910_P1_TMGALPHA_EXP 0xf45140f0 +#define FSTV0910_P1_TMGBETA_EXP 0xf451000f + +/* P1_RTCS2 */ +#define RSTV0910_P1_RTCS2 0xf452 +#define FSTV0910_P1_TMGALPHAS2_EXP 0xf45240f0 +#define FSTV0910_P1_TMGBETAS2_EXP 0xf452000f + +/* P1_TMGTHRISE */ +#define RSTV0910_P1_TMGTHRISE 0xf453 +#define FSTV0910_P1_TMGLOCK_THRISE 0xf45300ff + +/* P1_TMGTHFALL */ +#define RSTV0910_P1_TMGTHFALL 0xf454 +#define FSTV0910_P1_TMGLOCK_THFALL 0xf45400ff + +/* P1_SFRUPRATIO */ +#define RSTV0910_P1_SFRUPRATIO 0xf455 +#define FSTV0910_P1_SFR_UPRATIO 0xf45500ff + +/* P1_SFRLOWRATIO */ +#define RSTV0910_P1_SFRLOWRATIO 0xf456 +#define FSTV0910_P1_SFR_LOWRATIO 0xf45600ff + +/* P1_KTTMG */ +#define RSTV0910_P1_KTTMG 0xf457 +#define FSTV0910_P1_KT_TMG_EXP 0xf45740f0 + +/* P1_KREFTMG */ +#define RSTV0910_P1_KREFTMG 0xf458 +#define FSTV0910_P1_KREF_TMG 0xf45800ff + +/* P1_SFRSTEP */ +#define RSTV0910_P1_SFRSTEP 0xf459 +#define FSTV0910_P1_SFR_SCANSTEP 0xf45940f0 +#define FSTV0910_P1_SFR_CENTERSTEP 0xf459000f + +/* P1_TMGCFG2 */ +#define RSTV0910_P1_TMGCFG2 0xf45a +#define FSTV0910_P1_DIS_AUTOSAMP 0xf45a3008 +#define FSTV0910_P1_SFRRATIO_FINE 0xf45a0001 + +/* P1_KREFTMG2 */ +#define RSTV0910_P1_KREFTMG2 0xf45b +#define FSTV0910_P1_KREF_TMG2 0xf45b00ff + +/* P1_TMGCFG3 */ +#define RSTV0910_P1_TMGCFG3 0xf45d +#define FSTV0910_P1_CONT_TMGCENTER 0xf45d3008 +#define FSTV0910_P1_AUTO_GUP 0xf45d2004 +#define FSTV0910_P1_AUTO_GLOW 0xf45d1002 + +/* P1_SFRINIT1 */ +#define RSTV0910_P1_SFRINIT1 0xf45e +#define FSTV0910_P1_SFR_INIT1 0xf45e00ff + +/* P1_SFRINIT0 */ +#define RSTV0910_P1_SFRINIT0 0xf45f +#define FSTV0910_P1_SFR_INIT0 0xf45f00ff + +/* P1_SFRUP1 */ +#define RSTV0910_P1_SFRUP1 0xf460 +#define FSTV0910_P1_SYMB_FREQ_UP1 0xf46000ff + +/* P1_SFRUP0 */ +#define RSTV0910_P1_SFRUP0 0xf461 +#define FSTV0910_P1_SYMB_FREQ_UP0 0xf46100ff + +/* P1_SFRLOW1 */ +#define RSTV0910_P1_SFRLOW1 0xf462 +#define FSTV0910_P1_SYMB_FREQ_LOW1 0xf46200ff + +/* P1_SFRLOW0 */ +#define RSTV0910_P1_SFRLOW0 0xf463 +#define FSTV0910_P1_SYMB_FREQ_LOW0 0xf46300ff + +/* P1_SFR3 */ +#define RSTV0910_P1_SFR3 0xf464 +#define FSTV0910_P1_SYMB_FREQ3 0xf46400ff + +/* P1_SFR2 */ +#define RSTV0910_P1_SFR2 0xf465 +#define FSTV0910_P1_SYMB_FREQ2 0xf46500ff + +/* P1_SFR1 */ +#define RSTV0910_P1_SFR1 0xf466 +#define FSTV0910_P1_SYMB_FREQ1 0xf46600ff + +/* P1_SFR0 */ +#define RSTV0910_P1_SFR0 0xf467 +#define FSTV0910_P1_SYMB_FREQ0 0xf46700ff + +/* P1_TMGREG2 */ +#define RSTV0910_P1_TMGREG2 0xf468 +#define FSTV0910_P1_TMGREG2 0xf46800ff + +/* P1_TMGREG1 */ +#define RSTV0910_P1_TMGREG1 0xf469 +#define FSTV0910_P1_TMGREG1 0xf46900ff + +/* P1_TMGREG0 */ +#define RSTV0910_P1_TMGREG0 0xf46a +#define FSTV0910_P1_TMGREG0 0xf46a00ff + +/* P1_TMGLOCK1 */ +#define RSTV0910_P1_TMGLOCK1 0xf46b +#define FSTV0910_P1_TMGLOCK_LEVEL1 0xf46b01ff + +/* P1_TMGLOCK0 */ +#define RSTV0910_P1_TMGLOCK0 0xf46c +#define FSTV0910_P1_TMGLOCK_LEVEL0 0xf46c00ff + +/* P1_TMGOBS */ +#define RSTV0910_P1_TMGOBS 0xf46d +#define FSTV0910_P1_ROLLOFF_STATUS 0xf46d60c0 + +/* P1_EQUALCFG */ +#define RSTV0910_P1_EQUALCFG 0xf46f +#define FSTV0910_P1_EQUAL_ON 0xf46f6040 +#define FSTV0910_P1_MU_EQUALDFE 0xf46f0007 + +/* P1_EQUAI1 */ +#define RSTV0910_P1_EQUAI1 0xf470 +#define FSTV0910_P1_EQUA_ACCI1 0xf47001ff + +/* P1_EQUAQ1 */ +#define RSTV0910_P1_EQUAQ1 0xf471 +#define FSTV0910_P1_EQUA_ACCQ1 0xf47101ff + +/* P1_EQUAI2 */ +#define RSTV0910_P1_EQUAI2 0xf472 +#define FSTV0910_P1_EQUA_ACCI2 0xf47201ff + +/* P1_EQUAQ2 */ +#define RSTV0910_P1_EQUAQ2 0xf473 +#define FSTV0910_P1_EQUA_ACCQ2 0xf47301ff + +/* P1_EQUAI3 */ +#define RSTV0910_P1_EQUAI3 0xf474 +#define FSTV0910_P1_EQUA_ACCI3 0xf47401ff + +/* P1_EQUAQ3 */ +#define RSTV0910_P1_EQUAQ3 0xf475 +#define FSTV0910_P1_EQUA_ACCQ3 0xf47501ff + +/* P1_EQUAI4 */ +#define RSTV0910_P1_EQUAI4 0xf476 +#define FSTV0910_P1_EQUA_ACCI4 0xf47601ff + +/* P1_EQUAQ4 */ +#define RSTV0910_P1_EQUAQ4 0xf477 +#define FSTV0910_P1_EQUA_ACCQ4 0xf47701ff + +/* P1_EQUAI5 */ +#define RSTV0910_P1_EQUAI5 0xf478 +#define FSTV0910_P1_EQUA_ACCI5 0xf47801ff + +/* P1_EQUAQ5 */ +#define RSTV0910_P1_EQUAQ5 0xf479 +#define FSTV0910_P1_EQUA_ACCQ5 0xf47901ff + +/* P1_EQUAI6 */ +#define RSTV0910_P1_EQUAI6 0xf47a +#define FSTV0910_P1_EQUA_ACCI6 0xf47a01ff + +/* P1_EQUAQ6 */ +#define RSTV0910_P1_EQUAQ6 0xf47b +#define FSTV0910_P1_EQUA_ACCQ6 0xf47b01ff + +/* P1_EQUAI7 */ +#define RSTV0910_P1_EQUAI7 0xf47c +#define FSTV0910_P1_EQUA_ACCI7 0xf47c01ff + +/* P1_EQUAQ7 */ +#define RSTV0910_P1_EQUAQ7 0xf47d +#define FSTV0910_P1_EQUA_ACCQ7 0xf47d01ff + +/* P1_EQUAI8 */ +#define RSTV0910_P1_EQUAI8 0xf47e +#define FSTV0910_P1_EQUA_ACCI8 0xf47e01ff + +/* P1_EQUAQ8 */ +#define RSTV0910_P1_EQUAQ8 0xf47f +#define FSTV0910_P1_EQUA_ACCQ8 0xf47f01ff + +/* P1_NNOSDATAT1 */ +#define RSTV0910_P1_NNOSDATAT1 0xf480 +#define FSTV0910_P1_NOSDATAT_NORMED1 0xf48000ff + +/* P1_NNOSDATAT0 */ +#define RSTV0910_P1_NNOSDATAT0 0xf481 +#define FSTV0910_P1_NOSDATAT_NORMED0 0xf48100ff + +/* P1_NNOSDATA1 */ +#define RSTV0910_P1_NNOSDATA1 0xf482 +#define FSTV0910_P1_NOSDATA_NORMED1 0xf48200ff + +/* P1_NNOSDATA0 */ +#define RSTV0910_P1_NNOSDATA0 0xf483 +#define FSTV0910_P1_NOSDATA_NORMED0 0xf48300ff + +/* P1_NNOSPLHT1 */ +#define RSTV0910_P1_NNOSPLHT1 0xf484 +#define FSTV0910_P1_NOSPLHT_NORMED1 0xf48400ff + +/* P1_NNOSPLHT0 */ +#define RSTV0910_P1_NNOSPLHT0 0xf485 +#define FSTV0910_P1_NOSPLHT_NORMED0 0xf48500ff + +/* P1_NNOSPLH1 */ +#define RSTV0910_P1_NNOSPLH1 0xf486 +#define FSTV0910_P1_NOSPLH_NORMED1 0xf48600ff + +/* P1_NNOSPLH0 */ +#define RSTV0910_P1_NNOSPLH0 0xf487 +#define FSTV0910_P1_NOSPLH_NORMED0 0xf48700ff + +/* P1_NOSDATAT1 */ +#define RSTV0910_P1_NOSDATAT1 0xf488 +#define FSTV0910_P1_NOSDATAT_UNNORMED1 0xf48800ff + +/* P1_NOSDATAT0 */ +#define RSTV0910_P1_NOSDATAT0 0xf489 +#define FSTV0910_P1_NOSDATAT_UNNORMED0 0xf48900ff + +/* P1_NNOSFRAME1 */ +#define RSTV0910_P1_NNOSFRAME1 0xf48a +#define FSTV0910_P1_NOSFRAME_NORMED1 0xf48a00ff + +/* P1_NNOSFRAME0 */ +#define RSTV0910_P1_NNOSFRAME0 0xf48b +#define FSTV0910_P1_NOSFRAME_NORMED0 0xf48b00ff + +/* P1_NNOSRAD1 */ +#define RSTV0910_P1_NNOSRAD1 0xf48c +#define FSTV0910_P1_NOSRADIAL_NORMED1 0xf48c00ff + +/* P1_NNOSRAD0 */ +#define RSTV0910_P1_NNOSRAD0 0xf48d +#define FSTV0910_P1_NOSRADIAL_NORMED0 0xf48d00ff + +/* P1_NOSCFGF1 */ +#define RSTV0910_P1_NOSCFGF1 0xf48e +#define FSTV0910_P1_LOWNOISE_MESURE 0xf48e7080 +#define FSTV0910_P1_NOS_DELFRAME 0xf48e6040 +#define FSTV0910_P1_NOSDATA_MODE 0xf48e4030 +#define FSTV0910_P1_FRAMESEL_TYPESEL 0xf48e200c +#define FSTV0910_P1_FRAMESEL_TYPE 0xf48e0003 + +/* P1_NOSCFGF2 */ +#define RSTV0910_P1_NOSCFGF2 0xf48f +#define FSTV0910_P1_DIS_NOSPILOTS 0xf48f7080 +#define FSTV0910_P1_FRAMESEL_MODCODSEL 0xf48f5060 +#define FSTV0910_P1_FRAMESEL_MODCOD 0xf48f001f + +/* P1_CAR2CFG */ +#define RSTV0910_P1_CAR2CFG 0xf490 +#define FSTV0910_P1_ROTA2ON 0xf4902004 +#define FSTV0910_P1_PH_DET_ALGO2 0xf4900003 + +/* P1_CFR2CFR1 */ +#define RSTV0910_P1_CFR2CFR1 0xf491 +#define FSTV0910_P1_EN_S2CAR2CENTER 0xf4915020 +#define FSTV0910_P1_CFR2TOCFR1_BETA 0xf4910007 + +/* P1_CAR3CFG */ +#define RSTV0910_P1_CAR3CFG 0xf492 +#define FSTV0910_P1_CARRIER23_MODE 0xf49260c0 +#define FSTV0910_P1_CAR3INTERM_DVBS1 0xf4925020 +#define FSTV0910_P1_ABAMPLIF_MODE 0xf4923018 +#define FSTV0910_P1_CARRIER3_ALPHA3DL 0xf4920007 + +/* P1_CFR22 */ +#define RSTV0910_P1_CFR22 0xf493 +#define FSTV0910_P1_CAR2_FREQ2 0xf49301ff + +/* P1_CFR21 */ +#define RSTV0910_P1_CFR21 0xf494 +#define FSTV0910_P1_CAR2_FREQ1 0xf49400ff + +/* P1_CFR20 */ +#define RSTV0910_P1_CFR20 0xf495 +#define FSTV0910_P1_CAR2_FREQ0 0xf49500ff + +/* P1_ACLC2S2Q */ +#define RSTV0910_P1_ACLC2S2Q 0xf497 +#define FSTV0910_P1_ENAB_SPSKSYMB 0xf4977080 +#define FSTV0910_P1_CAR2S2_Q_ALPH_M 0xf4974030 +#define FSTV0910_P1_CAR2S2_Q_ALPH_E 0xf497000f + +/* P1_ACLC2S28 */ +#define RSTV0910_P1_ACLC2S28 0xf498 +#define FSTV0910_P1_CAR2S2_8_ALPH_M 0xf4984030 +#define FSTV0910_P1_CAR2S2_8_ALPH_E 0xf498000f + +/* P1_ACLC2S216A */ +#define RSTV0910_P1_ACLC2S216A 0xf499 +#define FSTV0910_P1_CAR2S2_16A_ALPH_M 0xf4994030 +#define FSTV0910_P1_CAR2S2_16A_ALPH_E 0xf499000f + +/* P1_ACLC2S232A */ +#define RSTV0910_P1_ACLC2S232A 0xf49a +#define FSTV0910_P1_CAR2S2_32A_ALPH_M 0xf49a4030 +#define FSTV0910_P1_CAR2S2_32A_ALPH_E 0xf49a000f + +/* P1_BCLC2S2Q */ +#define RSTV0910_P1_BCLC2S2Q 0xf49c +#define FSTV0910_P1_CAR2S2_Q_BETA_M 0xf49c4030 +#define FSTV0910_P1_CAR2S2_Q_BETA_E 0xf49c000f + +/* P1_BCLC2S28 */ +#define RSTV0910_P1_BCLC2S28 0xf49d +#define FSTV0910_P1_CAR2S2_8_BETA_M 0xf49d4030 +#define FSTV0910_P1_CAR2S2_8_BETA_E 0xf49d000f + +/* P1_BCLC2S216A */ +#define RSTV0910_P1_BCLC2S216A 0xf49e +#define FSTV0910_P1_DVBS2S216A_NIP 0xf49e7080 +#define FSTV0910_P1_CAR2S2_16A_BETA_M 0xf49e4030 +#define FSTV0910_P1_CAR2S2_16A_BETA_E 0xf49e000f + +/* P1_BCLC2S232A */ +#define RSTV0910_P1_BCLC2S232A 0xf49f +#define FSTV0910_P1_DVBS2S232A_NIP 0xf49f7080 +#define FSTV0910_P1_CAR2S2_32A_BETA_M 0xf49f4030 +#define FSTV0910_P1_CAR2S2_32A_BETA_E 0xf49f000f + +/* P1_PLROOT2 */ +#define RSTV0910_P1_PLROOT2 0xf4ac +#define FSTV0910_P1_PLSCRAMB_MODE 0xf4ac200c +#define FSTV0910_P1_PLSCRAMB_ROOT2 0xf4ac0003 + +/* P1_PLROOT1 */ +#define RSTV0910_P1_PLROOT1 0xf4ad +#define FSTV0910_P1_PLSCRAMB_ROOT1 0xf4ad00ff + +/* P1_PLROOT0 */ +#define RSTV0910_P1_PLROOT0 0xf4ae +#define FSTV0910_P1_PLSCRAMB_ROOT0 0xf4ae00ff + +/* P1_MODCODLST0 */ +#define RSTV0910_P1_MODCODLST0 0xf4b0 +#define FSTV0910_P1_NACCES_MODCODCH 0xf4b00001 + +/* P1_MODCODLST1 */ +#define RSTV0910_P1_MODCODLST1 0xf4b1 +#define FSTV0910_P1_SYMBRATE_FILTER 0xf4b13008 +#define FSTV0910_P1_NRESET_MODCODLST 0xf4b12004 +#define FSTV0910_P1_DIS_32PSK_9_10 0xf4b10003 + +/* P1_MODCODLST2 */ +#define RSTV0910_P1_MODCODLST2 0xf4b2 +#define FSTV0910_P1_DIS_32PSK_8_9 0xf4b240f0 +#define FSTV0910_P1_DIS_32PSK_5_6 0xf4b2000f + +/* P1_MODCODLST3 */ +#define RSTV0910_P1_MODCODLST3 0xf4b3 +#define FSTV0910_P1_DIS_32PSK_4_5 0xf4b340f0 +#define FSTV0910_P1_DIS_32PSK_3_4 0xf4b3000f + +/* P1_MODCODLST4 */ +#define RSTV0910_P1_MODCODLST4 0xf4b4 +#define FSTV0910_P1_DUMMYPL_PILOT 0xf4b47080 +#define FSTV0910_P1_DUMMYPL_NOPILOT 0xf4b46040 +#define FSTV0910_P1_DIS_16PSK_9_10 0xf4b44030 +#define FSTV0910_P1_DIS_16PSK_8_9 0xf4b4000f + +/* P1_MODCODLST5 */ +#define RSTV0910_P1_MODCODLST5 0xf4b5 +#define FSTV0910_P1_DIS_16PSK_5_6 0xf4b540f0 +#define FSTV0910_P1_DIS_16PSK_4_5 0xf4b5000f + +/* P1_MODCODLST6 */ +#define RSTV0910_P1_MODCODLST6 0xf4b6 +#define FSTV0910_P1_DIS_16PSK_3_4 0xf4b640f0 +#define FSTV0910_P1_DIS_16PSK_2_3 0xf4b6000f + +/* P1_MODCODLST7 */ +#define RSTV0910_P1_MODCODLST7 0xf4b7 +#define FSTV0910_P1_MODCOD_NNOSFILTER 0xf4b77080 +#define FSTV0910_P1_DIS_8PSK_9_10 0xf4b74030 +#define FSTV0910_P1_DIS_8PSK_8_9 0xf4b7000f + +/* P1_MODCODLST8 */ +#define RSTV0910_P1_MODCODLST8 0xf4b8 +#define FSTV0910_P1_DIS_8PSK_5_6 0xf4b840f0 +#define FSTV0910_P1_DIS_8PSK_3_4 0xf4b8000f + +/* P1_MODCODLST9 */ +#define RSTV0910_P1_MODCODLST9 0xf4b9 +#define FSTV0910_P1_DIS_8PSK_2_3 0xf4b940f0 +#define FSTV0910_P1_DIS_8PSK_3_5 0xf4b9000f + +/* P1_MODCODLSTA */ +#define RSTV0910_P1_MODCODLSTA 0xf4ba +#define FSTV0910_P1_NOSFILTER_LIMITE 0xf4ba7080 +#define FSTV0910_P1_DIS_QPSK_9_10 0xf4ba4030 +#define FSTV0910_P1_DIS_QPSK_8_9 0xf4ba000f + +/* P1_MODCODLSTB */ +#define RSTV0910_P1_MODCODLSTB 0xf4bb +#define FSTV0910_P1_DIS_QPSK_5_6 0xf4bb40f0 +#define FSTV0910_P1_DIS_QPSK_4_5 0xf4bb000f + +/* P1_MODCODLSTC */ +#define RSTV0910_P1_MODCODLSTC 0xf4bc +#define FSTV0910_P1_DIS_QPSK_3_4 0xf4bc40f0 +#define FSTV0910_P1_DIS_QPSK_2_3 0xf4bc000f + +/* P1_MODCODLSTD */ +#define RSTV0910_P1_MODCODLSTD 0xf4bd +#define FSTV0910_P1_DIS_QPSK_3_5 0xf4bd40f0 +#define FSTV0910_P1_DIS_QPSK_1_2 0xf4bd000f + +/* P1_MODCODLSTE */ +#define RSTV0910_P1_MODCODLSTE 0xf4be +#define FSTV0910_P1_DIS_QPSK_2_5 0xf4be40f0 +#define FSTV0910_P1_DIS_QPSK_1_3 0xf4be000f + +/* P1_MODCODLSTF */ +#define RSTV0910_P1_MODCODLSTF 0xf4bf +#define FSTV0910_P1_DIS_QPSK_1_4 0xf4bf40f0 +#define FSTV0910_P1_DEMOD_INVMODLST 0xf4bf3008 +#define FSTV0910_P1_DEMODOUT_ENABLE 0xf4bf2004 +#define FSTV0910_P1_DDEMOD_NSET 0xf4bf1002 +#define FSTV0910_P1_MODCOD_NSTOCK 0xf4bf0001 + +/* P1_GAUSSR0 */ +#define RSTV0910_P1_GAUSSR0 0xf4c0 +#define FSTV0910_P1_EN_CCIMODE 0xf4c07080 +#define FSTV0910_P1_R0_GAUSSIEN 0xf4c0007f + +/* P1_CCIR0 */ +#define RSTV0910_P1_CCIR0 0xf4c1 +#define FSTV0910_P1_CCIDETECT_PLHONLY 0xf4c17080 +#define FSTV0910_P1_R0_CCI 0xf4c1007f + +/* P1_CCIQUANT */ +#define RSTV0910_P1_CCIQUANT 0xf4c2 +#define FSTV0910_P1_CCI_BETA 0xf4c250e0 +#define FSTV0910_P1_CCI_QUANT 0xf4c2001f + +/* P1_CCITHRES */ +#define RSTV0910_P1_CCITHRES 0xf4c3 +#define FSTV0910_P1_CCI_THRESHOLD 0xf4c300ff + +/* P1_CCIACC */ +#define RSTV0910_P1_CCIACC 0xf4c4 +#define FSTV0910_P1_CCI_VALUE 0xf4c400ff + +/* P1_DSTATUS4 */ +#define RSTV0910_P1_DSTATUS4 0xf4c5 +#define FSTV0910_P1_RAINFADE_DETECT 0xf4c57080 +#define FSTV0910_P1_NOTHRES2_FAIL 0xf4c56040 +#define FSTV0910_P1_NOTHRES1_FAIL 0xf4c55020 +#define FSTV0910_P1_DMDPROG_ERROR 0xf4c52004 +#define FSTV0910_P1_CSTENV_DETECT 0xf4c51002 +#define FSTV0910_P1_DETECTION_TRIAX 0xf4c50001 + +/* P1_DMDRESCFG */ +#define RSTV0910_P1_DMDRESCFG 0xf4c6 +#define FSTV0910_P1_DMDRES_RESET 0xf4c67080 +#define FSTV0910_P1_DMDRES_STRALL 0xf4c63008 +#define FSTV0910_P1_DMDRES_NEWONLY 0xf4c62004 +#define FSTV0910_P1_DMDRES_NOSTORE 0xf4c61002 + +/* P1_DMDRESADR */ +#define RSTV0910_P1_DMDRESADR 0xf4c7 +#define FSTV0910_P1_DMDRES_VALIDCFR 0xf4c76040 +#define FSTV0910_P1_DMDRES_MEMFULL 0xf4c74030 +#define FSTV0910_P1_DMDRES_RESNBR 0xf4c7000f + +/* P1_DMDRESDATA7 */ +#define RSTV0910_P1_DMDRESDATA7 0xf4c8 +#define FSTV0910_P1_DMDRES_DATA7 0xf4c800ff + +/* P1_DMDRESDATA6 */ +#define RSTV0910_P1_DMDRESDATA6 0xf4c9 +#define FSTV0910_P1_DMDRES_DATA6 0xf4c900ff + +/* P1_DMDRESDATA5 */ +#define RSTV0910_P1_DMDRESDATA5 0xf4ca +#define FSTV0910_P1_DMDRES_DATA5 0xf4ca00ff + +/* P1_DMDRESDATA4 */ +#define RSTV0910_P1_DMDRESDATA4 0xf4cb +#define FSTV0910_P1_DMDRES_DATA4 0xf4cb00ff + +/* P1_DMDRESDATA3 */ +#define RSTV0910_P1_DMDRESDATA3 0xf4cc +#define FSTV0910_P1_DMDRES_DATA3 0xf4cc00ff + +/* P1_DMDRESDATA2 */ +#define RSTV0910_P1_DMDRESDATA2 0xf4cd +#define FSTV0910_P1_DMDRES_DATA2 0xf4cd00ff + +/* P1_DMDRESDATA1 */ +#define RSTV0910_P1_DMDRESDATA1 0xf4ce +#define FSTV0910_P1_DMDRES_DATA1 0xf4ce00ff + +/* P1_DMDRESDATA0 */ +#define RSTV0910_P1_DMDRESDATA0 0xf4cf +#define FSTV0910_P1_DMDRES_DATA0 0xf4cf00ff + +/* P1_FFEI1 */ +#define RSTV0910_P1_FFEI1 0xf4d0 +#define FSTV0910_P1_FFE_ACCI1 0xf4d001ff + +/* P1_FFEQ1 */ +#define RSTV0910_P1_FFEQ1 0xf4d1 +#define FSTV0910_P1_FFE_ACCQ1 0xf4d101ff + +/* P1_FFEI2 */ +#define RSTV0910_P1_FFEI2 0xf4d2 +#define FSTV0910_P1_FFE_ACCI2 0xf4d201ff + +/* P1_FFEQ2 */ +#define RSTV0910_P1_FFEQ2 0xf4d3 +#define FSTV0910_P1_FFE_ACCQ2 0xf4d301ff + +/* P1_FFEI3 */ +#define RSTV0910_P1_FFEI3 0xf4d4 +#define FSTV0910_P1_FFE_ACCI3 0xf4d401ff + +/* P1_FFEQ3 */ +#define RSTV0910_P1_FFEQ3 0xf4d5 +#define FSTV0910_P1_FFE_ACCQ3 0xf4d501ff + +/* P1_FFEI4 */ +#define RSTV0910_P1_FFEI4 0xf4d6 +#define FSTV0910_P1_FFE_ACCI4 0xf4d601ff + +/* P1_FFEQ4 */ +#define RSTV0910_P1_FFEQ4 0xf4d7 +#define FSTV0910_P1_FFE_ACCQ4 0xf4d701ff + +/* P1_FFECFG */ +#define RSTV0910_P1_FFECFG 0xf4d8 +#define FSTV0910_P1_EQUALFFE_ON 0xf4d86040 +#define FSTV0910_P1_EQUAL_USEDSYMB 0xf4d84030 +#define FSTV0910_P1_MU_EQUALFFE 0xf4d80007 + +/* P1_TNRCFG2 */ +#define RSTV0910_P1_TNRCFG2 0xf4e1 +#define FSTV0910_P1_TUN_IQSWAP 0xf4e17080 + +/* P1_SMAPCOEF7 */ +#define RSTV0910_P1_SMAPCOEF7 0xf500 +#define FSTV0910_P1_DIS_QSCALE 0xf5007080 +#define FSTV0910_P1_SMAPCOEF_Q_LLR12 0xf500017f + +/* P1_SMAPCOEF6 */ +#define RSTV0910_P1_SMAPCOEF6 0xf501 +#define FSTV0910_P1_DIS_AGC2SCALE 0xf5017080 +#define FSTV0910_P1_ADJ_8PSKLLR1 0xf5012004 +#define FSTV0910_P1_OLD_8PSKLLR1 0xf5011002 +#define FSTV0910_P1_DIS_AB8PSK 0xf5010001 + +/* P1_SMAPCOEF5 */ +#define RSTV0910_P1_SMAPCOEF5 0xf502 +#define FSTV0910_P1_DIS_8SCALE 0xf5027080 +#define FSTV0910_P1_SMAPCOEF_8P_LLR23 0xf502017f + +/* P1_SMAPCOEF4 */ +#define RSTV0910_P1_SMAPCOEF4 0xf503 +#define FSTV0910_P1_SMAPCOEF_16APSK_LLR12 0xf503017f + +/* P1_SMAPCOEF3 */ +#define RSTV0910_P1_SMAPCOEF3 0xf504 +#define FSTV0910_P1_SMAPCOEF_16APSK_LLR34 0xf504017f + +/* P1_SMAPCOEF2 */ +#define RSTV0910_P1_SMAPCOEF2 0xf505 +#define FSTV0910_P1_SMAPCOEF_32APSK_R2R3 0xf50541f0 +#define FSTV0910_P1_SMAPCOEF_32APSK_LLR2 0xf505010f + +/* P1_SMAPCOEF1 */ +#define RSTV0910_P1_SMAPCOEF1 0xf506 +#define FSTV0910_P1_DIS_16SCALE 0xf5067080 +#define FSTV0910_P1_SMAPCOEF_32_LLR34 0xf506017f + +/* P1_SMAPCOEF0 */ +#define RSTV0910_P1_SMAPCOEF0 0xf507 +#define FSTV0910_P1_DIS_32SCALE 0xf5077080 +#define FSTV0910_P1_SMAPCOEF_32_LLR15 0xf507017f + +/* P1_NOSTHRES1 */ +#define RSTV0910_P1_NOSTHRES1 0xf509 +#define FSTV0910_P1_NOS_THRESHOLD1 0xf50900ff + +/* P1_NOSTHRES2 */ +#define RSTV0910_P1_NOSTHRES2 0xf50a +#define FSTV0910_P1_NOS_THRESHOLD2 0xf50a00ff + +/* P1_NOSDIFF1 */ +#define RSTV0910_P1_NOSDIFF1 0xf50b +#define FSTV0910_P1_NOSTHRES1_DIFF 0xf50b00ff + +/* P1_RAINFADE */ +#define RSTV0910_P1_RAINFADE 0xf50c +#define FSTV0910_P1_NOSTHRES_DATAT 0xf50c7080 +#define FSTV0910_P1_RAINFADE_CNLIMIT 0xf50c4070 +#define FSTV0910_P1_RAINFADE_TIMEOUT 0xf50c0007 + +/* P1_NOSRAMCFG */ +#define RSTV0910_P1_NOSRAMCFG 0xf50d +#define FSTV0910_P1_NOSRAM_ACTIVATION 0xf50d4030 +#define FSTV0910_P1_NOSRAM_CNRONLY 0xf50d3008 +#define FSTV0910_P1_NOSRAM_LGNCNR1 0xf50d0007 + +/* P1_NOSRAMPOS */ +#define RSTV0910_P1_NOSRAMPOS 0xf50e +#define FSTV0910_P1_NOSRAM_LGNCNR0 0xf50e40f0 +#define FSTV0910_P1_NOSRAM_VALIDE 0xf50e2004 +#define FSTV0910_P1_NOSRAM_CNRVAL1 0xf50e0003 + +/* P1_NOSRAMVAL */ +#define RSTV0910_P1_NOSRAMVAL 0xf50f +#define FSTV0910_P1_NOSRAM_CNRVAL0 0xf50f00ff + +/* P1_DMDPLHSTAT */ +#define RSTV0910_P1_DMDPLHSTAT 0xf520 +#define FSTV0910_P1_PLH_STATISTIC 0xf52000ff + +/* P1_LOCKTIME3 */ +#define RSTV0910_P1_LOCKTIME3 0xf522 +#define FSTV0910_P1_DEMOD_LOCKTIME3 0xf52200ff + +/* P1_LOCKTIME2 */ +#define RSTV0910_P1_LOCKTIME2 0xf523 +#define FSTV0910_P1_DEMOD_LOCKTIME2 0xf52300ff + +/* P1_LOCKTIME1 */ +#define RSTV0910_P1_LOCKTIME1 0xf524 +#define FSTV0910_P1_DEMOD_LOCKTIME1 0xf52400ff + +/* P1_LOCKTIME0 */ +#define RSTV0910_P1_LOCKTIME0 0xf525 +#define FSTV0910_P1_DEMOD_LOCKTIME0 0xf52500ff + +/* P1_VITSCALE */ +#define RSTV0910_P1_VITSCALE 0xf532 +#define FSTV0910_P1_NVTH_NOSRANGE 0xf5327080 +#define FSTV0910_P1_VERROR_MAXMODE 0xf5326040 +#define FSTV0910_P1_NSLOWSN_LOCKED 0xf5323008 +#define FSTV0910_P1_DIS_RSFLOCK 0xf5321002 + +/* P1_FECM */ +#define RSTV0910_P1_FECM 0xf533 +#define FSTV0910_P1_DSS_DVB 0xf5337080 +#define FSTV0910_P1_DSS_SRCH 0xf5334010 +#define FSTV0910_P1_SYNCVIT 0xf5331002 +#define FSTV0910_P1_IQINV 0xf5330001 + +/* P1_VTH12 */ +#define RSTV0910_P1_VTH12 0xf534 +#define FSTV0910_P1_VTH12 0xf53400ff + +/* P1_VTH23 */ +#define RSTV0910_P1_VTH23 0xf535 +#define FSTV0910_P1_VTH23 0xf53500ff + +/* P1_VTH34 */ +#define RSTV0910_P1_VTH34 0xf536 +#define FSTV0910_P1_VTH34 0xf53600ff + +/* P1_VTH56 */ +#define RSTV0910_P1_VTH56 0xf537 +#define FSTV0910_P1_VTH56 0xf53700ff + +/* P1_VTH67 */ +#define RSTV0910_P1_VTH67 0xf538 +#define FSTV0910_P1_VTH67 0xf53800ff + +/* P1_VTH78 */ +#define RSTV0910_P1_VTH78 0xf539 +#define FSTV0910_P1_VTH78 0xf53900ff + +/* P1_VITCURPUN */ +#define RSTV0910_P1_VITCURPUN 0xf53a +#define FSTV0910_P1_VIT_CURPUN 0xf53a001f + +/* P1_VERROR */ +#define RSTV0910_P1_VERROR 0xf53b +#define FSTV0910_P1_REGERR_VIT 0xf53b00ff + +/* P1_PRVIT */ +#define RSTV0910_P1_PRVIT 0xf53c +#define FSTV0910_P1_DIS_VTHLOCK 0xf53c6040 +#define FSTV0910_P1_E7_8VIT 0xf53c5020 +#define FSTV0910_P1_E6_7VIT 0xf53c4010 +#define FSTV0910_P1_E5_6VIT 0xf53c3008 +#define FSTV0910_P1_E3_4VIT 0xf53c2004 +#define FSTV0910_P1_E2_3VIT 0xf53c1002 +#define FSTV0910_P1_E1_2VIT 0xf53c0001 + +/* P1_VAVSRVIT */ +#define RSTV0910_P1_VAVSRVIT 0xf53d +#define FSTV0910_P1_AMVIT 0xf53d7080 +#define FSTV0910_P1_FROZENVIT 0xf53d6040 +#define FSTV0910_P1_SNVIT 0xf53d4030 +#define FSTV0910_P1_TOVVIT 0xf53d200c +#define FSTV0910_P1_HYPVIT 0xf53d0003 + +/* P1_VSTATUSVIT */ +#define RSTV0910_P1_VSTATUSVIT 0xf53e +#define FSTV0910_P1_PRFVIT 0xf53e4010 +#define FSTV0910_P1_LOCKEDVIT 0xf53e3008 + +/* P1_VTHINUSE */ +#define RSTV0910_P1_VTHINUSE 0xf53f +#define FSTV0910_P1_VIT_INUSE 0xf53f00ff + +/* P1_KDIV12 */ +#define RSTV0910_P1_KDIV12 0xf540 +#define FSTV0910_P1_K_DIVIDER_12 0xf540007f + +/* P1_KDIV23 */ +#define RSTV0910_P1_KDIV23 0xf541 +#define FSTV0910_P1_K_DIVIDER_23 0xf541007f + +/* P1_KDIV34 */ +#define RSTV0910_P1_KDIV34 0xf542 +#define FSTV0910_P1_K_DIVIDER_34 0xf542007f + +/* P1_KDIV56 */ +#define RSTV0910_P1_KDIV56 0xf543 +#define FSTV0910_P1_K_DIVIDER_56 0xf543007f + +/* P1_KDIV67 */ +#define RSTV0910_P1_KDIV67 0xf544 +#define FSTV0910_P1_K_DIVIDER_67 0xf544007f + +/* P1_KDIV78 */ +#define RSTV0910_P1_KDIV78 0xf545 +#define FSTV0910_P1_K_DIVIDER_78 0xf545007f + +/* P1_TSPIDFLT1 */ +#define RSTV0910_P1_TSPIDFLT1 0xf546 +#define FSTV0910_P1_PIDFLT_ADDR 0xf54600ff + +/* P1_TSPIDFLT0 */ +#define RSTV0910_P1_TSPIDFLT0 0xf547 +#define FSTV0910_P1_PIDFLT_DATA 0xf54700ff + +/* P1_PDELCTRL0 */ +#define RSTV0910_P1_PDELCTRL0 0xf54f +#define FSTV0910_P1_ISIOBS_MODE 0xf54f4030 + +/* P1_PDELCTRL1 */ +#define RSTV0910_P1_PDELCTRL1 0xf550 +#define FSTV0910_P1_INV_MISMASK 0xf5507080 +#define FSTV0910_P1_FILTER_EN 0xf5505020 +#define FSTV0910_P1_HYSTEN 0xf5503008 +#define FSTV0910_P1_HYSTSWRST 0xf5502004 +#define FSTV0910_P1_EN_MIS00 0xf5501002 +#define FSTV0910_P1_ALGOSWRST 0xf5500001 + +/* P1_PDELCTRL2 */ +#define RSTV0910_P1_PDELCTRL2 0xf551 +#define FSTV0910_P1_FORCE_CONTINUOUS 0xf5517080 +#define FSTV0910_P1_RESET_UPKO_COUNT 0xf5516040 +#define FSTV0910_P1_USER_PKTDELIN_NB 0xf5515020 +#define FSTV0910_P1_FRAME_MODE 0xf5511002 + +/* P1_HYSTTHRESH */ +#define RSTV0910_P1_HYSTTHRESH 0xf554 +#define FSTV0910_P1_DELIN_LOCKTHRES 0xf55440f0 +#define FSTV0910_P1_DELIN_UNLOCKTHRES 0xf554000f + +/* P1_UPLCCST0 */ +#define RSTV0910_P1_UPLCCST0 0xf558 +#define FSTV0910_P1_UPL_CST0 0xf55830f8 +#define FSTV0910_P1_UPL_MODE 0xf5580007 + +/* P1_ISIENTRY */ +#define RSTV0910_P1_ISIENTRY 0xf55e +#define FSTV0910_P1_ISI_ENTRY 0xf55e00ff + +/* P1_ISIBITENA */ +#define RSTV0910_P1_ISIBITENA 0xf55f +#define FSTV0910_P1_ISI_BIT_EN 0xf55f00ff + +/* P1_MATSTR1 */ +#define RSTV0910_P1_MATSTR1 0xf560 +#define FSTV0910_P1_MATYPE_CURRENT1 0xf56000ff + +/* P1_MATSTR0 */ +#define RSTV0910_P1_MATSTR0 0xf561 +#define FSTV0910_P1_MATYPE_CURRENT0 0xf56100ff + +/* P1_UPLSTR1 */ +#define RSTV0910_P1_UPLSTR1 0xf562 +#define FSTV0910_P1_UPL_CURRENT1 0xf56200ff + +/* P1_UPLSTR0 */ +#define RSTV0910_P1_UPLSTR0 0xf563 +#define FSTV0910_P1_UPL_CURRENT0 0xf56300ff + +/* P1_DFLSTR1 */ +#define RSTV0910_P1_DFLSTR1 0xf564 +#define FSTV0910_P1_DFL_CURRENT1 0xf56400ff + +/* P1_DFLSTR0 */ +#define RSTV0910_P1_DFLSTR0 0xf565 +#define FSTV0910_P1_DFL_CURRENT0 0xf56500ff + +/* P1_SYNCSTR */ +#define RSTV0910_P1_SYNCSTR 0xf566 +#define FSTV0910_P1_SYNC_CURRENT 0xf56600ff + +/* P1_SYNCDSTR1 */ +#define RSTV0910_P1_SYNCDSTR1 0xf567 +#define FSTV0910_P1_SYNCD_CURRENT1 0xf56700ff + +/* P1_SYNCDSTR0 */ +#define RSTV0910_P1_SYNCDSTR0 0xf568 +#define FSTV0910_P1_SYNCD_CURRENT0 0xf56800ff + +/* P1_PDELSTATUS1 */ +#define RSTV0910_P1_PDELSTATUS1 0xf569 +#define FSTV0910_P1_PKTDELIN_DELOCK 0xf5697080 +#define FSTV0910_P1_SYNCDUPDFL_BADDFL 0xf5696040 +#define FSTV0910_P1_UNACCEPTED_STREAM 0xf5694010 +#define FSTV0910_P1_BCH_ERROR_FLAG 0xf5693008 +#define FSTV0910_P1_PKTDELIN_LOCK 0xf5691002 +#define FSTV0910_P1_FIRST_LOCK 0xf5690001 + +/* P1_PDELSTATUS2 */ +#define RSTV0910_P1_PDELSTATUS2 0xf56a +#define FSTV0910_P1_FRAME_MODCOD 0xf56a207c +#define FSTV0910_P1_FRAME_TYPE 0xf56a0003 + +/* P1_BBFCRCKO1 */ +#define RSTV0910_P1_BBFCRCKO1 0xf56b +#define FSTV0910_P1_BBHCRC_KOCNT1 0xf56b00ff + +/* P1_BBFCRCKO0 */ +#define RSTV0910_P1_BBFCRCKO0 0xf56c +#define FSTV0910_P1_BBHCRC_KOCNT0 0xf56c00ff + +/* P1_UPCRCKO1 */ +#define RSTV0910_P1_UPCRCKO1 0xf56d +#define FSTV0910_P1_PKTCRC_KOCNT1 0xf56d00ff + +/* P1_UPCRCKO0 */ +#define RSTV0910_P1_UPCRCKO0 0xf56e +#define FSTV0910_P1_PKTCRC_KOCNT0 0xf56e00ff + +/* P1_PDELCTRL3 */ +#define RSTV0910_P1_PDELCTRL3 0xf56f +#define FSTV0910_P1_NOFIFO_BCHERR 0xf56f5020 +#define FSTV0910_P1_PKTDELIN_DELACMERR 0xf56f4010 + +/* P1_TSSTATEM */ +#define RSTV0910_P1_TSSTATEM 0xf570 +#define FSTV0910_P1_TSDIL_ON 0xf5707080 +#define FSTV0910_P1_TSRS_ON 0xf5705020 +#define FSTV0910_P1_TSDESCRAMB_ON 0xf5704010 +#define FSTV0910_P1_TSFRAME_MODE 0xf5703008 +#define FSTV0910_P1_TS_DISABLE 0xf5702004 +#define FSTV0910_P1_TSACM_MODE 0xf5701002 +#define FSTV0910_P1_TSOUT_NOSYNC 0xf5700001 + +/* P1_TSSTATEL */ +#define RSTV0910_P1_TSSTATEL 0xf571 +#define FSTV0910_P1_TSNOSYNCBYTE 0xf5717080 +#define FSTV0910_P1_TSPARITY_ON 0xf5716040 +#define FSTV0910_P1_TSISSYI_ON 0xf5713008 +#define FSTV0910_P1_TSNPD_ON 0xf5712004 +#define FSTV0910_P1_TSCRC8_ON 0xf5711002 +#define FSTV0910_P1_TSDSS_PACKET 0xf5710001 + +/* P1_TSCFGH */ +#define RSTV0910_P1_TSCFGH 0xf572 +#define FSTV0910_P1_TSFIFO_DVBCI 0xf5727080 +#define FSTV0910_P1_TSFIFO_SERIAL 0xf5726040 +#define FSTV0910_P1_TSFIFO_TEIUPDATE 0xf5725020 +#define FSTV0910_P1_TSFIFO_DUTY50 0xf5724010 +#define FSTV0910_P1_TSFIFO_HSGNLOUT 0xf5723008 +#define FSTV0910_P1_TSFIFO_ERRMODE 0xf5721006 +#define FSTV0910_P1_RST_HWARE 0xf5720001 + +/* P1_TSCFGM */ +#define RSTV0910_P1_TSCFGM 0xf573 +#define FSTV0910_P1_TSFIFO_MANSPEED 0xf57360c0 +#define FSTV0910_P1_TSFIFO_PERMDATA 0xf5735020 +#define FSTV0910_P1_TSFIFO_NONEWSGNL 0xf5734010 +#define FSTV0910_P1_TSFIFO_INVDATA 0xf5730001 + +/* P1_TSCFGL */ +#define RSTV0910_P1_TSCFGL 0xf574 +#define FSTV0910_P1_TSFIFO_BCLKDEL1CK 0xf57460c0 +#define FSTV0910_P1_BCHERROR_MODE 0xf5744030 +#define FSTV0910_P1_TSFIFO_NSGNL2DATA 0xf5743008 +#define FSTV0910_P1_TSFIFO_EMBINDVB 0xf5742004 +#define FSTV0910_P1_TSFIFO_BITSPEED 0xf5740003 + +/* P1_TSSYNC */ +#define RSTV0910_P1_TSSYNC 0xf575 +#define FSTV0910_P1_TSFIFO_SYNCMODE 0xf5753018 + +/* P1_TSINSDELH */ +#define RSTV0910_P1_TSINSDELH 0xf576 +#define FSTV0910_P1_TSDEL_SYNCBYTE 0xf5767080 +#define FSTV0910_P1_TSDEL_XXHEADER 0xf5766040 +#define FSTV0910_P1_TSDEL_DATAFIELD 0xf5764010 +#define FSTV0910_P1_TSINSDEL_RSPARITY 0xf5761002 +#define FSTV0910_P1_TSINSDEL_CRC8 0xf5760001 + +/* P1_TSINSDELM */ +#define RSTV0910_P1_TSINSDELM 0xf577 +#define FSTV0910_P1_TSINS_EMODCOD 0xf5774010 +#define FSTV0910_P1_TSINS_TOKEN 0xf5773008 +#define FSTV0910_P1_TSINS_XXXERR 0xf5772004 +#define FSTV0910_P1_TSINS_MATYPE 0xf5771002 +#define FSTV0910_P1_TSINS_UPL 0xf5770001 + +/* P1_TSINSDELL */ +#define RSTV0910_P1_TSINSDELL 0xf578 +#define FSTV0910_P1_TSINS_DFL 0xf5787080 +#define FSTV0910_P1_TSINS_SYNCD 0xf5786040 +#define FSTV0910_P1_TSINS_BLOCLEN 0xf5785020 +#define FSTV0910_P1_TSINS_SIGPCOUNT 0xf5784010 +#define FSTV0910_P1_TSINS_FIFO 0xf5783008 +#define FSTV0910_P1_TSINS_REALPACK 0xf5782004 +#define FSTV0910_P1_TSINS_TSCONFIG 0xf5781002 +#define FSTV0910_P1_TSINS_LATENCY 0xf5780001 + +/* P1_TSDIVN */ +#define RSTV0910_P1_TSDIVN 0xf579 +#define FSTV0910_P1_TSFIFO_SPEEDMODE 0xf57960c0 +#define FSTV0910_P1_TSFIFO_RISEOK 0xf5790007 + +/* P1_TSCFG4 */ +#define RSTV0910_P1_TSCFG4 0xf57a +#define FSTV0910_P1_TSFIFO_TSSPEEDMODE 0xf57a60c0 + +/* P1_TSSPEED */ +#define RSTV0910_P1_TSSPEED 0xf580 +#define FSTV0910_P1_TSFIFO_OUTSPEED 0xf58000ff + +/* P1_TSSTATUS */ +#define RSTV0910_P1_TSSTATUS 0xf581 +#define FSTV0910_P1_TSFIFO_LINEOK 0xf5817080 +#define FSTV0910_P1_TSFIFO_ERROR 0xf5816040 +#define FSTV0910_P1_TSFIFO_NOSYNC 0xf5814010 +#define FSTV0910_P1_TSREGUL_ERROR 0xf5812004 +#define FSTV0910_P1_DIL_READY 0xf5810001 + +/* P1_TSSTATUS2 */ +#define RSTV0910_P1_TSSTATUS2 0xf582 +#define FSTV0910_P1_TSFIFO_DEMODSEL 0xf5827080 +#define FSTV0910_P1_TSFIFOSPEED_STORE 0xf5826040 +#define FSTV0910_P1_DILXX_RESET 0xf5825020 +#define FSTV0910_P1_SCRAMBDETECT 0xf5821002 + +/* P1_TSBITRATE1 */ +#define RSTV0910_P1_TSBITRATE1 0xf583 +#define FSTV0910_P1_TSFIFO_BITRATE1 0xf58300ff + +/* P1_TSBITRATE0 */ +#define RSTV0910_P1_TSBITRATE0 0xf584 +#define FSTV0910_P1_TSFIFO_BITRATE0 0xf58400ff + +/* P1_TSPACKLEN1 */ +#define RSTV0910_P1_TSPACKLEN1 0xf585 +#define FSTV0910_P1_TSFIFO_PACKCPT 0xf58550e0 + +/* P1_TSDLY2 */ +#define RSTV0910_P1_TSDLY2 0xf589 +#define FSTV0910_P1_SOFFIFO_LATENCY2 0xf589000f + +/* P1_TSDLY1 */ +#define RSTV0910_P1_TSDLY1 0xf58a +#define FSTV0910_P1_SOFFIFO_LATENCY1 0xf58a00ff + +/* P1_TSDLY0 */ +#define RSTV0910_P1_TSDLY0 0xf58b +#define FSTV0910_P1_SOFFIFO_LATENCY0 0xf58b00ff + +/* P1_TSNPDAV */ +#define RSTV0910_P1_TSNPDAV 0xf58c +#define FSTV0910_P1_TSNPD_AVERAGE 0xf58c00ff + +/* P1_TSBUFSTAT2 */ +#define RSTV0910_P1_TSBUFSTAT2 0xf58d +#define FSTV0910_P1_TSISCR_3BYTES 0xf58d7080 +#define FSTV0910_P1_TSISCR_NEWDATA 0xf58d6040 +#define FSTV0910_P1_TSISCR_BUFSTAT2 0xf58d003f + +/* P1_TSBUFSTAT1 */ +#define RSTV0910_P1_TSBUFSTAT1 0xf58e +#define FSTV0910_P1_TSISCR_BUFSTAT1 0xf58e00ff + +/* P1_TSBUFSTAT0 */ +#define RSTV0910_P1_TSBUFSTAT0 0xf58f +#define FSTV0910_P1_TSISCR_BUFSTAT0 0xf58f00ff + +/* P1_TSDEBUGL */ +#define RSTV0910_P1_TSDEBUGL 0xf591 +#define FSTV0910_P1_TSFIFO_ERROR_EVNT 0xf5912004 +#define FSTV0910_P1_TSFIFO_OVERFLOWM 0xf5910001 + +/* P1_TSDLYSET2 */ +#define RSTV0910_P1_TSDLYSET2 0xf592 +#define FSTV0910_P1_SOFFIFO_OFFSET 0xf59260c0 +#define FSTV0910_P1_HYSTERESIS_THRESHOLD 0xf5924030 +#define FSTV0910_P1_SOFFIFO_SYMBOFFS2 0xf592000f + +/* P1_TSDLYSET1 */ +#define RSTV0910_P1_TSDLYSET1 0xf593 +#define FSTV0910_P1_SOFFIFO_SYMBOFFS1 0xf59300ff + +/* P1_TSDLYSET0 */ +#define RSTV0910_P1_TSDLYSET0 0xf594 +#define FSTV0910_P1_SOFFIFO_SYMBOFFS0 0xf59400ff + +/* P1_ERRCTRL1 */ +#define RSTV0910_P1_ERRCTRL1 0xf598 +#define FSTV0910_P1_ERR_SOURCE1 0xf59840f0 +#define FSTV0910_P1_NUM_EVENT1 0xf5980007 + +/* P1_ERRCNT12 */ +#define RSTV0910_P1_ERRCNT12 0xf599 +#define FSTV0910_P1_ERRCNT1_OLDVALUE 0xf5997080 +#define FSTV0910_P1_ERR_CNT12 0xf599007f + +/* P1_ERRCNT11 */ +#define RSTV0910_P1_ERRCNT11 0xf59a +#define FSTV0910_P1_ERR_CNT11 0xf59a00ff + +/* P1_ERRCNT10 */ +#define RSTV0910_P1_ERRCNT10 0xf59b +#define FSTV0910_P1_ERR_CNT10 0xf59b00ff + +/* P1_ERRCTRL2 */ +#define RSTV0910_P1_ERRCTRL2 0xf59c +#define FSTV0910_P1_ERR_SOURCE2 0xf59c40f0 +#define FSTV0910_P1_NUM_EVENT2 0xf59c0007 + +/* P1_ERRCNT22 */ +#define RSTV0910_P1_ERRCNT22 0xf59d +#define FSTV0910_P1_ERRCNT2_OLDVALUE 0xf59d7080 +#define FSTV0910_P1_ERR_CNT22 0xf59d007f + +/* P1_ERRCNT21 */ +#define RSTV0910_P1_ERRCNT21 0xf59e +#define FSTV0910_P1_ERR_CNT21 0xf59e00ff + +/* P1_ERRCNT20 */ +#define RSTV0910_P1_ERRCNT20 0xf59f +#define FSTV0910_P1_ERR_CNT20 0xf59f00ff + +/* P1_FECSPY */ +#define RSTV0910_P1_FECSPY 0xf5a0 +#define FSTV0910_P1_SPY_ENABLE 0xf5a07080 +#define FSTV0910_P1_NO_SYNCBYTE 0xf5a06040 +#define FSTV0910_P1_SERIAL_MODE 0xf5a05020 +#define FSTV0910_P1_UNUSUAL_PACKET 0xf5a04010 +#define FSTV0910_P1_BERMETER_DATAMODE 0xf5a0200c +#define FSTV0910_P1_BERMETER_LMODE 0xf5a01002 +#define FSTV0910_P1_BERMETER_RESET 0xf5a00001 + +/* P1_FSPYCFG */ +#define RSTV0910_P1_FSPYCFG 0xf5a1 +#define FSTV0910_P1_FECSPY_INPUT 0xf5a160c0 +#define FSTV0910_P1_RST_ON_ERROR 0xf5a15020 +#define FSTV0910_P1_ONE_SHOT 0xf5a14010 +#define FSTV0910_P1_I2C_MODE 0xf5a1200c +#define FSTV0910_P1_SPY_HYSTERESIS 0xf5a10003 + +/* P1_FSPYDATA */ +#define RSTV0910_P1_FSPYDATA 0xf5a2 +#define FSTV0910_P1_SPY_STUFFING 0xf5a27080 +#define FSTV0910_P1_SPY_CNULLPKT 0xf5a25020 +#define FSTV0910_P1_SPY_OUTDATA_MODE 0xf5a2001f + +/* P1_FSPYOUT */ +#define RSTV0910_P1_FSPYOUT 0xf5a3 +#define FSTV0910_P1_FSPY_DIRECT 0xf5a37080 +#define FSTV0910_P1_STUFF_MODE 0xf5a30007 + +/* P1_FSTATUS */ +#define RSTV0910_P1_FSTATUS 0xf5a4 +#define FSTV0910_P1_SPY_ENDSIM 0xf5a47080 +#define FSTV0910_P1_VALID_SIM 0xf5a46040 +#define FSTV0910_P1_FOUND_SIGNAL 0xf5a45020 +#define FSTV0910_P1_DSS_SYNCBYTE 0xf5a44010 +#define FSTV0910_P1_RESULT_STATE 0xf5a4000f + +/* P1_FBERCPT4 */ +#define RSTV0910_P1_FBERCPT4 0xf5a8 +#define FSTV0910_P1_FBERMETER_CPT4 0xf5a800ff + +/* P1_FBERCPT3 */ +#define RSTV0910_P1_FBERCPT3 0xf5a9 +#define FSTV0910_P1_FBERMETER_CPT3 0xf5a900ff + +/* P1_FBERCPT2 */ +#define RSTV0910_P1_FBERCPT2 0xf5aa +#define FSTV0910_P1_FBERMETER_CPT2 0xf5aa00ff + +/* P1_FBERCPT1 */ +#define RSTV0910_P1_FBERCPT1 0xf5ab +#define FSTV0910_P1_FBERMETER_CPT1 0xf5ab00ff + +/* P1_FBERCPT0 */ +#define RSTV0910_P1_FBERCPT0 0xf5ac +#define FSTV0910_P1_FBERMETER_CPT0 0xf5ac00ff + +/* P1_FBERERR2 */ +#define RSTV0910_P1_FBERERR2 0xf5ad +#define FSTV0910_P1_FBERMETER_ERR2 0xf5ad00ff + +/* P1_FBERERR1 */ +#define RSTV0910_P1_FBERERR1 0xf5ae +#define FSTV0910_P1_FBERMETER_ERR1 0xf5ae00ff + +/* P1_FBERERR0 */ +#define RSTV0910_P1_FBERERR0 0xf5af +#define FSTV0910_P1_FBERMETER_ERR0 0xf5af00ff + +/* P1_FSPYBER */ +#define RSTV0910_P1_FSPYBER 0xf5b2 +#define FSTV0910_P1_FSPYBER_SYNCBYTE 0xf5b24010 +#define FSTV0910_P1_FSPYBER_UNSYNC 0xf5b23008 +#define FSTV0910_P1_FSPYBER_CTIME 0xf5b20007 + +/* P1_SFERROR */ +#define RSTV0910_P1_SFERROR 0xf5c1 +#define FSTV0910_P1_SFEC_REGERR_VIT 0xf5c100ff + +/* P1_SFECSTATUS */ +#define RSTV0910_P1_SFECSTATUS 0xf5c3 +#define FSTV0910_P1_SFEC_ON 0xf5c37080 +#define FSTV0910_P1_SFEC_OFF 0xf5c36040 +#define FSTV0910_P1_LOCKEDSFEC 0xf5c33008 +#define FSTV0910_P1_SFEC_DELOCK 0xf5c32004 +#define FSTV0910_P1_SFEC_DEMODSEL 0xf5c31002 +#define FSTV0910_P1_SFEC_OVFON 0xf5c30001 + +/* P1_SFKDIV12 */ +#define RSTV0910_P1_SFKDIV12 0xf5c4 +#define FSTV0910_P1_SFECKDIV12_MAN 0xf5c47080 + +/* P1_SFKDIV23 */ +#define RSTV0910_P1_SFKDIV23 0xf5c5 +#define FSTV0910_P1_SFECKDIV23_MAN 0xf5c57080 + +/* P1_SFKDIV34 */ +#define RSTV0910_P1_SFKDIV34 0xf5c6 +#define FSTV0910_P1_SFECKDIV34_MAN 0xf5c67080 + +/* P1_SFKDIV56 */ +#define RSTV0910_P1_SFKDIV56 0xf5c7 +#define FSTV0910_P1_SFECKDIV56_MAN 0xf5c77080 + +/* P1_SFKDIV67 */ +#define RSTV0910_P1_SFKDIV67 0xf5c8 +#define FSTV0910_P1_SFECKDIV67_MAN 0xf5c87080 + +/* P1_SFKDIV78 */ +#define RSTV0910_P1_SFKDIV78 0xf5c9 +#define FSTV0910_P1_SFECKDIV78_MAN 0xf5c97080 + +/* P1_SFSTATUS */ +#define RSTV0910_P1_SFSTATUS 0xf5cc +#define FSTV0910_P1_SFEC_LINEOK 0xf5cc7080 +#define FSTV0910_P1_SFEC_ERROR 0xf5cc6040 +#define FSTV0910_P1_SFEC_DATA7 0xf5cc5020 +#define FSTV0910_P1_SFEC_PKTDNBRFAIL 0xf5cc4010 +#define FSTV0910_P1_TSSFEC_DEMODSEL 0xf5cc3008 +#define FSTV0910_P1_SFEC_NOSYNC 0xf5cc2004 +#define FSTV0910_P1_SFEC_UNREGULA 0xf5cc1002 +#define FSTV0910_P1_SFEC_READY 0xf5cc0001 + +/* P1_SFDLYSET2 */ +#define RSTV0910_P1_SFDLYSET2 0xf5d0 +#define FSTV0910_P1_SFEC_DISABLE 0xf5d01002 + +/* P1_SFERRCTRL */ +#define RSTV0910_P1_SFERRCTRL 0xf5d8 +#define FSTV0910_P1_SFEC_ERR_SOURCE 0xf5d840f0 +#define FSTV0910_P1_SFEC_NUM_EVENT 0xf5d80007 + +/* P1_SFERRCNT2 */ +#define RSTV0910_P1_SFERRCNT2 0xf5d9 +#define FSTV0910_P1_SFERRC_OLDVALUE 0xf5d97080 +#define FSTV0910_P1_SFEC_ERR_CNT2 0xf5d9007f + +/* P1_SFERRCNT1 */ +#define RSTV0910_P1_SFERRCNT1 0xf5da +#define FSTV0910_P1_SFEC_ERR_CNT1 0xf5da00ff + +/* P1_SFERRCNT0 */ +#define RSTV0910_P1_SFERRCNT0 0xf5db +#define FSTV0910_P1_SFEC_ERR_CNT0 0xf5db00ff + +/* RCCFG2 */ +#define RSTV0910_RCCFG2 0xf600 +#define FSTV0910_TSRCFIFO_DVBCI 0xf6007080 +#define FSTV0910_TSRCFIFO_SERIAL 0xf6006040 +#define FSTV0910_TSRCFIFO_DISABLE 0xf6005020 +#define FSTV0910_TSFIFO_2TORC 0xf6004010 +#define FSTV0910_TSRCFIFO_HSGNLOUT 0xf6003008 +#define FSTV0910_TSRCFIFO_ERRMODE 0xf6001006 + +/* RCCFG1 */ +#define RSTV0910_RCCFG1 0xf601 +#define FSTV0910_TSRCFIFO_MANSPEED 0xf60160c0 +#define FSTV0910_TSRCFIFO_PERMDATA 0xf6015020 +#define FSTV0910_TSRCFIFO_NONEWSGNL 0xf6014010 +#define FSTV0910_TSRCFIFO_INVDATA 0xf6010001 + +/* RCCFG0 */ +#define RSTV0910_RCCFG0 0xf602 +#define FSTV0910_TSRCFIFO_BCLKDEL1CK 0xf60260c0 +#define FSTV0910_TSRCFIFO_DUTY50 0xf6024010 +#define FSTV0910_TSRCFIFO_NSGNL2DATA 0xf6023008 +#define FSTV0910_TSRCFIFO_NPDSGNL 0xf6022004 + +/* RCINSDEL2 */ +#define RSTV0910_RCINSDEL2 0xf603 +#define FSTV0910_TSRCDEL_SYNCBYTE 0xf6037080 +#define FSTV0910_TSRCDEL_XXHEADER 0xf6036040 +#define FSTV0910_TSRCDEL_BBHEADER 0xf6035020 +#define FSTV0910_TSRCDEL_DATAFIELD 0xf6034010 +#define FSTV0910_TSRCINSDEL_ISCR 0xf6033008 +#define FSTV0910_TSRCINSDEL_NPD 0xf6032004 +#define FSTV0910_TSRCINSDEL_RSPARITY 0xf6031002 +#define FSTV0910_TSRCINSDEL_CRC8 0xf6030001 + +/* RCINSDEL1 */ +#define RSTV0910_RCINSDEL1 0xf604 +#define FSTV0910_TSRCINS_BBPADDING 0xf6047080 +#define FSTV0910_TSRCINS_BCHFEC 0xf6046040 +#define FSTV0910_TSRCINS_EMODCOD 0xf6044010 +#define FSTV0910_TSRCINS_TOKEN 0xf6043008 +#define FSTV0910_TSRCINS_XXXERR 0xf6042004 +#define FSTV0910_TSRCINS_MATYPE 0xf6041002 +#define FSTV0910_TSRCINS_UPL 0xf6040001 + +/* RCINSDEL0 */ +#define RSTV0910_RCINSDEL0 0xf605 +#define FSTV0910_TSRCINS_DFL 0xf6057080 +#define FSTV0910_TSRCINS_SYNCD 0xf6056040 +#define FSTV0910_TSRCINS_BLOCLEN 0xf6055020 +#define FSTV0910_TSRCINS_SIGPCOUNT 0xf6054010 +#define FSTV0910_TSRCINS_FIFO 0xf6053008 +#define FSTV0910_TSRCINS_REALPACK 0xf6052004 +#define FSTV0910_TSRCINS_TSCONFIG 0xf6051002 +#define FSTV0910_TSRCINS_LATENCY 0xf6050001 + +/* RCSTATUS */ +#define RSTV0910_RCSTATUS 0xf606 +#define FSTV0910_TSRCFIFO_LINEOK 0xf6067080 +#define FSTV0910_TSRCFIFO_ERROR 0xf6066040 +#define FSTV0910_TSRCREGUL_ERROR 0xf6064010 +#define FSTV0910_TSRCFIFO_DEMODSEL 0xf6063008 +#define FSTV0910_TSRCFIFOSPEED_STORE 0xf6062004 +#define FSTV0910_TSRCSPEED_IMPOSSIBLE 0xf6060001 + +/* RCSPEED */ +#define RSTV0910_RCSPEED 0xf607 +#define FSTV0910_TSRCFIFO_OUTSPEED 0xf60700ff + +/* TSGENERAL */ +#define RSTV0910_TSGENERAL 0xf630 +#define FSTV0910_TSFIFO_DISTS2PAR 0xf6306040 +#define FSTV0910_MUXSTREAM_OUTMODE 0xf6303008 +#define FSTV0910_TSFIFO_PERMPARAL 0xf6301006 + +/* P1_DISIRQCFG */ +#define RSTV0910_P1_DISIRQCFG 0xf700 +#define FSTV0910_P1_ENRXEND 0xf7006040 +#define FSTV0910_P1_ENRXFIFO8B 0xf7005020 +#define FSTV0910_P1_ENTRFINISH 0xf7004010 +#define FSTV0910_P1_ENTIMEOUT 0xf7003008 +#define FSTV0910_P1_ENTXEND 0xf7002004 +#define FSTV0910_P1_ENTXFIFO64B 0xf7001002 +#define FSTV0910_P1_ENGAPBURST 0xf7000001 + +/* P1_DISIRQSTAT */ +#define RSTV0910_P1_DISIRQSTAT 0xf701 +#define FSTV0910_P1_IRQRXEND 0xf7016040 +#define FSTV0910_P1_IRQRXFIFO8B 0xf7015020 +#define FSTV0910_P1_IRQTRFINISH 0xf7014010 +#define FSTV0910_P1_IRQTIMEOUT 0xf7013008 +#define FSTV0910_P1_IRQTXEND 0xf7012004 +#define FSTV0910_P1_IRQTXFIFO64B 0xf7011002 +#define FSTV0910_P1_IRQGAPBURST 0xf7010001 + +/* P1_DISTXCFG */ +#define RSTV0910_P1_DISTXCFG 0xf702 +#define FSTV0910_P1_DISTX_RESET 0xf7027080 +#define FSTV0910_P1_TIM_OFF 0xf7026040 +#define FSTV0910_P1_TIM_CMD 0xf7024030 +#define FSTV0910_P1_ENVELOP 0xf7023008 +#define FSTV0910_P1_DIS_PRECHARGE 0xf7022004 +#define FSTV0910_P1_DISEQC_MODE 0xf7020003 + +/* P1_DISTXSTATUS */ +#define RSTV0910_P1_DISTXSTATUS 0xf703 +#define FSTV0910_P1_TX_FIFO_FULL 0xf7036040 +#define FSTV0910_P1_TX_IDLE 0xf7035020 +#define FSTV0910_P1_GAP_BURST 0xf7034010 +#define FSTV0910_P1_TX_FIFO64B 0xf7033008 +#define FSTV0910_P1_TX_END 0xf7032004 +#define FSTV0910_P1_TR_TIMEOUT 0xf7031002 +#define FSTV0910_P1_TR_FINISH 0xf7030001 + +/* P1_DISTXBYTES */ +#define RSTV0910_P1_DISTXBYTES 0xf704 +#define FSTV0910_P1_TXFIFO_BYTES 0xf70400ff + +/* P1_DISTXFIFO */ +#define RSTV0910_P1_DISTXFIFO 0xf705 +#define FSTV0910_P1_DISEQC_TX_FIFO 0xf70500ff + +/* P1_DISTXF22 */ +#define RSTV0910_P1_DISTXF22 0xf706 +#define FSTV0910_P1_F22TX 0xf70600ff + +/* P1_DISTIMEOCFG */ +#define RSTV0910_P1_DISTIMEOCFG 0xf708 +#define FSTV0910_P1_RXCHOICE 0xf7081006 +#define FSTV0910_P1_TIMEOUT_OFF 0xf7080001 + +/* P1_DISTIMEOUT */ +#define RSTV0910_P1_DISTIMEOUT 0xf709 +#define FSTV0910_P1_TIMEOUT_COUNT 0xf70900ff + +/* P1_DISRXCFG */ +#define RSTV0910_P1_DISRXCFG 0xf70a +#define FSTV0910_P1_DISRX_RESET 0xf70a7080 +#define FSTV0910_P1_EXTENVELOP 0xf70a6040 +#define FSTV0910_P1_PINSELECT 0xf70a3038 +#define FSTV0910_P1_IGNORE_SHORT22K 0xf70a2004 +#define FSTV0910_P1_SIGNED_RXIN 0xf70a1002 +#define FSTV0910_P1_DISRX_ON 0xf70a0001 + +/* P1_DISRXSTAT1 */ +#define RSTV0910_P1_DISRXSTAT1 0xf70b +#define FSTV0910_P1_RXEND 0xf70b7080 +#define FSTV0910_P1_RXACTIVE 0xf70b6040 +#define FSTV0910_P1_RXDETECT 0xf70b5020 +#define FSTV0910_P1_CONTTONE 0xf70b4010 +#define FSTV0910_P1_8BFIFOREADY 0xf70b3008 +#define FSTV0910_P1_FIFOEMPTY 0xf70b2004 + +/* P1_DISRXSTAT0 */ +#define RSTV0910_P1_DISRXSTAT0 0xf70c +#define FSTV0910_P1_RXFAIL 0xf70c7080 +#define FSTV0910_P1_FIFOPFAIL 0xf70c6040 +#define FSTV0910_P1_RXNONBYTE 0xf70c5020 +#define FSTV0910_P1_FIFOOVF 0xf70c4010 +#define FSTV0910_P1_SHORT22K 0xf70c3008 +#define FSTV0910_P1_RXMSGLOST 0xf70c2004 + +/* P1_DISRXBYTES */ +#define RSTV0910_P1_DISRXBYTES 0xf70d +#define FSTV0910_P1_RXFIFO_BYTES 0xf70d001f + +/* P1_DISRXPARITY1 */ +#define RSTV0910_P1_DISRXPARITY1 0xf70e +#define FSTV0910_P1_DISRX_PARITY1 0xf70e00ff + +/* P1_DISRXPARITY0 */ +#define RSTV0910_P1_DISRXPARITY0 0xf70f +#define FSTV0910_P1_DISRX_PARITY0 0xf70f00ff + +/* P1_DISRXFIFO */ +#define RSTV0910_P1_DISRXFIFO 0xf710 +#define FSTV0910_P1_DISEQC_RX_FIFO 0xf71000ff + +/* P1_DISRXDC1 */ +#define RSTV0910_P1_DISRXDC1 0xf711 +#define FSTV0910_P1_DC_VALUE1 0xf7110103 + +/* P1_DISRXDC0 */ +#define RSTV0910_P1_DISRXDC0 0xf712 +#define FSTV0910_P1_DC_VALUE0 0xf71200ff + +/* P1_DISRXF221 */ +#define RSTV0910_P1_DISRXF221 0xf714 +#define FSTV0910_P1_F22RX1 0xf714000f + +/* P1_DISRXF220 */ +#define RSTV0910_P1_DISRXF220 0xf715 +#define FSTV0910_P1_F22RX0 0xf71500ff + +/* P1_DISRXF100 */ +#define RSTV0910_P1_DISRXF100 0xf716 +#define FSTV0910_P1_F100RX 0xf71600ff + +/* P1_DISRXSHORT22K */ +#define RSTV0910_P1_DISRXSHORT22K 0xf71c +#define FSTV0910_P1_SHORT22K_LENGTH 0xf71c001f + +/* P1_ACRPRESC */ +#define RSTV0910_P1_ACRPRESC 0xf71e +#define FSTV0910_P1_ACR_PRESC 0xf71e0007 + +/* P1_ACRDIV */ +#define RSTV0910_P1_ACRDIV 0xf71f +#define FSTV0910_P1_ACR_DIV 0xf71f00ff + +/* P2_DISIRQCFG */ +#define RSTV0910_P2_DISIRQCFG 0xf740 +#define FSTV0910_P2_ENRXEND 0xf7406040 +#define FSTV0910_P2_ENRXFIFO8B 0xf7405020 +#define FSTV0910_P2_ENTRFINISH 0xf7404010 +#define FSTV0910_P2_ENTIMEOUT 0xf7403008 +#define FSTV0910_P2_ENTXEND 0xf7402004 +#define FSTV0910_P2_ENTXFIFO64B 0xf7401002 +#define FSTV0910_P2_ENGAPBURST 0xf7400001 + +/* P2_DISIRQSTAT */ +#define RSTV0910_P2_DISIRQSTAT 0xf741 +#define FSTV0910_P2_IRQRXEND 0xf7416040 +#define FSTV0910_P2_IRQRXFIFO8B 0xf7415020 +#define FSTV0910_P2_IRQTRFINISH 0xf7414010 +#define FSTV0910_P2_IRQTIMEOUT 0xf7413008 +#define FSTV0910_P2_IRQTXEND 0xf7412004 +#define FSTV0910_P2_IRQTXFIFO64B 0xf7411002 +#define FSTV0910_P2_IRQGAPBURST 0xf7410001 + +/* P2_DISTXCFG */ +#define RSTV0910_P2_DISTXCFG 0xf742 +#define FSTV0910_P2_DISTX_RESET 0xf7427080 +#define FSTV0910_P2_TIM_OFF 0xf7426040 +#define FSTV0910_P2_TIM_CMD 0xf7424030 +#define FSTV0910_P2_ENVELOP 0xf7423008 +#define FSTV0910_P2_DIS_PRECHARGE 0xf7422004 +#define FSTV0910_P2_DISEQC_MODE 0xf7420003 + +/* P2_DISTXSTATUS */ +#define RSTV0910_P2_DISTXSTATUS 0xf743 +#define FSTV0910_P2_TX_FIFO_FULL 0xf7436040 +#define FSTV0910_P2_TX_IDLE 0xf7435020 +#define FSTV0910_P2_GAP_BURST 0xf7434010 +#define FSTV0910_P2_TX_FIFO64B 0xf7433008 +#define FSTV0910_P2_TX_END 0xf7432004 +#define FSTV0910_P2_TR_TIMEOUT 0xf7431002 +#define FSTV0910_P2_TR_FINISH 0xf7430001 + +/* P2_DISTXBYTES */ +#define RSTV0910_P2_DISTXBYTES 0xf744 +#define FSTV0910_P2_TXFIFO_BYTES 0xf74400ff + +/* P2_DISTXFIFO */ +#define RSTV0910_P2_DISTXFIFO 0xf745 +#define FSTV0910_P2_DISEQC_TX_FIFO 0xf74500ff + +/* P2_DISTXF22 */ +#define RSTV0910_P2_DISTXF22 0xf746 +#define FSTV0910_P2_F22TX 0xf74600ff + +/* P2_DISTIMEOCFG */ +#define RSTV0910_P2_DISTIMEOCFG 0xf748 +#define FSTV0910_P2_RXCHOICE 0xf7481006 +#define FSTV0910_P2_TIMEOUT_OFF 0xf7480001 + +/* P2_DISTIMEOUT */ +#define RSTV0910_P2_DISTIMEOUT 0xf749 +#define FSTV0910_P2_TIMEOUT_COUNT 0xf74900ff + +/* P2_DISRXCFG */ +#define RSTV0910_P2_DISRXCFG 0xf74a +#define FSTV0910_P2_DISRX_RESET 0xf74a7080 +#define FSTV0910_P2_EXTENVELOP 0xf74a6040 +#define FSTV0910_P2_PINSELECT 0xf74a3038 +#define FSTV0910_P2_IGNORE_SHORT22K 0xf74a2004 +#define FSTV0910_P2_SIGNED_RXIN 0xf74a1002 +#define FSTV0910_P2_DISRX_ON 0xf74a0001 + +/* P2_DISRXSTAT1 */ +#define RSTV0910_P2_DISRXSTAT1 0xf74b +#define FSTV0910_P2_RXEND 0xf74b7080 +#define FSTV0910_P2_RXACTIVE 0xf74b6040 +#define FSTV0910_P2_RXDETECT 0xf74b5020 +#define FSTV0910_P2_CONTTONE 0xf74b4010 +#define FSTV0910_P2_8BFIFOREADY 0xf74b3008 +#define FSTV0910_P2_FIFOEMPTY 0xf74b2004 + +/* P2_DISRXSTAT0 */ +#define RSTV0910_P2_DISRXSTAT0 0xf74c +#define FSTV0910_P2_RXFAIL 0xf74c7080 +#define FSTV0910_P2_FIFOPFAIL 0xf74c6040 +#define FSTV0910_P2_RXNONBYTE 0xf74c5020 +#define FSTV0910_P2_FIFOOVF 0xf74c4010 +#define FSTV0910_P2_SHORT22K 0xf74c3008 +#define FSTV0910_P2_RXMSGLOST 0xf74c2004 + +/* P2_DISRXBYTES */ +#define RSTV0910_P2_DISRXBYTES 0xf74d +#define FSTV0910_P2_RXFIFO_BYTES 0xf74d001f + +/* P2_DISRXPARITY1 */ +#define RSTV0910_P2_DISRXPARITY1 0xf74e +#define FSTV0910_P2_DISRX_PARITY1 0xf74e00ff + +/* P2_DISRXPARITY0 */ +#define RSTV0910_P2_DISRXPARITY0 0xf74f +#define FSTV0910_P2_DISRX_PARITY0 0xf74f00ff + +/* P2_DISRXFIFO */ +#define RSTV0910_P2_DISRXFIFO 0xf750 +#define FSTV0910_P2_DISEQC_RX_FIFO 0xf75000ff + +/* P2_DISRXDC1 */ +#define RSTV0910_P2_DISRXDC1 0xf751 +#define FSTV0910_P2_DC_VALUE1 0xf7510103 + +/* P2_DISRXDC0 */ +#define RSTV0910_P2_DISRXDC0 0xf752 +#define FSTV0910_P2_DC_VALUE0 0xf75200ff + +/* P2_DISRXF221 */ +#define RSTV0910_P2_DISRXF221 0xf754 +#define FSTV0910_P2_F22RX1 0xf754000f + +/* P2_DISRXF220 */ +#define RSTV0910_P2_DISRXF220 0xf755 +#define FSTV0910_P2_F22RX0 0xf75500ff + +/* P2_DISRXF100 */ +#define RSTV0910_P2_DISRXF100 0xf756 +#define FSTV0910_P2_F100RX 0xf75600ff + +/* P2_DISRXSHORT22K */ +#define RSTV0910_P2_DISRXSHORT22K 0xf75c +#define FSTV0910_P2_SHORT22K_LENGTH 0xf75c001f + +/* P2_ACRPRESC */ +#define RSTV0910_P2_ACRPRESC 0xf75e +#define FSTV0910_P2_ACR_PRESC 0xf75e0007 + +/* P2_ACRDIV */ +#define RSTV0910_P2_ACRDIV 0xf75f +#define FSTV0910_P2_ACR_DIV 0xf75f00ff + +/* P1_NBITER_NF1 */ +#define RSTV0910_P1_NBITER_NF1 0xfa00 +#define FSTV0910_P1_NBITER_NF_QPSK_1_4 0xfa0000ff + +/* P1_NBITER_NF2 */ +#define RSTV0910_P1_NBITER_NF2 0xfa01 +#define FSTV0910_P1_NBITER_NF_QPSK_1_3 0xfa0100ff + +/* P1_NBITER_NF3 */ +#define RSTV0910_P1_NBITER_NF3 0xfa02 +#define FSTV0910_P1_NBITER_NF_QPSK_2_5 0xfa0200ff + +/* P1_NBITER_NF4 */ +#define RSTV0910_P1_NBITER_NF4 0xfa03 +#define FSTV0910_P1_NBITER_NF_QPSK_1_2 0xfa0300ff + +/* P1_NBITER_NF5 */ +#define RSTV0910_P1_NBITER_NF5 0xfa04 +#define FSTV0910_P1_NBITER_NF_QPSK_3_5 0xfa0400ff + +/* P1_NBITER_NF6 */ +#define RSTV0910_P1_NBITER_NF6 0xfa05 +#define FSTV0910_P1_NBITER_NF_QPSK_2_3 0xfa0500ff + +/* P1_NBITER_NF7 */ +#define RSTV0910_P1_NBITER_NF7 0xfa06 +#define FSTV0910_P1_NBITER_NF_QPSK_3_4 0xfa0600ff + +/* P1_NBITER_NF8 */ +#define RSTV0910_P1_NBITER_NF8 0xfa07 +#define FSTV0910_P1_NBITER_NF_QPSK_4_5 0xfa0700ff + +/* P1_NBITER_NF9 */ +#define RSTV0910_P1_NBITER_NF9 0xfa08 +#define FSTV0910_P1_NBITER_NF_QPSK_5_6 0xfa0800ff + +/* P1_NBITER_NF10 */ +#define RSTV0910_P1_NBITER_NF10 0xfa09 +#define FSTV0910_P1_NBITER_NF_QPSK_8_9 0xfa0900ff + +/* P1_NBITER_NF11 */ +#define RSTV0910_P1_NBITER_NF11 0xfa0a +#define FSTV0910_P1_NBITER_NF_QPSK_9_10 0xfa0a00ff + +/* P1_NBITER_NF12 */ +#define RSTV0910_P1_NBITER_NF12 0xfa0b +#define FSTV0910_P1_NBITER_NF_8PSK_3_5 0xfa0b00ff + +/* P1_NBITER_NF13 */ +#define RSTV0910_P1_NBITER_NF13 0xfa0c +#define FSTV0910_P1_NBITER_NF_8PSK_2_3 0xfa0c00ff + +/* P1_NBITER_NF14 */ +#define RSTV0910_P1_NBITER_NF14 0xfa0d +#define FSTV0910_P1_NBITER_NF_8PSK_3_4 0xfa0d00ff + +/* P1_NBITER_NF15 */ +#define RSTV0910_P1_NBITER_NF15 0xfa0e +#define FSTV0910_P1_NBITER_NF_8PSK_5_6 0xfa0e00ff + +/* P1_NBITER_NF16 */ +#define RSTV0910_P1_NBITER_NF16 0xfa0f +#define FSTV0910_P1_NBITER_NF_8PSK_8_9 0xfa0f00ff + +/* P1_NBITER_NF17 */ +#define RSTV0910_P1_NBITER_NF17 0xfa10 +#define FSTV0910_P1_NBITER_NF_8PSK_9_10 0xfa1000ff + +/* P1_NBITER_NF18 */ +#define RSTV0910_P1_NBITER_NF18 0xfa11 +#define FSTV0910_P1_NBITER_NF_16APSK_2_3 0xfa1100ff + +/* P1_NBITER_NF19 */ +#define RSTV0910_P1_NBITER_NF19 0xfa12 +#define FSTV0910_P1_NBITER_NF_16APSK_3_4 0xfa1200ff + +/* P1_NBITER_NF20 */ +#define RSTV0910_P1_NBITER_NF20 0xfa13 +#define FSTV0910_P1_NBITER_NF_16APSK_4_5 0xfa1300ff + +/* P1_NBITER_NF21 */ +#define RSTV0910_P1_NBITER_NF21 0xfa14 +#define FSTV0910_P1_NBITER_NF_16APSK_5_6 0xfa1400ff + +/* P1_NBITER_NF22 */ +#define RSTV0910_P1_NBITER_NF22 0xfa15 +#define FSTV0910_P1_NBITER_NF_16APSK_8_9 0xfa1500ff + +/* P1_NBITER_NF23 */ +#define RSTV0910_P1_NBITER_NF23 0xfa16 +#define FSTV0910_P1_NBITER_NF_16APSK_9_10 0xfa1600ff + +/* P1_NBITER_NF24 */ +#define RSTV0910_P1_NBITER_NF24 0xfa17 +#define FSTV0910_P1_NBITER_NF_32APSK_3_4 0xfa1700ff + +/* P1_NBITER_NF25 */ +#define RSTV0910_P1_NBITER_NF25 0xfa18 +#define FSTV0910_P1_NBITER_NF_32APSK_4_5 0xfa1800ff + +/* P1_NBITER_NF26 */ +#define RSTV0910_P1_NBITER_NF26 0xfa19 +#define FSTV0910_P1_NBITER_NF_32APSK_5_6 0xfa1900ff + +/* P1_NBITER_NF27 */ +#define RSTV0910_P1_NBITER_NF27 0xfa1a +#define FSTV0910_P1_NBITER_NF_32APSK_8_9 0xfa1a00ff + +/* P1_NBITER_NF28 */ +#define RSTV0910_P1_NBITER_NF28 0xfa1b +#define FSTV0910_P1_NBITER_NF_32APSK_9_10 0xfa1b00ff + +/* P1_NBITER_SF1 */ +#define RSTV0910_P1_NBITER_SF1 0xfa1c +#define FSTV0910_P1_NBITER_SF_QPSK_1_4 0xfa1c00ff + +/* P1_NBITER_SF2 */ +#define RSTV0910_P1_NBITER_SF2 0xfa1d +#define FSTV0910_P1_NBITER_SF_QPSK_1_3 0xfa1d00ff + +/* P1_NBITER_SF3 */ +#define RSTV0910_P1_NBITER_SF3 0xfa1e +#define FSTV0910_P1_NBITER_SF_QPSK_2_5 0xfa1e00ff + +/* P1_NBITER_SF4 */ +#define RSTV0910_P1_NBITER_SF4 0xfa1f +#define FSTV0910_P1_NBITER_SF_QPSK_1_2 0xfa1f00ff + +/* P1_NBITER_SF5 */ +#define RSTV0910_P1_NBITER_SF5 0xfa20 +#define FSTV0910_P1_NBITER_SF_QPSK_3_5 0xfa2000ff + +/* P1_NBITER_SF6 */ +#define RSTV0910_P1_NBITER_SF6 0xfa21 +#define FSTV0910_P1_NBITER_SF_QPSK_2_3 0xfa2100ff + +/* P1_NBITER_SF7 */ +#define RSTV0910_P1_NBITER_SF7 0xfa22 +#define FSTV0910_P1_NBITER_SF_QPSK_3_4 0xfa2200ff + +/* P1_NBITER_SF8 */ +#define RSTV0910_P1_NBITER_SF8 0xfa23 +#define FSTV0910_P1_NBITER_SF_QPSK_4_5 0xfa2300ff + +/* P1_NBITER_SF9 */ +#define RSTV0910_P1_NBITER_SF9 0xfa24 +#define FSTV0910_P1_NBITER_SF_QPSK_5_6 0xfa2400ff + +/* P1_NBITER_SF10 */ +#define RSTV0910_P1_NBITER_SF10 0xfa25 +#define FSTV0910_P1_NBITER_SF_QPSK_8_9 0xfa2500ff + +/* P1_NBITER_SF12 */ +#define RSTV0910_P1_NBITER_SF12 0xfa26 +#define FSTV0910_P1_NBITER_SF_8PSK_3_5 0xfa2600ff + +/* P1_NBITER_SF13 */ +#define RSTV0910_P1_NBITER_SF13 0xfa27 +#define FSTV0910_P1_NBITER_SF_8PSK_2_3 0xfa2700ff + +/* P1_NBITER_SF14 */ +#define RSTV0910_P1_NBITER_SF14 0xfa28 +#define FSTV0910_P1_NBITER_SF_8PSK_3_4 0xfa2800ff + +/* P1_NBITER_SF15 */ +#define RSTV0910_P1_NBITER_SF15 0xfa29 +#define FSTV0910_P1_NBITER_SF_8PSK_5_6 0xfa2900ff + +/* P1_NBITER_SF16 */ +#define RSTV0910_P1_NBITER_SF16 0xfa2a +#define FSTV0910_P1_NBITER_SF_8PSK_8_9 0xfa2a00ff + +/* P1_NBITER_SF18 */ +#define RSTV0910_P1_NBITER_SF18 0xfa2b +#define FSTV0910_P1_NBITER_SF_16APSK_2_3 0xfa2b00ff + +/* P1_NBITER_SF19 */ +#define RSTV0910_P1_NBITER_SF19 0xfa2c +#define FSTV0910_P1_NBITER_SF_16APSK_3_4 0xfa2c00ff + +/* P1_NBITER_SF20 */ +#define RSTV0910_P1_NBITER_SF20 0xfa2d +#define FSTV0910_P1_NBITER_SF_16APSK_4_5 0xfa2d00ff + +/* P1_NBITER_SF21 */ +#define RSTV0910_P1_NBITER_SF21 0xfa2e +#define FSTV0910_P1_NBITER_SF_16APSK_5_6 0xfa2e00ff + +/* P1_NBITER_SF22 */ +#define RSTV0910_P1_NBITER_SF22 0xfa2f +#define FSTV0910_P1_NBITER_SF_16APSK_8_9 0xfa2f00ff + +/* P1_NBITER_SF24 */ +#define RSTV0910_P1_NBITER_SF24 0xfa30 +#define FSTV0910_P1_NBITER_SF_32APSK_3_4 0xfa3000ff + +/* P1_NBITER_SF25 */ +#define RSTV0910_P1_NBITER_SF25 0xfa31 +#define FSTV0910_P1_NBITER_SF_32APSK_4_5 0xfa3100ff + +/* P1_NBITER_SF26 */ +#define RSTV0910_P1_NBITER_SF26 0xfa32 +#define FSTV0910_P1_NBITER_SF_32APSK_5_6 0xfa3200ff + +/* P1_NBITER_SF27 */ +#define RSTV0910_P1_NBITER_SF27 0xfa33 +#define FSTV0910_P1_NBITER_SF_32APSK_8_9 0xfa3300ff + +/* SELSATUR6 */ +#define RSTV0910_SELSATUR6 0xfa34 +#define FSTV0910_SSAT_SF27 0xfa343008 +#define FSTV0910_SSAT_SF26 0xfa342004 +#define FSTV0910_SSAT_SF25 0xfa341002 +#define FSTV0910_SSAT_SF24 0xfa340001 + +/* SELSATUR5 */ +#define RSTV0910_SELSATUR5 0xfa35 +#define FSTV0910_SSAT_SF22 0xfa357080 +#define FSTV0910_SSAT_SF21 0xfa356040 +#define FSTV0910_SSAT_SF20 0xfa355020 +#define FSTV0910_SSAT_SF19 0xfa354010 +#define FSTV0910_SSAT_SF18 0xfa353008 +#define FSTV0910_SSAT_SF16 0xfa352004 +#define FSTV0910_SSAT_SF15 0xfa351002 +#define FSTV0910_SSAT_SF14 0xfa350001 + +/* SELSATUR4 */ +#define RSTV0910_SELSATUR4 0xfa36 +#define FSTV0910_SSAT_SF13 0xfa367080 +#define FSTV0910_SSAT_SF12 0xfa366040 +#define FSTV0910_SSAT_SF10 0xfa365020 +#define FSTV0910_SSAT_SF9 0xfa364010 +#define FSTV0910_SSAT_SF8 0xfa363008 +#define FSTV0910_SSAT_SF7 0xfa362004 +#define FSTV0910_SSAT_SF6 0xfa361002 +#define FSTV0910_SSAT_SF5 0xfa360001 + +/* SELSATUR3 */ +#define RSTV0910_SELSATUR3 0xfa37 +#define FSTV0910_SSAT_SF4 0xfa377080 +#define FSTV0910_SSAT_SF3 0xfa376040 +#define FSTV0910_SSAT_SF2 0xfa375020 +#define FSTV0910_SSAT_SF1 0xfa374010 +#define FSTV0910_SSAT_NF28 0xfa373008 +#define FSTV0910_SSAT_NF27 0xfa372004 +#define FSTV0910_SSAT_NF26 0xfa371002 +#define FSTV0910_SSAT_NF25 0xfa370001 + +/* SELSATUR2 */ +#define RSTV0910_SELSATUR2 0xfa38 +#define FSTV0910_SSAT_NF24 0xfa387080 +#define FSTV0910_SSAT_NF23 0xfa386040 +#define FSTV0910_SSAT_NF22 0xfa385020 +#define FSTV0910_SSAT_NF21 0xfa384010 +#define FSTV0910_SSAT_NF20 0xfa383008 +#define FSTV0910_SSAT_NF19 0xfa382004 +#define FSTV0910_SSAT_NF18 0xfa381002 +#define FSTV0910_SSAT_NF17 0xfa380001 + +/* SELSATUR1 */ +#define RSTV0910_SELSATUR1 0xfa39 +#define FSTV0910_SSAT_NF16 0xfa397080 +#define FSTV0910_SSAT_NF15 0xfa396040 +#define FSTV0910_SSAT_NF14 0xfa395020 +#define FSTV0910_SSAT_NF13 0xfa394010 +#define FSTV0910_SSAT_NF12 0xfa393008 +#define FSTV0910_SSAT_NF11 0xfa392004 +#define FSTV0910_SSAT_NF10 0xfa391002 +#define FSTV0910_SSAT_NF9 0xfa390001 + +/* SELSATUR0 */ +#define RSTV0910_SELSATUR0 0xfa3a +#define FSTV0910_SSAT_NF8 0xfa3a7080 +#define FSTV0910_SSAT_NF7 0xfa3a6040 +#define FSTV0910_SSAT_NF6 0xfa3a5020 +#define FSTV0910_SSAT_NF5 0xfa3a4010 +#define FSTV0910_SSAT_NF4 0xfa3a3008 +#define FSTV0910_SSAT_NF3 0xfa3a2004 +#define FSTV0910_SSAT_NF2 0xfa3a1002 +#define FSTV0910_SSAT_NF1 0xfa3a0001 + +/* GAINLLR_NF1 */ +#define RSTV0910_GAINLLR_NF1 0xfa40 +#define FSTV0910_GAINLLR_NF_QPSK_1_4 0xfa40007f + +/* GAINLLR_NF2 */ +#define RSTV0910_GAINLLR_NF2 0xfa41 +#define FSTV0910_GAINLLR_NF_QPSK_1_3 0xfa41007f + +/* GAINLLR_NF3 */ +#define RSTV0910_GAINLLR_NF3 0xfa42 +#define FSTV0910_GAINLLR_NF_QPSK_2_5 0xfa42007f + +/* GAINLLR_NF4 */ +#define RSTV0910_GAINLLR_NF4 0xfa43 +#define FSTV0910_GAINLLR_NF_QPSK_1_2 0xfa43007f + +/* GAINLLR_NF5 */ +#define RSTV0910_GAINLLR_NF5 0xfa44 +#define FSTV0910_GAINLLR_NF_QPSK_3_5 0xfa44007f + +/* GAINLLR_NF6 */ +#define RSTV0910_GAINLLR_NF6 0xfa45 +#define FSTV0910_GAINLLR_NF_QPSK_2_3 0xfa45007f + +/* GAINLLR_NF7 */ +#define RSTV0910_GAINLLR_NF7 0xfa46 +#define FSTV0910_GAINLLR_NF_QPSK_3_4 0xfa46007f + +/* GAINLLR_NF8 */ +#define RSTV0910_GAINLLR_NF8 0xfa47 +#define FSTV0910_GAINLLR_NF_QPSK_4_5 0xfa47007f + +/* GAINLLR_NF9 */ +#define RSTV0910_GAINLLR_NF9 0xfa48 +#define FSTV0910_GAINLLR_NF_QPSK_5_6 0xfa48007f + +/* GAINLLR_NF10 */ +#define RSTV0910_GAINLLR_NF10 0xfa49 +#define FSTV0910_GAINLLR_NF_QPSK_8_9 0xfa49007f + +/* GAINLLR_NF11 */ +#define RSTV0910_GAINLLR_NF11 0xfa4a +#define FSTV0910_GAINLLR_NF_QPSK_9_10 0xfa4a007f + +/* GAINLLR_NF12 */ +#define RSTV0910_GAINLLR_NF12 0xfa4b +#define FSTV0910_GAINLLR_NF_8PSK_3_5 0xfa4b007f + +/* GAINLLR_NF13 */ +#define RSTV0910_GAINLLR_NF13 0xfa4c +#define FSTV0910_GAINLLR_NF_8PSK_2_3 0xfa4c007f + +/* GAINLLR_NF14 */ +#define RSTV0910_GAINLLR_NF14 0xfa4d +#define FSTV0910_GAINLLR_NF_8PSK_3_4 0xfa4d007f + +/* GAINLLR_NF15 */ +#define RSTV0910_GAINLLR_NF15 0xfa4e +#define FSTV0910_GAINLLR_NF_8PSK_5_6 0xfa4e007f + +/* GAINLLR_NF16 */ +#define RSTV0910_GAINLLR_NF16 0xfa4f +#define FSTV0910_GAINLLR_NF_8PSK_8_9 0xfa4f007f + +/* GAINLLR_NF17 */ +#define RSTV0910_GAINLLR_NF17 0xfa50 +#define FSTV0910_GAINLLR_NF_8PSK_9_10 0xfa50007f + +/* GAINLLR_NF18 */ +#define RSTV0910_GAINLLR_NF18 0xfa51 +#define FSTV0910_GAINLLR_NF_16APSK_2_3 0xfa51007f + +/* GAINLLR_NF19 */ +#define RSTV0910_GAINLLR_NF19 0xfa52 +#define FSTV0910_GAINLLR_NF_16APSK_3_4 0xfa52007f + +/* GAINLLR_NF20 */ +#define RSTV0910_GAINLLR_NF20 0xfa53 +#define FSTV0910_GAINLLR_NF_16APSK_4_5 0xfa53007f + +/* GAINLLR_NF21 */ +#define RSTV0910_GAINLLR_NF21 0xfa54 +#define FSTV0910_GAINLLR_NF_16APSK_5_6 0xfa54007f + +/* GAINLLR_NF22 */ +#define RSTV0910_GAINLLR_NF22 0xfa55 +#define FSTV0910_GAINLLR_NF_16APSK_8_9 0xfa55007f + +/* GAINLLR_NF23 */ +#define RSTV0910_GAINLLR_NF23 0xfa56 +#define FSTV0910_GAINLLR_NF_16APSK_9_10 0xfa56007f + +/* GAINLLR_NF24 */ +#define RSTV0910_GAINLLR_NF24 0xfa57 +#define FSTV0910_GAINLLR_NF_32APSK_3_4 0xfa57007f + +/* GAINLLR_NF25 */ +#define RSTV0910_GAINLLR_NF25 0xfa58 +#define FSTV0910_GAINLLR_NF_32APSK_4_5 0xfa58007f + +/* GAINLLR_NF26 */ +#define RSTV0910_GAINLLR_NF26 0xfa59 +#define FSTV0910_GAINLLR_NF_32APSK_5_6 0xfa59007f + +/* GAINLLR_NF27 */ +#define RSTV0910_GAINLLR_NF27 0xfa5a +#define FSTV0910_GAINLLR_NF_32APSK_8_9 0xfa5a007f + +/* GAINLLR_NF28 */ +#define RSTV0910_GAINLLR_NF28 0xfa5b +#define FSTV0910_GAINLLR_NF_32APSK_9_10 0xfa5b007f + +/* GAINLLR_SF1 */ +#define RSTV0910_GAINLLR_SF1 0xfa5c +#define FSTV0910_GAINLLR_SF_QPSK_1_4 0xfa5c007f + +/* GAINLLR_SF2 */ +#define RSTV0910_GAINLLR_SF2 0xfa5d +#define FSTV0910_GAINLLR_SF_QPSK_1_3 0xfa5d007f + +/* GAINLLR_SF3 */ +#define RSTV0910_GAINLLR_SF3 0xfa5e +#define FSTV0910_GAINLLR_SF_QPSK_2_5 0xfa5e007f + +/* GAINLLR_SF4 */ +#define RSTV0910_GAINLLR_SF4 0xfa5f +#define FSTV0910_GAINLLR_SF_QPSK_1_2 0xfa5f007f + +/* GAINLLR_SF5 */ +#define RSTV0910_GAINLLR_SF5 0xfa60 +#define FSTV0910_GAINLLR_SF_QPSK_3_5 0xfa60007f + +/* GAINLLR_SF6 */ +#define RSTV0910_GAINLLR_SF6 0xfa61 +#define FSTV0910_GAINLLR_SF_QPSK_2_3 0xfa61007f + +/* GAINLLR_SF7 */ +#define RSTV0910_GAINLLR_SF7 0xfa62 +#define FSTV0910_GAINLLR_SF_QPSK_3_4 0xfa62007f + +/* GAINLLR_SF8 */ +#define RSTV0910_GAINLLR_SF8 0xfa63 +#define FSTV0910_GAINLLR_SF_QPSK_4_5 0xfa63007f + +/* GAINLLR_SF9 */ +#define RSTV0910_GAINLLR_SF9 0xfa64 +#define FSTV0910_GAINLLR_SF_QPSK_5_6 0xfa64007f + +/* GAINLLR_SF10 */ +#define RSTV0910_GAINLLR_SF10 0xfa65 +#define FSTV0910_GAINLLR_SF_QPSK_8_9 0xfa65007f + +/* GAINLLR_SF12 */ +#define RSTV0910_GAINLLR_SF12 0xfa66 +#define FSTV0910_GAINLLR_SF_8PSK_3_5 0xfa66007f + +/* GAINLLR_SF13 */ +#define RSTV0910_GAINLLR_SF13 0xfa67 +#define FSTV0910_GAINLLR_SF_8PSK_2_3 0xfa67007f + +/* GAINLLR_SF14 */ +#define RSTV0910_GAINLLR_SF14 0xfa68 +#define FSTV0910_GAINLLR_SF_8PSK_3_4 0xfa68007f + +/* GAINLLR_SF15 */ +#define RSTV0910_GAINLLR_SF15 0xfa69 +#define FSTV0910_GAINLLR_SF_8PSK_5_6 0xfa69007f + +/* GAINLLR_SF16 */ +#define RSTV0910_GAINLLR_SF16 0xfa6a +#define FSTV0910_GAINLLR_SF_8PSK_8_9 0xfa6a007f + +/* GAINLLR_SF18 */ +#define RSTV0910_GAINLLR_SF18 0xfa6b +#define FSTV0910_GAINLLR_SF_16APSK_2_3 0xfa6b007f + +/* GAINLLR_SF19 */ +#define RSTV0910_GAINLLR_SF19 0xfa6c +#define FSTV0910_GAINLLR_SF_16APSK_3_4 0xfa6c007f + +/* GAINLLR_SF20 */ +#define RSTV0910_GAINLLR_SF20 0xfa6d +#define FSTV0910_GAINLLR_SF_16APSK_4_5 0xfa6d007f + +/* GAINLLR_SF21 */ +#define RSTV0910_GAINLLR_SF21 0xfa6e +#define FSTV0910_GAINLLR_SF_16APSK_5_6 0xfa6e007f + +/* GAINLLR_SF22 */ +#define RSTV0910_GAINLLR_SF22 0xfa6f +#define FSTV0910_GAINLLR_SF_16APSK_8_9 0xfa6f007f + +/* GAINLLR_SF24 */ +#define RSTV0910_GAINLLR_SF24 0xfa70 +#define FSTV0910_GAINLLR_SF_32APSK_3_4 0xfa70007f + +/* GAINLLR_SF25 */ +#define RSTV0910_GAINLLR_SF25 0xfa71 +#define FSTV0910_GAINLLR_SF_32APSK_4_5 0xfa71007f + +/* GAINLLR_SF26 */ +#define RSTV0910_GAINLLR_SF26 0xfa72 +#define FSTV0910_GAINLLR_SF_32APSK_5_6 0xfa72007f + +/* GAINLLR_SF27 */ +#define RSTV0910_GAINLLR_SF27 0xfa73 +#define FSTV0910_GAINLLR_SF_32APSK_8_9 0xfa73007f + +/* CFGEXT */ +#define RSTV0910_CFGEXT 0xfa80 +#define FSTV0910_BYPBCH 0xfa806040 +#define FSTV0910_BYPLDPC 0xfa805020 +#define FSTV0910_SHORTMULT 0xfa802004 + +/* GENCFG */ +#define RSTV0910_GENCFG 0xfa86 +#define FSTV0910_BROADCAST 0xfa864010 +#define FSTV0910_CROSSINPUT 0xfa861002 +#define FSTV0910_DDEMOD 0xfa860001 + +/* LDPCERR1 */ +#define RSTV0910_LDPCERR1 0xfa96 +#define FSTV0910_LDPC_ERRORS1 0xfa9600ff + +/* LDPCERR0 */ +#define RSTV0910_LDPCERR0 0xfa97 +#define FSTV0910_LDPC_ERRORS0 0xfa9700ff + +/* BCHERR */ +#define RSTV0910_BCHERR 0xfa98 +#define FSTV0910_ERRORFLAG 0xfa984010 +#define FSTV0910_BCH_ERRORS_COUNTER 0xfa98000f + +/* P1_MAXEXTRAITER */ +#define RSTV0910_P1_MAXEXTRAITER 0xfab1 +#define FSTV0910_P1_MAX_EXTRA_ITER 0xfab100ff + +/* P2_MAXEXTRAITER */ +#define RSTV0910_P2_MAXEXTRAITER 0xfab6 +#define FSTV0910_P2_MAX_EXTRA_ITER 0xfab600ff + +/* P1_STATUSITER */ +#define RSTV0910_P1_STATUSITER 0xfabc +#define FSTV0910_P1_STATUS_ITER 0xfabc00ff + +/* P1_STATUSMAXITER */ +#define RSTV0910_P1_STATUSMAXITER 0xfabd +#define FSTV0910_P1_STATUS_MAX_ITER 0xfabd00ff + +/* P2_STATUSITER */ +#define RSTV0910_P2_STATUSITER 0xfabe +#define FSTV0910_P2_STATUS_ITER 0xfabe00ff + +/* P2_STATUSMAXITER */ +#define RSTV0910_P2_STATUSMAXITER 0xfabf +#define FSTV0910_P2_STATUS_MAX_ITER 0xfabf00ff + +/* P2_NBITER_NF1 */ +#define RSTV0910_P2_NBITER_NF1 0xfac0 +#define FSTV0910_P2_NBITER_NF_QPSK_1_4 0xfac000ff + +/* P2_NBITER_NF2 */ +#define RSTV0910_P2_NBITER_NF2 0xfac1 +#define FSTV0910_P2_NBITER_NF_QPSK_1_3 0xfac100ff + +/* P2_NBITER_NF3 */ +#define RSTV0910_P2_NBITER_NF3 0xfac2 +#define FSTV0910_P2_NBITER_NF_QPSK_2_5 0xfac200ff + +/* P2_NBITER_NF4 */ +#define RSTV0910_P2_NBITER_NF4 0xfac3 +#define FSTV0910_P2_NBITER_NF_QPSK_1_2 0xfac300ff + +/* P2_NBITER_NF5 */ +#define RSTV0910_P2_NBITER_NF5 0xfac4 +#define FSTV0910_P2_NBITER_NF_QPSK_3_5 0xfac400ff + +/* P2_NBITER_NF6 */ +#define RSTV0910_P2_NBITER_NF6 0xfac5 +#define FSTV0910_P2_NBITER_NF_QPSK_2_3 0xfac500ff + +/* P2_NBITER_NF7 */ +#define RSTV0910_P2_NBITER_NF7 0xfac6 +#define FSTV0910_P2_NBITER_NF_QPSK_3_4 0xfac600ff + +/* P2_NBITER_NF8 */ +#define RSTV0910_P2_NBITER_NF8 0xfac7 +#define FSTV0910_P2_NBITER_NF_QPSK_4_5 0xfac700ff + +/* P2_NBITER_NF9 */ +#define RSTV0910_P2_NBITER_NF9 0xfac8 +#define FSTV0910_P2_NBITER_NF_QPSK_5_6 0xfac800ff + +/* P2_NBITER_NF10 */ +#define RSTV0910_P2_NBITER_NF10 0xfac9 +#define FSTV0910_P2_NBITER_NF_QPSK_8_9 0xfac900ff + +/* P2_NBITER_NF11 */ +#define RSTV0910_P2_NBITER_NF11 0xfaca +#define FSTV0910_P2_NBITER_NF_QPSK_9_10 0xfaca00ff + +/* P2_NBITER_NF12 */ +#define RSTV0910_P2_NBITER_NF12 0xfacb +#define FSTV0910_P2_NBITER_NF_8PSK_3_5 0xfacb00ff + +/* P2_NBITER_NF13 */ +#define RSTV0910_P2_NBITER_NF13 0xfacc +#define FSTV0910_P2_NBITER_NF_8PSK_2_3 0xfacc00ff + +/* P2_NBITER_NF14 */ +#define RSTV0910_P2_NBITER_NF14 0xfacd +#define FSTV0910_P2_NBITER_NF_8PSK_3_4 0xfacd00ff + +/* P2_NBITER_NF15 */ +#define RSTV0910_P2_NBITER_NF15 0xface +#define FSTV0910_P2_NBITER_NF_8PSK_5_6 0xface00ff + +/* P2_NBITER_NF16 */ +#define RSTV0910_P2_NBITER_NF16 0xfacf +#define FSTV0910_P2_NBITER_NF_8PSK_8_9 0xfacf00ff + +/* P2_NBITER_NF17 */ +#define RSTV0910_P2_NBITER_NF17 0xfad0 +#define FSTV0910_P2_NBITER_NF_8PSK_9_10 0xfad000ff + +/* P2_NBITER_NF18 */ +#define RSTV0910_P2_NBITER_NF18 0xfad1 +#define FSTV0910_P2_NBITER_NF_16APSK_2_3 0xfad100ff + +/* P2_NBITER_NF19 */ +#define RSTV0910_P2_NBITER_NF19 0xfad2 +#define FSTV0910_P2_NBITER_NF_16APSK_3_4 0xfad200ff + +/* P2_NBITER_NF20 */ +#define RSTV0910_P2_NBITER_NF20 0xfad3 +#define FSTV0910_P2_NBITER_NF_16APSK_4_5 0xfad300ff + +/* P2_NBITER_NF21 */ +#define RSTV0910_P2_NBITER_NF21 0xfad4 +#define FSTV0910_P2_NBITER_NF_16APSK_5_6 0xfad400ff + +/* P2_NBITER_NF22 */ +#define RSTV0910_P2_NBITER_NF22 0xfad5 +#define FSTV0910_P2_NBITER_NF_16APSK_8_9 0xfad500ff + +/* P2_NBITER_NF23 */ +#define RSTV0910_P2_NBITER_NF23 0xfad6 +#define FSTV0910_P2_NBITER_NF_16APSK_9_10 0xfad600ff + +/* P2_NBITER_NF24 */ +#define RSTV0910_P2_NBITER_NF24 0xfad7 +#define FSTV0910_P2_NBITER_NF_32APSK_3_4 0xfad700ff + +/* P2_NBITER_NF25 */ +#define RSTV0910_P2_NBITER_NF25 0xfad8 +#define FSTV0910_P2_NBITER_NF_32APSK_4_5 0xfad800ff + +/* P2_NBITER_NF26 */ +#define RSTV0910_P2_NBITER_NF26 0xfad9 +#define FSTV0910_P2_NBITER_NF_32APSK_5_6 0xfad900ff + +/* P2_NBITER_NF27 */ +#define RSTV0910_P2_NBITER_NF27 0xfada +#define FSTV0910_P2_NBITER_NF_32APSK_8_9 0xfada00ff + +/* P2_NBITER_NF28 */ +#define RSTV0910_P2_NBITER_NF28 0xfadb +#define FSTV0910_P2_NBITER_NF_32APSK_9_10 0xfadb00ff + +/* P2_NBITER_SF1 */ +#define RSTV0910_P2_NBITER_SF1 0xfadc +#define FSTV0910_P2_NBITER_SF_QPSK_1_4 0xfadc00ff + +/* P2_NBITER_SF2 */ +#define RSTV0910_P2_NBITER_SF2 0xfadd +#define FSTV0910_P2_NBITER_SF_QPSK_1_3 0xfadd00ff + +/* P2_NBITER_SF3 */ +#define RSTV0910_P2_NBITER_SF3 0xfade +#define FSTV0910_P2_NBITER_SF_QPSK_2_5 0xfade00ff + +/* P2_NBITER_SF4 */ +#define RSTV0910_P2_NBITER_SF4 0xfadf +#define FSTV0910_P2_NBITER_SF_QPSK_1_2 0xfadf00ff + +/* P2_NBITER_SF5 */ +#define RSTV0910_P2_NBITER_SF5 0xfae0 +#define FSTV0910_P2_NBITER_SF_QPSK_3_5 0xfae000ff + +/* P2_NBITER_SF6 */ +#define RSTV0910_P2_NBITER_SF6 0xfae1 +#define FSTV0910_P2_NBITER_SF_QPSK_2_3 0xfae100ff + +/* P2_NBITER_SF7 */ +#define RSTV0910_P2_NBITER_SF7 0xfae2 +#define FSTV0910_P2_NBITER_SF_QPSK_3_4 0xfae200ff + +/* P2_NBITER_SF8 */ +#define RSTV0910_P2_NBITER_SF8 0xfae3 +#define FSTV0910_P2_NBITER_SF_QPSK_4_5 0xfae300ff + +/* P2_NBITER_SF9 */ +#define RSTV0910_P2_NBITER_SF9 0xfae4 +#define FSTV0910_P2_NBITER_SF_QPSK_5_6 0xfae400ff + +/* P2_NBITER_SF10 */ +#define RSTV0910_P2_NBITER_SF10 0xfae5 +#define FSTV0910_P2_NBITER_SF_QPSK_8_9 0xfae500ff + +/* P2_NBITER_SF12 */ +#define RSTV0910_P2_NBITER_SF12 0xfae6 +#define FSTV0910_P2_NBITER_SF_8PSK_3_5 0xfae600ff + +/* P2_NBITER_SF13 */ +#define RSTV0910_P2_NBITER_SF13 0xfae7 +#define FSTV0910_P2_NBITER_SF_8PSK_2_3 0xfae700ff + +/* P2_NBITER_SF14 */ +#define RSTV0910_P2_NBITER_SF14 0xfae8 +#define FSTV0910_P2_NBITER_SF_8PSK_3_4 0xfae800ff + +/* P2_NBITER_SF15 */ +#define RSTV0910_P2_NBITER_SF15 0xfae9 +#define FSTV0910_P2_NBITER_SF_8PSK_5_6 0xfae900ff + +/* P2_NBITER_SF16 */ +#define RSTV0910_P2_NBITER_SF16 0xfaea +#define FSTV0910_P2_NBITER_SF_8PSK_8_9 0xfaea00ff + +/* P2_NBITER_SF18 */ +#define RSTV0910_P2_NBITER_SF18 0xfaeb +#define FSTV0910_P2_NBITER_SF_16APSK_2_3 0xfaeb00ff + +/* P2_NBITER_SF19 */ +#define RSTV0910_P2_NBITER_SF19 0xfaec +#define FSTV0910_P2_NBITER_SF_16APSK_3_4 0xfaec00ff + +/* P2_NBITER_SF20 */ +#define RSTV0910_P2_NBITER_SF20 0xfaed +#define FSTV0910_P2_NBITER_SF_16APSK_4_5 0xfaed00ff + +/* P2_NBITER_SF21 */ +#define RSTV0910_P2_NBITER_SF21 0xfaee +#define FSTV0910_P2_NBITER_SF_16APSK_5_6 0xfaee00ff + +/* P2_NBITER_SF22 */ +#define RSTV0910_P2_NBITER_SF22 0xfaef +#define FSTV0910_P2_NBITER_SF_16APSK_8_9 0xfaef00ff + +/* P2_NBITER_SF24 */ +#define RSTV0910_P2_NBITER_SF24 0xfaf0 +#define FSTV0910_P2_NBITER_SF_32APSK_3_4 0xfaf000ff + +/* P2_NBITER_SF25 */ +#define RSTV0910_P2_NBITER_SF25 0xfaf1 +#define FSTV0910_P2_NBITER_SF_32APSK_4_5 0xfaf100ff + +/* P2_NBITER_SF26 */ +#define RSTV0910_P2_NBITER_SF26 0xfaf2 +#define FSTV0910_P2_NBITER_SF_32APSK_5_6 0xfaf200ff + +/* P2_NBITER_SF27 */ +#define RSTV0910_P2_NBITER_SF27 0xfaf3 +#define FSTV0910_P2_NBITER_SF_32APSK_8_9 0xfaf300ff + +/* TSTRES0 */ +#define RSTV0910_TSTRES0 0xff11 +#define FSTV0910_FRESFEC 0xff117080 +#define FSTV0910_FRESSYM1 0xff113008 +#define FSTV0910_FRESSYM2 0xff112004 + +/* TSTOUT */ +#define RSTV0910_TSTOUT 0xff12 +#define FSTV0910_TS 0xff12103e +#define FSTV0910_TEST_OUT 0xff120001 + +/* TSTIN */ +#define RSTV0910_TSTIN 0xff13 +#define FSTV0910_TEST_IN 0xff137080 + +/* P2_TSTDMD */ +#define RSTV0910_P2_TSTDMD 0xff20 +#define FSTV0910_P2_CFRINIT_INVZIGZAG 0xff203008 + +/* P2_TCTL1 */ +#define RSTV0910_P2_TCTL1 0xff24 +#define FSTV0910_P2_TST_IQSYMBSEL 0xff24001f + +/* P2_TCTL4 */ +#define RSTV0910_P2_TCTL4 0xff28 +#define FSTV0910_P2_CFR2TOCFR1_DVBS1 0xff2860c0 + +/* P2_TPKTDELIN */ +#define RSTV0910_P2_TPKTDELIN 0xff37 +#define FSTV0910_P2_CFG_RSPARITYON 0xff377080 + +/* P1_TSTDMD */ +#define RSTV0910_P1_TSTDMD 0xff40 +#define FSTV0910_P1_CFRINIT_INVZIGZAG 0xff403008 + +/* P1_TCTL1 */ +#define RSTV0910_P1_TCTL1 0xff44 +#define FSTV0910_P1_TST_IQSYMBSEL 0xff44001f + +/* P1_TCTL4 */ +#define RSTV0910_P1_TCTL4 0xff48 +#define FSTV0910_P1_CFR2TOCFR1_DVBS1 0xff4860c0 + +/* P1_TPKTDELIN */ +#define RSTV0910_P1_TPKTDELIN 0xff57 +#define FSTV0910_P1_CFG_RSPARITYON 0xff577080 + +/* TSTTSRS */ +#define RSTV0910_TSTTSRS 0xff6d +#define FSTV0910_TSTRS_DISRS2 0xff6d1002 +#define FSTV0910_TSTRS_DISRS1 0xff6d0001 + +#define STV0910_NBREGS 975 +#define STV0910_NBFIELDS 1818 diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c new file mode 100644 index 0000000000..1cf9c095db --- /dev/null +++ b/drivers/media/dvb-frontends/stv6110.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * stv6110.c + * + * Driver for ST STV6110 satellite tuner IC. + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> + +#include <linux/types.h> + +#include "stv6110.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +static int debug; + +struct stv6110_priv { + int i2c_address; + struct i2c_adapter *i2c; + + u32 mclk; + u8 clk_div; + u8 gain; + u8 regs[8]; +}; + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG args); \ + } while (0) + +static s32 abssub(s32 a, s32 b) +{ + if (a > b) + return a - b; + else + return b - a; +}; + +static void stv6110_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[], + int start, int len) +{ + struct stv6110_priv *priv = fe->tuner_priv; + int rc; + u8 cmdbuf[MAX_XFER_SIZE]; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = cmdbuf, + .len = len + 1 + }; + + dprintk("%s\n", __func__); + + if (1 + len > sizeof(cmdbuf)) { + printk(KERN_WARNING + "%s: i2c wr: len=%d is too big!\n", + KBUILD_MODNAME, len); + return -EINVAL; + } + + if (start + len > 8) + return -EINVAL; + + memcpy(&cmdbuf[1], buf, len); + cmdbuf[0] = start; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = i2c_transfer(priv->i2c, &msg, 1); + if (rc != 1) + dprintk("%s: i2c error\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[], + int start, int len) +{ + struct stv6110_priv *priv = fe->tuner_priv; + int rc; + u8 reg[] = { start }; + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = reg, + .len = 1, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .buf = regs, + .len = len, + }, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = i2c_transfer(priv->i2c, msg, 2); + if (rc != 2) + dprintk("%s: i2c error\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&priv->regs[start], regs, len); + + return 0; +} + +static int stv6110_read_reg(struct dvb_frontend *fe, int start) +{ + u8 buf[] = { 0 }; + stv6110_read_regs(fe, buf, start, 1); + + return buf[0]; +} + +static int stv6110_sleep(struct dvb_frontend *fe) +{ + u8 reg[] = { 0 }; + stv6110_write_regs(fe, reg, 0, 1); + + return 0; +} + +static u32 carrier_width(u32 symbol_rate, enum fe_rolloff rolloff) +{ + u32 rlf; + + switch (rolloff) { + case ROLLOFF_20: + rlf = 20; + break; + case ROLLOFF_25: + rlf = 25; + break; + default: + rlf = 35; + break; + } + + return symbol_rate + ((symbol_rate * rlf) / 100); +} + +static int stv6110_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + struct stv6110_priv *priv = fe->tuner_priv; + u8 r8, ret = 0x04; + int i; + + if ((bandwidth / 2) > 36000000) /*BW/2 max=31+5=36 mhz for r8=31*/ + r8 = 31; + else if ((bandwidth / 2) < 5000000) /* BW/2 min=5Mhz for F=0 */ + r8 = 0; + else /*if 5 < BW/2 < 36*/ + r8 = (bandwidth / 2) / 1000000 - 5; + + /* ctrl3, RCCLKOFF = 0 Activate the calibration Clock */ + /* ctrl3, CF = r8 Set the LPF value */ + priv->regs[RSTV6110_CTRL3] &= ~((1 << 6) | 0x1f); + priv->regs[RSTV6110_CTRL3] |= (r8 & 0x1f); + stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); + /* stat1, CALRCSTRT = 1 Start LPF auto calibration*/ + priv->regs[RSTV6110_STAT1] |= 0x02; + stv6110_write_regs(fe, &priv->regs[RSTV6110_STAT1], RSTV6110_STAT1, 1); + + i = 0; + /* Wait for CALRCSTRT == 0 */ + while ((i < 10) && (ret != 0)) { + ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x02); + mdelay(1); /* wait for LPF auto calibration */ + i++; + } + + /* RCCLKOFF = 1 calibration done, deactivate the calibration Clock */ + priv->regs[RSTV6110_CTRL3] |= (1 << 6); + stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); + return 0; +} + +static int stv6110_init(struct dvb_frontend *fe) +{ + struct stv6110_priv *priv = fe->tuner_priv; + u8 buf0[] = { 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; + + memcpy(priv->regs, buf0, 8); + /* K = (Reference / 1000000) - 16 */ + priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); + priv->regs[RSTV6110_CTRL1] |= + ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); + + /* divisor value for the output clock */ + priv->regs[RSTV6110_CTRL2] &= ~0xc0; + priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6); + + stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8); + msleep(1); + stv6110_set_bandwidth(fe, 72000000); + + return 0; +} + +static int stv6110_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct stv6110_priv *priv = fe->tuner_priv; + u32 nbsteps, divider, psd2, freq; + u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + stv6110_read_regs(fe, regs, 0, 8); + /*N*/ + divider = (priv->regs[RSTV6110_TUNING2] & 0x0f) << 8; + divider += priv->regs[RSTV6110_TUNING1]; + + /*R*/ + nbsteps = (priv->regs[RSTV6110_TUNING2] >> 6) & 3; + /*p*/ + psd2 = (priv->regs[RSTV6110_TUNING2] >> 4) & 1; + + freq = divider * (priv->mclk / 1000); + freq /= (1 << (nbsteps + psd2)); + freq /= 4; + + *frequency = freq; + + return 0; +} + +static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + struct stv6110_priv *priv = fe->tuner_priv; + u8 ret = 0x04; + u32 divider, ref, p, presc, i, result_freq, vco_freq; + s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val; + + dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__, + frequency, priv->mclk); + + /* K = (Reference / 1000000) - 16 */ + priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); + priv->regs[RSTV6110_CTRL1] |= + ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); + + /* BB_GAIN = db/2 */ + priv->regs[RSTV6110_CTRL2] &= ~0x0f; + priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f); + + if (frequency <= 1023000) { + p = 1; + presc = 0; + } else if (frequency <= 1300000) { + p = 1; + presc = 1; + } else if (frequency <= 2046000) { + p = 0; + presc = 0; + } else { + p = 0; + presc = 1; + } + /* DIV4SEL = p*/ + priv->regs[RSTV6110_TUNING2] &= ~(1 << 4); + priv->regs[RSTV6110_TUNING2] |= (p << 4); + + /* PRESC32ON = presc */ + priv->regs[RSTV6110_TUNING2] &= ~(1 << 5); + priv->regs[RSTV6110_TUNING2] |= (presc << 5); + + p_val = (int)(1 << (p + 1)) * 10;/* P = 2 or P = 4 */ + for (r_div = 0; r_div <= 3; r_div++) { + p_calc = (priv->mclk / 100000); + p_calc /= (1 << (r_div + 1)); + if ((abssub(p_calc, p_val)) < (abssub(p_calc_opt, p_val))) + r_div_opt = r_div; + + p_calc_opt = (priv->mclk / 100000); + p_calc_opt /= (1 << (r_div_opt + 1)); + } + + ref = priv->mclk / ((1 << (r_div_opt + 1)) * (1 << (p + 1))); + divider = (((frequency * 1000) + (ref >> 1)) / ref); + + /* RDIV = r_div_opt */ + priv->regs[RSTV6110_TUNING2] &= ~(3 << 6); + priv->regs[RSTV6110_TUNING2] |= (((r_div_opt) & 3) << 6); + + /* NDIV_MSB = MSB(divider) */ + priv->regs[RSTV6110_TUNING2] &= ~0x0f; + priv->regs[RSTV6110_TUNING2] |= (((divider) >> 8) & 0x0f); + + /* NDIV_LSB, LSB(divider) */ + priv->regs[RSTV6110_TUNING1] = (divider & 0xff); + + /* CALVCOSTRT = 1 VCO Auto Calibration */ + priv->regs[RSTV6110_STAT1] |= 0x04; + stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], + RSTV6110_CTRL1, 8); + + i = 0; + /* Wait for CALVCOSTRT == 0 */ + while ((i < 10) && (ret != 0)) { + ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x04); + msleep(1); /* wait for VCO auto calibration */ + i++; + } + + ret = stv6110_read_reg(fe, RSTV6110_STAT1); + stv6110_get_frequency(fe, &result_freq); + + vco_freq = divider * ((priv->mclk / 1000) / ((1 << (r_div_opt + 1)))); + dprintk("%s, stat1=%x, lo_freq=%d kHz, vco_frec=%d kHz\n", __func__, + ret, result_freq, vco_freq); + + return 0; +} + +static int stv6110_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 bandwidth = carrier_width(c->symbol_rate, c->rolloff); + + stv6110_set_frequency(fe, c->frequency); + stv6110_set_bandwidth(fe, bandwidth); + + return 0; +} + +static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct stv6110_priv *priv = fe->tuner_priv; + u8 r8 = 0; + u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + stv6110_read_regs(fe, regs, 0, 8); + + /* CF */ + r8 = priv->regs[RSTV6110_CTRL3] & 0x1f; + *bandwidth = (r8 + 5) * 2000000;/* x2 for ZIF tuner BW/2 = F+5 Mhz */ + + return 0; +} + +static const struct dvb_tuner_ops stv6110_tuner_ops = { + .info = { + .name = "ST STV6110", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, + }, + .init = stv6110_init, + .release = stv6110_release, + .sleep = stv6110_sleep, + .set_params = stv6110_set_params, + .get_frequency = stv6110_get_frequency, + .set_frequency = stv6110_set_frequency, + .get_bandwidth = stv6110_get_bandwidth, + .set_bandwidth = stv6110_set_bandwidth, + +}; + +struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, + const struct stv6110_config *config, + struct i2c_adapter *i2c) +{ + struct stv6110_priv *priv = NULL; + u8 reg0[] = { 0x00, 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; + + struct i2c_msg msg[] = { + { + .addr = config->i2c_address, + .flags = 0, + .buf = reg0, + .len = 9 + } + }; + int ret; + + /* divisor value for the output clock */ + reg0[2] &= ~0xc0; + reg0[2] |= (config->clk_div << 6); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(i2c, msg, 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 1) + return NULL; + + priv = kzalloc(sizeof(struct stv6110_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = config->i2c_address; + priv->i2c = i2c; + priv->mclk = config->mclk; + priv->clk_div = config->clk_div; + priv->gain = config->gain; + + memcpy(&priv->regs, ®0[1], 8); + + memcpy(&fe->ops.tuner_ops, &stv6110_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + printk(KERN_INFO "STV6110 attached on addr=%x!\n", priv->i2c_address); + + return fe; +} +EXPORT_SYMBOL_GPL(stv6110_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("ST STV6110 driver"); +MODULE_AUTHOR("Igor M. Liplianin"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stv6110.h b/drivers/media/dvb-frontends/stv6110.h new file mode 100644 index 0000000000..1cee9e62b7 --- /dev/null +++ b/drivers/media/dvb-frontends/stv6110.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stv6110.h + * + * Driver for ST STV6110 satellite tuner IC. + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + */ + +#ifndef __DVB_STV6110_H__ +#define __DVB_STV6110_H__ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +/* registers */ +#define RSTV6110_CTRL1 0 +#define RSTV6110_CTRL2 1 +#define RSTV6110_TUNING1 2 +#define RSTV6110_TUNING2 3 +#define RSTV6110_CTRL3 4 +#define RSTV6110_STAT1 5 +#define RSTV6110_STAT2 6 +#define RSTV6110_STAT3 7 + +struct stv6110_config { + u8 i2c_address; + u32 mclk; + u8 gain; + u8 clk_div; /* divisor value for the output clock */ +}; + +#if IS_REACHABLE(CONFIG_DVB_STV6110) +extern struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, + const struct stv6110_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, + const struct stv6110_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c new file mode 100644 index 0000000000..c678f47d24 --- /dev/null +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + STV6110(A) Silicon tuner driver + + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <media/dvb_frontend.h> + +#include "stv6110x_reg.h" +#include "stv6110x.h" +#include "stv6110x_priv.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +static unsigned int verbose; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "Set Verbosity level"); + +static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data) +{ + int ret; + const struct stv6110x_config *config = stv6110x->config; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = config->addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = config->addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } + }; + + ret = i2c_transfer(stv6110x->i2c, msg, 2); + if (ret != 2) { + dprintk(FE_ERROR, 1, "I/O Error"); + return -EREMOTEIO; + } + *data = b1[0]; + + return 0; +} + +static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 data[], int len) +{ + int ret; + const struct stv6110x_config *config = stv6110x->config; + u8 buf[MAX_XFER_SIZE]; + + struct i2c_msg msg = { + .addr = config->addr, + .flags = 0, + .buf = buf, + .len = len + 1 + }; + + if (1 + len > sizeof(buf)) { + printk(KERN_WARNING + "%s: i2c wr: len=%d is too big!\n", + KBUILD_MODNAME, len); + return -EINVAL; + } + + if (start + len > 8) + return -EINVAL; + + buf[0] = start; + memcpy(&buf[1], data, len); + + ret = i2c_transfer(stv6110x->i2c, &msg, 1); + if (ret != 1) { + dprintk(FE_ERROR, 1, "I/O Error"); + return -EREMOTEIO; + } + + return 0; +} + +static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) +{ + u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return stv6110x_write_regs(stv6110x, reg, &tmp, 1); +} + +static int stv6110x_init(struct dvb_frontend *fe) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + int ret; + + ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, + ARRAY_SIZE(stv6110x->regs)); + if (ret < 0) { + dprintk(FE_ERROR, 1, "Initialization failed"); + return -1; + } + + return 0; +} + +static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + u32 rDiv, divider; + s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000; + u8 i; + + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); + + if (frequency <= 1023000) { + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); + pVal = 40; + } else if (frequency <= 1300000) { + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); + pVal = 40; + } else if (frequency <= 2046000) { + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); + pVal = 20; + } else { + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); + pVal = 20; + } + + for (rDiv = 0; rDiv <= 3; rDiv++) { + pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv); + + if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal)))) + rDivOpt = rDiv; + + pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt); + } + + divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz; + divider = (divider + 5) / 10; + + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider)); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider)); + + /* VCO Auto calibration */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1); + + stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); + stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x->regs[STV6110x_TNG1]); + stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x->regs[STV6110x_TNG0]); + stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); + + for (i = 0; i < TRIALS; i++) { + stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); + if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x->regs[STV6110x_STAT1])) + break; + msleep(1); + } + + return 0; +} + +static int stv6110x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x->regs[STV6110x_TNG1]); + stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x->regs[STV6110x_TNG0]); + + *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x->regs[STV6110x_TNG1]), + STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x->regs[STV6110x_TNG0]))) * REFCLOCK_kHz; + + *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x->regs[STV6110x_TNG1]) + + STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x->regs[STV6110x_TNG1]))); + + *frequency >>= 2; + + return 0; +} + +static int stv6110x_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + u32 halfbw; + u8 i; + + halfbw = bandwidth >> 1; + + if (halfbw > 36000000) + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */ + else if (halfbw < 5000000) + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */ + else + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */ + + + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */ + + stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); + stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); + + for (i = 0; i < TRIALS; i++) { + stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); + if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x->regs[STV6110x_STAT1])) + break; + msleep(1); + } + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */ + stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); + + return 0; +} + +static int stv6110x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x->regs[STV6110x_CTRL3]); + *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x->regs[STV6110x_CTRL3]) + 5) * 2000000; + + return 0; +} + +static int stv6110x_set_refclock(struct dvb_frontend *fe, u32 refclock) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + /* setup divider */ + switch (refclock) { + default: + case 1: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); + break; + case 2: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); + break; + case 4: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); + break; + case 8: + case 0: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); + break; + } + stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); + + return 0; +} + +static int stv6110x_get_bbgain(struct dvb_frontend *fe, u32 *gain) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x->regs[STV6110x_CTRL2]); + *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x->regs[STV6110x_CTRL2]); + + return 0; +} + +static int stv6110x_set_bbgain(struct dvb_frontend *fe, u32 gain) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2); + stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); + + return 0; +} + +static int stv6110x_set_mode(struct dvb_frontend *fe, enum tuner_mode mode) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + int ret; + + switch (mode) { + case TUNER_SLEEP: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 0); + break; + + case TUNER_WAKE: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 1); + break; + } + + ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); + if (ret < 0) { + dprintk(FE_ERROR, 1, "I/O Error"); + return -EIO; + } + + return 0; +} + +static int stv6110x_sleep(struct dvb_frontend *fe) +{ + if (fe->tuner_priv) + return stv6110x_set_mode(fe, TUNER_SLEEP); + + return 0; +} + +static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); + + if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x->regs[STV6110x_STAT1])) + *status = TUNER_PHASELOCKED; + else + *status = 0; + + return 0; +} + + +static void stv6110x_release(struct dvb_frontend *fe) +{ + struct stv6110x_state *stv6110x = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(stv6110x); +} + +static void st6110x_init_regs(struct stv6110x_state *stv6110x) +{ + u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; + + memcpy(stv6110x->regs, default_regs, 8); +} + +static void stv6110x_setup_divider(struct stv6110x_state *stv6110x) +{ + switch (stv6110x->config->clk_div) { + default: + case 1: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 0); + break; + case 2: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 1); + break; + case 4: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 2); + break; + case 8: + case 0: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 3); + break; + } +} + +static const struct dvb_tuner_ops stv6110x_ops = { + .info = { + .name = "STV6110(A) Silicon Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + }, + .release = stv6110x_release +}; + +static struct stv6110x_devctl stv6110x_ctl = { + .tuner_init = stv6110x_init, + .tuner_sleep = stv6110x_sleep, + .tuner_set_mode = stv6110x_set_mode, + .tuner_set_frequency = stv6110x_set_frequency, + .tuner_get_frequency = stv6110x_get_frequency, + .tuner_set_bandwidth = stv6110x_set_bandwidth, + .tuner_get_bandwidth = stv6110x_get_bandwidth, + .tuner_set_bbgain = stv6110x_set_bbgain, + .tuner_get_bbgain = stv6110x_get_bbgain, + .tuner_set_refclk = stv6110x_set_refclock, + .tuner_get_status = stv6110x_get_status, +}; + +static void stv6110x_set_frontend_opts(struct stv6110x_state *stv6110x) +{ + stv6110x->frontend->tuner_priv = stv6110x; + stv6110x->frontend->ops.tuner_ops = stv6110x_ops; +} + +static struct stv6110x_devctl *stv6110x_get_devctl(struct i2c_client *client) +{ + struct stv6110x_state *stv6110x = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return stv6110x->devctl; +} + +static int stv6110x_probe(struct i2c_client *client) +{ + struct stv6110x_config *config = client->dev.platform_data; + + struct stv6110x_state *stv6110x; + + stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL); + if (!stv6110x) + return -ENOMEM; + + stv6110x->frontend = config->frontend; + stv6110x->i2c = client->adapter; + stv6110x->config = config; + stv6110x->devctl = &stv6110x_ctl; + + st6110x_init_regs(stv6110x); + stv6110x_setup_divider(stv6110x); + stv6110x_set_frontend_opts(stv6110x); + + dev_info(&stv6110x->i2c->dev, "Probed STV6110x\n"); + + i2c_set_clientdata(client, stv6110x); + + /* setup callbacks */ + config->get_devctl = stv6110x_get_devctl; + + return 0; +} + +static void stv6110x_remove(struct i2c_client *client) +{ + struct stv6110x_state *stv6110x = i2c_get_clientdata(client); + + stv6110x_release(stv6110x->frontend); +} + +const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, + const struct stv6110x_config *config, + struct i2c_adapter *i2c) +{ + struct stv6110x_state *stv6110x; + + stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL); + if (!stv6110x) + return NULL; + + stv6110x->frontend = fe; + stv6110x->i2c = i2c; + stv6110x->config = config; + stv6110x->devctl = &stv6110x_ctl; + + st6110x_init_regs(stv6110x); + stv6110x_setup_divider(stv6110x); + stv6110x_set_frontend_opts(stv6110x); + + fe->tuner_priv = stv6110x; + fe->ops.tuner_ops = stv6110x_ops; + + dev_info(&stv6110x->i2c->dev, "Attaching STV6110x\n"); + return stv6110x->devctl; +} +EXPORT_SYMBOL_GPL(stv6110x_attach); + +static const struct i2c_device_id stv6110x_id_table[] = { + {"stv6110x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); + +static struct i2c_driver stv6110x_driver = { + .driver = { + .name = "stv6110x", + .suppress_bind_attrs = true, + }, + .probe = stv6110x_probe, + .remove = stv6110x_remove, + .id_table = stv6110x_id_table, +}; + +module_i2c_driver(stv6110x_driver); + +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("STV6110x Silicon tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h new file mode 100644 index 0000000000..1feade3158 --- /dev/null +++ b/drivers/media/dvb-frontends/stv6110x.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STV6110(A) Silicon tuner driver + + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STV6110x_H +#define __STV6110x_H + +struct stv6110x_config { + u8 addr; + u32 refclk; + u8 clk_div; /* divisor value for the output clock */ + struct dvb_frontend *frontend; + + struct stv6110x_devctl* (*get_devctl)(struct i2c_client *i2c); +}; + +enum tuner_mode { + TUNER_SLEEP = 1, + TUNER_WAKE, +}; + +enum tuner_status { + TUNER_PHASELOCKED = 1, +}; + +struct stv6110x_devctl { + int (*tuner_init) (struct dvb_frontend *fe); + int (*tuner_sleep) (struct dvb_frontend *fe); + int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); + int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); + int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); + int (*tuner_set_bandwidth) (struct dvb_frontend *fe, u32 bandwidth); + int (*tuner_get_bandwidth) (struct dvb_frontend *fe, u32 *bandwidth); + int (*tuner_set_bbgain) (struct dvb_frontend *fe, u32 gain); + int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain); + int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk); + int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status); +}; + + +#if IS_REACHABLE(CONFIG_DVB_STV6110x) + +extern const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, + const struct stv6110x_config *config, + struct i2c_adapter *i2c); + +#else +static inline const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, + const struct stv6110x_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_STV6110x */ + +#endif /* __STV6110x_H */ diff --git a/drivers/media/dvb-frontends/stv6110x_priv.h b/drivers/media/dvb-frontends/stv6110x_priv.h new file mode 100644 index 0000000000..b27769558f --- /dev/null +++ b/drivers/media/dvb-frontends/stv6110x_priv.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STV6110(A) Silicon tuner driver + + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STV6110x_PRIV_H +#define __STV6110x_PRIV_H + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 +#define FE_DEBUGREG 4 + +#define dprintk(__y, __z, format, arg...) do { \ + if (__z) { \ + if ((verbose > FE_ERROR) && (verbose > __y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_NOTICE) && (verbose > __y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_INFO) && (verbose > __y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((verbose > FE_DEBUG) && (verbose > __y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (verbose > __y) \ + printk(format, ##arg); \ + } \ +} while (0) + + +#define STV6110x_SETFIELD(mask, bitf, val) \ + (mask = (mask & (~(((1 << STV6110x_WIDTH_##bitf) - 1) << \ + STV6110x_OFFST_##bitf))) | \ + (val << STV6110x_OFFST_##bitf)) + +#define STV6110x_GETFIELD(bitf, val) \ + ((val >> STV6110x_OFFST_##bitf) & \ + ((1 << STV6110x_WIDTH_##bitf) - 1)) + +#define MAKEWORD16(a, b) (((a) << 8) | (b)) + +#define LSB(x) ((x & 0xff)) +#define MSB(y) ((y >> 8) & 0xff) + +#define TRIALS 10 +#define R_DIV(__div) (1 << (__div + 1)) +#define REFCLOCK_kHz (stv6110x->config->refclk / 1000) +#define REFCLOCK_MHz (stv6110x->config->refclk / 1000000) + +struct stv6110x_state { + struct dvb_frontend *frontend; + struct i2c_adapter *i2c; + const struct stv6110x_config *config; + u8 regs[8]; + + struct stv6110x_devctl *devctl; +}; + +#endif /* __STV6110x_PRIV_H */ diff --git a/drivers/media/dvb-frontends/stv6110x_reg.h b/drivers/media/dvb-frontends/stv6110x_reg.h new file mode 100644 index 0000000000..deb17d293b --- /dev/null +++ b/drivers/media/dvb-frontends/stv6110x_reg.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + STV6110(A) Silicon tuner driver + + Copyright (C) Manu Abraham <abraham.manu@gmail.com> + + Copyright (C) ST Microelectronics + +*/ + +#ifndef __STV6110x_REG_H +#define __STV6110x_REG_H + +#define STV6110x_CTRL1 0x00 +#define STV6110x_OFFST_CTRL1_K 3 +#define STV6110x_WIDTH_CTRL1_K 5 +#define STV6110x_OFFST_CTRL1_LPT 2 +#define STV6110x_WIDTH_CTRL1_LPT 1 +#define STV6110x_OFFST_CTRL1_RX 1 +#define STV6110x_WIDTH_CTRL1_RX 1 +#define STV6110x_OFFST_CTRL1_SYN 0 +#define STV6110x_WIDTH_CTRL1_SYN 1 + +#define STV6110x_CTRL2 0x01 +#define STV6110x_OFFST_CTRL2_CO_DIV 6 +#define STV6110x_WIDTH_CTRL2_CO_DIV 2 +#define STV6110x_OFFST_CTRL2_RSVD 5 +#define STV6110x_WIDTH_CTRL2_RSVD 1 +#define STV6110x_OFFST_CTRL2_REFOUT_SEL 4 +#define STV6110x_WIDTH_CTRL2_REFOUT_SEL 1 +#define STV6110x_OFFST_CTRL2_BBGAIN 0 +#define STV6110x_WIDTH_CTRL2_BBGAIN 4 + +#define STV6110x_TNG0 0x02 +#define STV6110x_OFFST_TNG0_N_DIV_7_0 0 +#define STV6110x_WIDTH_TNG0_N_DIV_7_0 8 + +#define STV6110x_TNG1 0x03 +#define STV6110x_OFFST_TNG1_R_DIV 6 +#define STV6110x_WIDTH_TNG1_R_DIV 2 +#define STV6110x_OFFST_TNG1_PRESC32_ON 5 +#define STV6110x_WIDTH_TNG1_PRESC32_ON 1 +#define STV6110x_OFFST_TNG1_DIV4SEL 4 +#define STV6110x_WIDTH_TNG1_DIV4SEL 1 +#define STV6110x_OFFST_TNG1_N_DIV_11_8 0 +#define STV6110x_WIDTH_TNG1_N_DIV_11_8 4 + + +#define STV6110x_CTRL3 0x04 +#define STV6110x_OFFST_CTRL3_DCLOOP_OFF 7 +#define STV6110x_WIDTH_CTRL3_DCLOOP_OFF 1 +#define STV6110x_OFFST_CTRL3_RCCLK_OFF 6 +#define STV6110x_WIDTH_CTRL3_RCCLK_OFF 1 +#define STV6110x_OFFST_CTRL3_ICP 5 +#define STV6110x_WIDTH_CTRL3_ICP 1 +#define STV6110x_OFFST_CTRL3_CF 0 +#define STV6110x_WIDTH_CTRL3_CF 5 + +#define STV6110x_STAT1 0x05 +#define STV6110x_OFFST_STAT1_CALVCO_STRT 2 +#define STV6110x_WIDTH_STAT1_CALVCO_STRT 1 +#define STV6110x_OFFST_STAT1_CALRC_STRT 1 +#define STV6110x_WIDTH_STAT1_CALRC_STRT 1 +#define STV6110x_OFFST_STAT1_LOCK 0 +#define STV6110x_WIDTH_STAT1_LOCK 1 + +#define STV6110x_STAT2 0x06 +#define STV6110x_STAT3 0x07 + +#endif /* __STV6110x_REG_H */ diff --git a/drivers/media/dvb-frontends/stv6111.c b/drivers/media/dvb-frontends/stv6111.c new file mode 100644 index 0000000000..2d0adb6fcb --- /dev/null +++ b/drivers/media/dvb-frontends/stv6111.c @@ -0,0 +1,681 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ST STV6111 tuner + * + * Copyright (C) 2014 Digital Devices GmbH + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <asm/div64.h> + +#include "stv6111.h" + +#include <media/dvb_frontend.h> + +struct stv { + struct i2c_adapter *i2c; + u8 adr; + + u8 reg[11]; + u32 ref_freq; + u32 frequency; +}; + +struct slookup { + s16 value; + u16 reg_value; +}; + +static const struct slookup lnagain_nf_lookup[] = { + /* Gain *100dB // Reg */ + { 2572, 0 }, + { 2575, 1 }, + { 2580, 2 }, + { 2588, 3 }, + { 2596, 4 }, + { 2611, 5 }, + { 2633, 6 }, + { 2664, 7 }, + { 2701, 8 }, + { 2753, 9 }, + { 2816, 10 }, + { 2902, 11 }, + { 2995, 12 }, + { 3104, 13 }, + { 3215, 14 }, + { 3337, 15 }, + { 3492, 16 }, + { 3614, 17 }, + { 3731, 18 }, + { 3861, 19 }, + { 3988, 20 }, + { 4124, 21 }, + { 4253, 22 }, + { 4386, 23 }, + { 4505, 24 }, + { 4623, 25 }, + { 4726, 26 }, + { 4821, 27 }, + { 4903, 28 }, + { 4979, 29 }, + { 5045, 30 }, + { 5102, 31 } +}; + +static const struct slookup lnagain_iip3_lookup[] = { + /* Gain *100dB // reg */ + { 1548, 0 }, + { 1552, 1 }, + { 1569, 2 }, + { 1565, 3 }, + { 1577, 4 }, + { 1594, 5 }, + { 1627, 6 }, + { 1656, 7 }, + { 1700, 8 }, + { 1748, 9 }, + { 1805, 10 }, + { 1896, 11 }, + { 1995, 12 }, + { 2113, 13 }, + { 2233, 14 }, + { 2366, 15 }, + { 2543, 16 }, + { 2687, 17 }, + { 2842, 18 }, + { 2999, 19 }, + { 3167, 20 }, + { 3342, 21 }, + { 3507, 22 }, + { 3679, 23 }, + { 3827, 24 }, + { 3970, 25 }, + { 4094, 26 }, + { 4210, 27 }, + { 4308, 28 }, + { 4396, 29 }, + { 4468, 30 }, + { 4535, 31 } +}; + +static const struct slookup gain_rfagc_lookup[] = { + /* Gain *100dB // reg */ + { 4870, 0x3000 }, + { 4850, 0x3C00 }, + { 4800, 0x4500 }, + { 4750, 0x4800 }, + { 4700, 0x4B00 }, + { 4650, 0x4D00 }, + { 4600, 0x4F00 }, + { 4550, 0x5100 }, + { 4500, 0x5200 }, + { 4420, 0x5500 }, + { 4316, 0x5800 }, + { 4200, 0x5B00 }, + { 4119, 0x5D00 }, + { 3999, 0x6000 }, + { 3950, 0x6100 }, + { 3876, 0x6300 }, + { 3755, 0x6600 }, + { 3641, 0x6900 }, + { 3567, 0x6B00 }, + { 3425, 0x6F00 }, + { 3350, 0x7100 }, + { 3236, 0x7400 }, + { 3118, 0x7700 }, + { 3004, 0x7A00 }, + { 2917, 0x7C00 }, + { 2776, 0x7F00 }, + { 2635, 0x8200 }, + { 2516, 0x8500 }, + { 2406, 0x8800 }, + { 2290, 0x8B00 }, + { 2170, 0x8E00 }, + { 2073, 0x9100 }, + { 1949, 0x9400 }, + { 1836, 0x9700 }, + { 1712, 0x9A00 }, + { 1631, 0x9C00 }, + { 1515, 0x9F00 }, + { 1400, 0xA200 }, + { 1323, 0xA400 }, + { 1203, 0xA700 }, + { 1091, 0xAA00 }, + { 1011, 0xAC00 }, + { 904, 0xAF00 }, + { 787, 0xB200 }, + { 685, 0xB500 }, + { 571, 0xB800 }, + { 464, 0xBB00 }, + { 374, 0xBE00 }, + { 275, 0xC200 }, + { 181, 0xC600 }, + { 102, 0xCC00 }, + { 49, 0xD900 } +}; + +/* + * This table is 6 dB too low comapred to the others (probably created with + * a different BB_MAG setting) + */ +static const struct slookup gain_channel_agc_nf_lookup[] = { + /* Gain *100dB // reg */ + { 7082, 0x3000 }, + { 7052, 0x4000 }, + { 7007, 0x4600 }, + { 6954, 0x4A00 }, + { 6909, 0x4D00 }, + { 6833, 0x5100 }, + { 6753, 0x5400 }, + { 6659, 0x5700 }, + { 6561, 0x5A00 }, + { 6472, 0x5C00 }, + { 6366, 0x5F00 }, + { 6259, 0x6100 }, + { 6151, 0x6400 }, + { 6026, 0x6700 }, + { 5920, 0x6900 }, + { 5835, 0x6B00 }, + { 5770, 0x6C00 }, + { 5681, 0x6E00 }, + { 5596, 0x7000 }, + { 5503, 0x7200 }, + { 5429, 0x7300 }, + { 5319, 0x7500 }, + { 5220, 0x7700 }, + { 5111, 0x7900 }, + { 4983, 0x7B00 }, + { 4876, 0x7D00 }, + { 4755, 0x7F00 }, + { 4635, 0x8100 }, + { 4499, 0x8300 }, + { 4405, 0x8500 }, + { 4323, 0x8600 }, + { 4233, 0x8800 }, + { 4156, 0x8A00 }, + { 4038, 0x8C00 }, + { 3935, 0x8E00 }, + { 3823, 0x9000 }, + { 3712, 0x9200 }, + { 3601, 0x9500 }, + { 3511, 0x9700 }, + { 3413, 0x9900 }, + { 3309, 0x9B00 }, + { 3213, 0x9D00 }, + { 3088, 0x9F00 }, + { 2992, 0xA100 }, + { 2878, 0xA400 }, + { 2769, 0xA700 }, + { 2645, 0xAA00 }, + { 2538, 0xAD00 }, + { 2441, 0xB000 }, + { 2350, 0xB600 }, + { 2237, 0xBA00 }, + { 2137, 0xBF00 }, + { 2039, 0xC500 }, + { 1938, 0xDF00 }, + { 1927, 0xFF00 } +}; + +static const struct slookup gain_channel_agc_iip3_lookup[] = { + /* Gain *100dB // reg */ + { 7070, 0x3000 }, + { 7028, 0x4000 }, + { 7019, 0x4600 }, + { 6900, 0x4A00 }, + { 6811, 0x4D00 }, + { 6763, 0x5100 }, + { 6690, 0x5400 }, + { 6644, 0x5700 }, + { 6617, 0x5A00 }, + { 6598, 0x5C00 }, + { 6462, 0x5F00 }, + { 6348, 0x6100 }, + { 6197, 0x6400 }, + { 6154, 0x6700 }, + { 6098, 0x6900 }, + { 5893, 0x6B00 }, + { 5812, 0x6C00 }, + { 5773, 0x6E00 }, + { 5723, 0x7000 }, + { 5661, 0x7200 }, + { 5579, 0x7300 }, + { 5460, 0x7500 }, + { 5308, 0x7700 }, + { 5099, 0x7900 }, + { 4910, 0x7B00 }, + { 4800, 0x7D00 }, + { 4785, 0x7F00 }, + { 4635, 0x8100 }, + { 4466, 0x8300 }, + { 4314, 0x8500 }, + { 4295, 0x8600 }, + { 4144, 0x8800 }, + { 3920, 0x8A00 }, + { 3889, 0x8C00 }, + { 3771, 0x8E00 }, + { 3655, 0x9000 }, + { 3446, 0x9200 }, + { 3298, 0x9500 }, + { 3083, 0x9700 }, + { 3015, 0x9900 }, + { 2833, 0x9B00 }, + { 2746, 0x9D00 }, + { 2632, 0x9F00 }, + { 2598, 0xA100 }, + { 2480, 0xA400 }, + { 2236, 0xA700 }, + { 2171, 0xAA00 }, + { 2060, 0xAD00 }, + { 1999, 0xB000 }, + { 1974, 0xB600 }, + { 1820, 0xBA00 }, + { 1741, 0xBF00 }, + { 1655, 0xC500 }, + { 1444, 0xDF00 }, + { 1325, 0xFF00 }, +}; + +static inline u32 muldiv32(u32 a, u32 b, u32 c) +{ + u64 tmp64; + + tmp64 = (u64)a * (u64)b; + do_div(tmp64, c); + + return (u32)tmp64; +} + +static int i2c_read(struct i2c_adapter *adap, + u8 adr, u8 *msg, int len, u8 *answ, int alen) +{ + struct i2c_msg msgs[2] = { { .addr = adr, .flags = 0, + .buf = msg, .len = len}, + { .addr = adr, .flags = I2C_M_RD, + .buf = answ, .len = alen } }; + if (i2c_transfer(adap, msgs, 2) != 2) { + dev_err(&adap->dev, "i2c read error\n"); + return -EIO; + } + return 0; +} + +static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, + .buf = data, .len = len}; + + if (i2c_transfer(adap, &msg, 1) != 1) { + dev_err(&adap->dev, "i2c write error\n"); + return -EIO; + } + return 0; +} + +static int write_regs(struct stv *state, int reg, int len) +{ + u8 d[12]; + + memcpy(&d[1], &state->reg[reg], len); + d[0] = reg; + return i2c_write(state->i2c, state->adr, d, len + 1); +} + +static int write_reg(struct stv *state, u8 reg, u8 val) +{ + u8 d[2] = {reg, val}; + + return i2c_write(state->i2c, state->adr, d, 2); +} + +static int read_reg(struct stv *state, u8 reg, u8 *val) +{ + return i2c_read(state->i2c, state->adr, ®, 1, val, 1); +} + +static int wait_for_call_done(struct stv *state, u8 mask) +{ + int status = 0; + u32 lock_retry_count = 10; + + while (lock_retry_count > 0) { + u8 regval; + + status = read_reg(state, 9, ®val); + if (status < 0) + return status; + + if ((regval & mask) == 0) + break; + usleep_range(4000, 6000); + lock_retry_count -= 1; + + status = -EIO; + } + return status; +} + +static void init_state(struct stv *state) +{ + u32 clkdiv = 0; + u32 agcmode = 0; + u32 agcref = 2; + u32 agcset = 0xffffffff; + u32 bbmode = 0xffffffff; + + state->reg[0] = 0x08; + state->reg[1] = 0x41; + state->reg[2] = 0x8f; + state->reg[3] = 0x00; + state->reg[4] = 0xce; + state->reg[5] = 0x54; + state->reg[6] = 0x55; + state->reg[7] = 0x45; + state->reg[8] = 0x46; + state->reg[9] = 0xbd; + state->reg[10] = 0x11; + + state->ref_freq = 16000; + + if (clkdiv <= 3) + state->reg[0x00] |= (clkdiv & 0x03); + if (agcmode <= 3) { + state->reg[0x03] |= (agcmode << 5); + if (agcmode == 0x01) + state->reg[0x01] |= 0x30; + } + if (bbmode <= 3) + state->reg[0x01] = (state->reg[0x01] & ~0x30) | (bbmode << 4); + if (agcref <= 7) + state->reg[0x03] |= agcref; + if (agcset <= 31) + state->reg[0x02] = (state->reg[0x02] & ~0x1F) | agcset | 0x40; +} + +static int attach_init(struct stv *state) +{ + if (write_regs(state, 0, 11)) + return -ENODEV; + return 0; +} + +static void release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int set_bandwidth(struct dvb_frontend *fe, u32 cutoff_frequency) +{ + struct stv *state = fe->tuner_priv; + u32 index = (cutoff_frequency + 999999) / 1000000; + int stat = 0; + + if (index < 6) + index = 6; + if (index > 50) + index = 50; + if ((state->reg[0x08] & ~0xFC) == ((index - 6) << 2)) + return 0; + + state->reg[0x08] = (state->reg[0x08] & ~0xFC) | ((index - 6) << 2); + state->reg[0x09] = (state->reg[0x09] & ~0x0C) | 0x08; + if (fe->ops.i2c_gate_ctrl) + stat = fe->ops.i2c_gate_ctrl(fe, 1); + if (!stat) { + write_regs(state, 0x08, 2); + wait_for_call_done(state, 0x08); + } + if (fe->ops.i2c_gate_ctrl && !stat) + fe->ops.i2c_gate_ctrl(fe, 0); + return stat; +} + +static int set_lof(struct stv *state, u32 local_frequency, u32 cutoff_frequency) +{ + u32 index = (cutoff_frequency + 999999) / 1000000; + u32 frequency = (local_frequency + 500) / 1000; + u32 p = 1, psel = 0, fvco, div, frac; + u8 icp, tmp; + + if (index < 6) + index = 6; + if (index > 50) + index = 50; + + if (frequency <= 1300000) { + p = 4; + psel = 1; + } else { + p = 2; + psel = 0; + } + fvco = frequency * p; + div = fvco / state->ref_freq; + frac = fvco % state->ref_freq; + frac = muldiv32(frac, 0x40000, state->ref_freq); + + icp = 0; + if (fvco < 2700000) + icp = 0; + else if (fvco < 2950000) + icp = 1; + else if (fvco < 3300000) + icp = 2; + else if (fvco < 3700000) + icp = 3; + else if (fvco < 4200000) + icp = 5; + else if (fvco < 4800000) + icp = 6; + else + icp = 7; + + state->reg[0x02] |= 0x80; /* LNA IIP3 Mode */ + + state->reg[0x03] = (state->reg[0x03] & ~0x80) | (psel << 7); + state->reg[0x04] = (div & 0xFF); + state->reg[0x05] = (((div >> 8) & 0x01) | ((frac & 0x7F) << 1)) & 0xff; + state->reg[0x06] = ((frac >> 7) & 0xFF); + state->reg[0x07] = (state->reg[0x07] & ~0x07) | ((frac >> 15) & 0x07); + state->reg[0x07] = (state->reg[0x07] & ~0xE0) | (icp << 5); + + state->reg[0x08] = (state->reg[0x08] & ~0xFC) | ((index - 6) << 2); + /* Start cal vco,CF */ + state->reg[0x09] = (state->reg[0x09] & ~0x0C) | 0x0C; + write_regs(state, 2, 8); + + wait_for_call_done(state, 0x0C); + + usleep_range(10000, 12000); + + read_reg(state, 0x03, &tmp); + if (tmp & 0x10) { + state->reg[0x02] &= ~0x80; /* LNA NF Mode */ + write_regs(state, 2, 1); + } + read_reg(state, 0x08, &tmp); + + state->frequency = frequency; + + return 0; +} + +static int set_params(struct dvb_frontend *fe) +{ + struct stv *state = fe->tuner_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq, cutoff; + int stat = 0; + + if (p->delivery_system != SYS_DVBS && p->delivery_system != SYS_DVBS2) + return -EINVAL; + + freq = p->frequency * 1000; + cutoff = 5000000 + muldiv32(p->symbol_rate, 135, 200); + + if (fe->ops.i2c_gate_ctrl) + stat = fe->ops.i2c_gate_ctrl(fe, 1); + if (!stat) + set_lof(state, freq, cutoff); + if (fe->ops.i2c_gate_ctrl && !stat) + fe->ops.i2c_gate_ctrl(fe, 0); + return 0; +} + +static s32 table_lookup(const struct slookup *table, + int table_size, u16 reg_value) +{ + s32 gain; + s32 reg_diff; + int imin = 0; + int imax = table_size - 1; + int i; + + /* Assumes Table[0].RegValue < Table[imax].RegValue */ + if (reg_value <= table[0].reg_value) { + gain = table[0].value; + } else if (reg_value >= table[imax].reg_value) { + gain = table[imax].value; + } else { + while ((imax - imin) > 1) { + i = (imax + imin) / 2; + if ((table[imin].reg_value <= reg_value) && + (reg_value <= table[i].reg_value)) + imax = i; + else + imin = i; + } + reg_diff = table[imax].reg_value - table[imin].reg_value; + gain = table[imin].value; + if (reg_diff != 0) + gain += ((s32)(reg_value - table[imin].reg_value) * + (s32)(table[imax].value + - table[imin].value)) / reg_diff; + } + return gain; +} + +static int get_rf_strength(struct dvb_frontend *fe, u16 *st) +{ + struct stv *state = fe->tuner_priv; + u16 rfagc = *st; + s32 gain; + + if ((state->reg[0x03] & 0x60) == 0) { + /* RF Mode, Read AGC ADC */ + u8 reg = 0; + int stat = 0; + + if (fe->ops.i2c_gate_ctrl) + stat = fe->ops.i2c_gate_ctrl(fe, 1); + if (!stat) { + write_reg(state, 0x02, state->reg[0x02] | 0x20); + read_reg(state, 2, ®); + if (reg & 0x20) + read_reg(state, 2, ®); + } + if (fe->ops.i2c_gate_ctrl && !stat) + fe->ops.i2c_gate_ctrl(fe, 0); + + if ((state->reg[0x02] & 0x80) == 0) + /* NF */ + gain = table_lookup(lnagain_nf_lookup, + ARRAY_SIZE(lnagain_nf_lookup), + reg & 0x1F); + else + /* IIP3 */ + gain = table_lookup(lnagain_iip3_lookup, + ARRAY_SIZE(lnagain_iip3_lookup), + reg & 0x1F); + + gain += table_lookup(gain_rfagc_lookup, + ARRAY_SIZE(gain_rfagc_lookup), rfagc); + + gain -= 2400; + } else { + /* Channel Mode */ + if ((state->reg[0x02] & 0x80) == 0) { + /* NF */ + gain = table_lookup( + gain_channel_agc_nf_lookup, + ARRAY_SIZE(gain_channel_agc_nf_lookup), rfagc); + + gain += 600; + } else { + /* IIP3 */ + gain = table_lookup( + gain_channel_agc_iip3_lookup, + ARRAY_SIZE(gain_channel_agc_iip3_lookup), + rfagc); + } + } + + if (state->frequency > 0) + /* Tilt correction ( 0.00016 dB/MHz ) */ + gain -= ((((s32)(state->frequency / 1000) - 1550) * 2) / 12); + + /* + (BBGain * 10); */ + gain += (s32)((state->reg[0x01] & 0xC0) >> 6) * 600 - 1300; + + if (gain < 0) + gain = 0; + else if (gain > 10000) + gain = 10000; + + *st = 10000 - gain; + + return 0; +} + +static const struct dvb_tuner_ops tuner_ops = { + .info = { + .name = "ST STV6111", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + }, + .set_params = set_params, + .release = release, + .get_rf_strength = get_rf_strength, + .set_bandwidth = set_bandwidth, +}; + +struct dvb_frontend *stv6111_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 adr) +{ + struct stv *state; + int stat = -ENODEV; + int gatestat = 0; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + state->adr = adr; + state->i2c = i2c; + memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops)); + init_state(state); + + if (fe->ops.i2c_gate_ctrl) + gatestat = fe->ops.i2c_gate_ctrl(fe, 1); + if (!gatestat) + stat = attach_init(state); + if (fe->ops.i2c_gate_ctrl && !gatestat) + fe->ops.i2c_gate_ctrl(fe, 0); + if (stat < 0) { + kfree(state); + return NULL; + } + fe->tuner_priv = state; + return fe; +} +EXPORT_SYMBOL_GPL(stv6111_attach); + +MODULE_DESCRIPTION("ST STV6111 satellite tuner driver"); +MODULE_AUTHOR("Ralph Metzler, Manfred Voelkel"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/stv6111.h b/drivers/media/dvb-frontends/stv6111.h new file mode 100644 index 0000000000..f172c3e3d8 --- /dev/null +++ b/drivers/media/dvb-frontends/stv6111.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the ST STV6111 tuner + * + * Copyright (C) 2014 Digital Devices GmbH + */ + +#ifndef _STV6111_H_ +#define _STV6111_H_ + +#if IS_REACHABLE(CONFIG_DVB_STV6111) + +struct dvb_frontend *stv6111_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 adr); + +#else + +static inline struct dvb_frontend *stv6111_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 adr) +{ + pr_warn("%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_STV6111 */ + +#endif /* _STV6111_H_ */ diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c new file mode 100644 index 0000000000..1f8cbf4555 --- /dev/null +++ b/drivers/media/dvb-frontends/tc90522.c @@ -0,0 +1,852 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Toshiba TC90522 Demodulator + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> + */ + +/* + * NOTICE: + * This driver is incomplete and lacks init/config of the chips, + * as the necessary info is not disclosed. + * It assumes that users of this driver (such as a PCI bridge of + * DTV receiver cards) properly init and configure the chip + * via I2C *before* calling this driver's init() function. + * + * Currently, PT3 driver is the only one that uses this driver, + * and contains init/config code in its firmware. + * Thus some part of the code might be dependent on PT3 specific config. + */ + +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/dvb/frontend.h> +#include <linux/int_log.h> +#include "tc90522.h" + +#define TC90522_I2C_THRU_REG 0xfe + +#define TC90522_MODULE_IDX(addr) (((u8)(addr) & 0x02U) >> 1) + +struct tc90522_state { + struct tc90522_config cfg; + struct dvb_frontend fe; + struct i2c_client *i2c_client; + struct i2c_adapter tuner_i2c; + + bool lna; +}; + +struct reg_val { + u8 reg; + u8 val; +}; + +static int +reg_write(struct tc90522_state *state, const struct reg_val *regs, int num) +{ + int i, ret; + struct i2c_msg msg; + + ret = 0; + msg.addr = state->i2c_client->addr; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < num; i++) { + msg.buf = (u8 *)®s[i]; + ret = i2c_transfer(state->i2c_client->adapter, &msg, 1); + if (ret == 0) + ret = -EIO; + if (ret < 0) + return ret; + } + return 0; +} + +static int reg_read(struct tc90522_state *state, u8 reg, u8 *val, u8 len) +{ + struct i2c_msg msgs[2] = { + { + .addr = state->i2c_client->addr, + .flags = 0, + .buf = ®, + .len = 1, + }, + { + .addr = state->i2c_client->addr, + .flags = I2C_M_RD, + .buf = val, + .len = len, + }, + }; + int ret; + + ret = i2c_transfer(state->i2c_client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret == ARRAY_SIZE(msgs)) + ret = 0; + else if (ret >= 0) + ret = -EIO; + return ret; +} + +static struct tc90522_state *cfg_to_state(struct tc90522_config *c) +{ + return container_of(c, struct tc90522_state, cfg); +} + + +static int tc90522s_set_tsid(struct dvb_frontend *fe) +{ + struct reg_val set_tsid[] = { + { 0x8f, 00 }, + { 0x90, 00 } + }; + + set_tsid[0].val = (fe->dtv_property_cache.stream_id & 0xff00) >> 8; + set_tsid[1].val = fe->dtv_property_cache.stream_id & 0xff; + return reg_write(fe->demodulator_priv, set_tsid, ARRAY_SIZE(set_tsid)); +} + +static int tc90522t_set_layers(struct dvb_frontend *fe) +{ + struct reg_val rv; + u8 laysel; + + laysel = ~fe->dtv_property_cache.isdbt_layer_enabled & 0x07; + laysel = (laysel & 0x01) << 2 | (laysel & 0x02) | (laysel & 0x04) >> 2; + rv.reg = 0x71; + rv.val = laysel; + return reg_write(fe->demodulator_priv, &rv, 1); +} + +/* frontend ops */ + +static int tc90522s_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct tc90522_state *state; + int ret; + u8 reg; + + state = fe->demodulator_priv; + ret = reg_read(state, 0xc3, ®, 1); + if (ret < 0) + return ret; + + *status = 0; + if (reg & 0x80) /* input level under min ? */ + return 0; + *status |= FE_HAS_SIGNAL; + + if (reg & 0x60) /* carrier? */ + return 0; + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC; + + if (reg & 0x10) + return 0; + if (reg_read(state, 0xc5, ®, 1) < 0 || !(reg & 0x03)) + return 0; + *status |= FE_HAS_LOCK; + return 0; +} + +static int tc90522t_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct tc90522_state *state; + int ret; + u8 reg; + + state = fe->demodulator_priv; + ret = reg_read(state, 0x96, ®, 1); + if (ret < 0) + return ret; + + *status = 0; + if (reg & 0xe0) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI + | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; + } + + ret = reg_read(state, 0x80, ®, 1); + if (ret < 0) + return ret; + + if (reg & 0xf0) + return 0; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + if (reg & 0x0c) + return 0; + *status |= FE_HAS_SYNC | FE_HAS_VITERBI; + + if (reg & 0x02) + return 0; + *status |= FE_HAS_LOCK; + return 0; +} + +static const enum fe_code_rate fec_conv_sat[] = { + FEC_NONE, /* unused */ + FEC_1_2, /* for BPSK */ + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, /* for QPSK */ + FEC_2_3, /* for 8PSK. (trellis code) */ +}; + +static int tc90522s_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct tc90522_state *state; + struct dtv_fe_stats *stats; + int ret, i; + int layers; + u8 val[10]; + u32 cndat; + + state = fe->demodulator_priv; + c->delivery_system = SYS_ISDBS; + c->symbol_rate = 28860000; + + layers = 0; + ret = reg_read(state, 0xe6, val, 5); + if (ret == 0) { + u8 v; + + c->stream_id = val[0] << 8 | val[1]; + + /* high/single layer */ + v = (val[2] & 0x70) >> 4; + c->modulation = (v == 7) ? PSK_8 : QPSK; + c->fec_inner = fec_conv_sat[v]; + c->layer[0].fec = c->fec_inner; + c->layer[0].modulation = c->modulation; + c->layer[0].segment_count = val[3] & 0x3f; /* slots */ + + /* low layer */ + v = (val[2] & 0x07); + c->layer[1].fec = fec_conv_sat[v]; + if (v == 0) /* no low layer */ + c->layer[1].segment_count = 0; + else + c->layer[1].segment_count = val[4] & 0x3f; /* slots */ + /* + * actually, BPSK if v==1, but not defined in + * enum fe_modulation + */ + c->layer[1].modulation = QPSK; + layers = (v > 0) ? 2 : 1; + } + + /* statistics */ + + stats = &c->strength; + stats->len = 0; + /* let the connected tuner set RSSI property cache */ + if (fe->ops.tuner_ops.get_rf_strength) { + u16 dummy; + + fe->ops.tuner_ops.get_rf_strength(fe, &dummy); + } + + stats = &c->cnr; + stats->len = 1; + stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE; + cndat = 0; + ret = reg_read(state, 0xbc, val, 2); + if (ret == 0) + cndat = val[0] << 8 | val[1]; + if (cndat >= 3000) { + u32 p, p4; + s64 cn; + + cndat -= 3000; /* cndat: 4.12 fixed point float */ + /* + * cnr[mdB] = -1634.6 * P^5 + 14341 * P^4 - 50259 * P^3 + * + 88977 * P^2 - 89565 * P + 58857 + * (P = sqrt(cndat) / 64) + */ + /* p := sqrt(cndat) << 8 = P << 14, 2.14 fixed point float */ + /* cn = cnr << 3 */ + p = int_sqrt(cndat << 16); + p4 = cndat * cndat; + cn = div64_s64(-16346LL * p4 * p, 10) >> 35; + cn += (14341LL * p4) >> 21; + cn -= (50259LL * cndat * p) >> 23; + cn += (88977LL * cndat) >> 9; + cn -= (89565LL * p) >> 11; + cn += 58857 << 3; + stats->stat[0].svalue = cn >> 3; + stats->stat[0].scale = FE_SCALE_DECIBEL; + } + + /* per-layer post viterbi BER (or PER? config dependent?) */ + stats = &c->post_bit_error; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + ret = reg_read(state, 0xeb, val, 10); + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = val[i * 5] << 16 + | val[i * 5 + 1] << 8 | val[i * 5 + 2]; + } + } + stats = &c->post_bit_count; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = + val[i * 5 + 3] << 8 | val[i * 5 + 4]; + stats->stat[i].uvalue *= 204 * 8; + } + } + + return 0; +} + + +static const enum fe_transmit_mode tm_conv[] = { + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_4K, + TRANSMISSION_MODE_8K, + 0 +}; + +static const enum fe_code_rate fec_conv_ter[] = { + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, 0, 0, 0 +}; + +static const enum fe_modulation mod_conv[] = { + DQPSK, QPSK, QAM_16, QAM_64, 0, 0, 0, 0 +}; + +static int tc90522t_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct tc90522_state *state; + struct dtv_fe_stats *stats; + int ret, i; + int layers; + u8 val[15], mode; + u32 cndat; + + state = fe->demodulator_priv; + c->delivery_system = SYS_ISDBT; + c->bandwidth_hz = 6000000; + mode = 1; + ret = reg_read(state, 0xb0, val, 1); + if (ret == 0) { + mode = (val[0] & 0xc0) >> 6; + c->transmission_mode = tm_conv[mode]; + c->guard_interval = (val[0] & 0x30) >> 4; + } + + ret = reg_read(state, 0xb2, val, 6); + layers = 0; + if (ret == 0) { + u8 v; + + c->isdbt_partial_reception = val[0] & 0x01; + c->isdbt_sb_mode = (val[0] & 0xc0) == 0x40; + + /* layer A */ + v = (val[2] & 0x78) >> 3; + if (v == 0x0f) + c->layer[0].segment_count = 0; + else { + layers++; + c->layer[0].segment_count = v; + c->layer[0].fec = fec_conv_ter[(val[1] & 0x1c) >> 2]; + c->layer[0].modulation = mod_conv[(val[1] & 0xe0) >> 5]; + v = (val[1] & 0x03) << 1 | (val[2] & 0x80) >> 7; + c->layer[0].interleaving = v; + } + + /* layer B */ + v = (val[3] & 0x03) << 2 | (val[4] & 0xc0) >> 6; + if (v == 0x0f) + c->layer[1].segment_count = 0; + else { + layers++; + c->layer[1].segment_count = v; + c->layer[1].fec = fec_conv_ter[(val[3] & 0xe0) >> 5]; + c->layer[1].modulation = mod_conv[(val[2] & 0x07)]; + c->layer[1].interleaving = (val[3] & 0x1c) >> 2; + } + + /* layer C */ + v = (val[5] & 0x1e) >> 1; + if (v == 0x0f) + c->layer[2].segment_count = 0; + else { + layers++; + c->layer[2].segment_count = v; + c->layer[2].fec = fec_conv_ter[(val[4] & 0x07)]; + c->layer[2].modulation = mod_conv[(val[4] & 0x38) >> 3]; + c->layer[2].interleaving = (val[5] & 0xe0) >> 5; + } + } + + /* statistics */ + + stats = &c->strength; + stats->len = 0; + /* let the connected tuner set RSSI property cache */ + if (fe->ops.tuner_ops.get_rf_strength) { + u16 dummy; + + fe->ops.tuner_ops.get_rf_strength(fe, &dummy); + } + + stats = &c->cnr; + stats->len = 1; + stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE; + cndat = 0; + ret = reg_read(state, 0x8b, val, 3); + if (ret == 0) + cndat = val[0] << 16 | val[1] << 8 | val[2]; + if (cndat != 0) { + u32 p, tmp; + s64 cn; + + /* + * cnr[mdB] = 0.024 P^4 - 1.6 P^3 + 39.8 P^2 + 549.1 P + 3096.5 + * (P = 10log10(5505024/cndat)) + */ + /* cn = cnr << 3 (61.3 fixed point float */ + /* p = 10log10(5505024/cndat) << 24 (8.24 fixed point float)*/ + p = intlog10(5505024) - intlog10(cndat); + p *= 10; + + cn = 24772; + cn += div64_s64(43827LL * p, 10) >> 24; + tmp = p >> 8; + cn += div64_s64(3184LL * tmp * tmp, 10) >> 32; + tmp = p >> 13; + cn -= div64_s64(128LL * tmp * tmp * tmp, 10) >> 33; + tmp = p >> 18; + cn += div64_s64(192LL * tmp * tmp * tmp * tmp, 1000) >> 24; + + stats->stat[0].svalue = cn >> 3; + stats->stat[0].scale = FE_SCALE_DECIBEL; + } + + /* per-layer post viterbi BER (or PER? config dependent?) */ + stats = &c->post_bit_error; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + ret = reg_read(state, 0x9d, val, 15); + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = val[i * 3] << 16 + | val[i * 3 + 1] << 8 | val[i * 3 + 2]; + } + } + stats = &c->post_bit_count; + memset(stats, 0, sizeof(*stats)); + stats->len = layers; + if (ret < 0) + for (i = 0; i < layers; i++) + stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE; + else { + for (i = 0; i < layers; i++) { + stats->stat[i].scale = FE_SCALE_COUNTER; + stats->stat[i].uvalue = + val[9 + i * 2] << 8 | val[9 + i * 2 + 1]; + stats->stat[i].uvalue *= 204 * 8; + } + } + + return 0; +} + +static const struct reg_val reset_sat = { 0x03, 0x01 }; +static const struct reg_val reset_ter = { 0x01, 0x40 }; + +static int tc90522_set_frontend(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + int ret; + + state = fe->demodulator_priv; + + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + else + ret = -ENODEV; + if (ret < 0) + goto failed; + + if (fe->ops.delsys[0] == SYS_ISDBS) { + ret = tc90522s_set_tsid(fe); + if (ret < 0) + goto failed; + ret = reg_write(state, &reset_sat, 1); + } else { + ret = tc90522t_set_layers(fe); + if (ret < 0) + goto failed; + ret = reg_write(state, &reset_ter, 1); + } + if (ret < 0) + goto failed; + + return 0; + +failed: + dev_warn(&state->tuner_i2c.dev, "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +static int tc90522_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *settings) +{ + if (fe->ops.delsys[0] == SYS_ISDBS) { + settings->min_delay_ms = 250; + settings->step_size = 1000; + settings->max_drift = settings->step_size * 2; + } else { + settings->min_delay_ms = 400; + settings->step_size = 142857; + settings->max_drift = settings->step_size; + } + return 0; +} + +static int tc90522_set_if_agc(struct dvb_frontend *fe, bool on) +{ + struct reg_val agc_sat[] = { + { 0x0a, 0x00 }, + { 0x10, 0x30 }, + { 0x11, 0x00 }, + { 0x03, 0x01 }, + }; + struct reg_val agc_ter[] = { + { 0x25, 0x00 }, + { 0x23, 0x4c }, + { 0x01, 0x40 }, + }; + struct tc90522_state *state; + struct reg_val *rv; + int num; + + state = fe->demodulator_priv; + if (fe->ops.delsys[0] == SYS_ISDBS) { + agc_sat[0].val = on ? 0xff : 0x00; + agc_sat[1].val |= 0x80; + agc_sat[1].val |= on ? 0x01 : 0x00; + agc_sat[2].val |= on ? 0x40 : 0x00; + rv = agc_sat; + num = ARRAY_SIZE(agc_sat); + } else { + agc_ter[0].val = on ? 0x40 : 0x00; + agc_ter[1].val |= on ? 0x00 : 0x01; + rv = agc_ter; + num = ARRAY_SIZE(agc_ter); + } + return reg_write(state, rv, num); +} + +static const struct reg_val sleep_sat = { 0x17, 0x01 }; +static const struct reg_val sleep_ter = { 0x03, 0x90 }; + +static int tc90522_sleep(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + int ret; + + state = fe->demodulator_priv; + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = reg_write(state, &sleep_sat, 1); + else { + ret = reg_write(state, &sleep_ter, 1); + if (ret == 0 && fe->ops.set_lna && + fe->dtv_property_cache.lna == LNA_AUTO) { + fe->dtv_property_cache.lna = 0; + ret = fe->ops.set_lna(fe); + fe->dtv_property_cache.lna = LNA_AUTO; + } + } + if (ret < 0) + dev_warn(&state->tuner_i2c.dev, + "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; +} + +static const struct reg_val wakeup_sat = { 0x17, 0x00 }; +static const struct reg_val wakeup_ter = { 0x03, 0x80 }; + +static int tc90522_init(struct dvb_frontend *fe) +{ + struct tc90522_state *state; + int ret; + + /* + * Because the init sequence is not public, + * the parent device/driver should have init'ed the device before. + * just wake up the device here. + */ + + state = fe->demodulator_priv; + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = reg_write(state, &wakeup_sat, 1); + else { + ret = reg_write(state, &wakeup_ter, 1); + if (ret == 0 && fe->ops.set_lna && + fe->dtv_property_cache.lna == LNA_AUTO) { + fe->dtv_property_cache.lna = 1; + ret = fe->ops.set_lna(fe); + fe->dtv_property_cache.lna = LNA_AUTO; + } + } + if (ret < 0) { + dev_warn(&state->tuner_i2c.dev, + "(%s) failed. [adap%d-fe%d]\n", + __func__, fe->dvb->num, fe->id); + return ret; + } + + /* prefer 'all-layers' to 'none' as a default */ + if (fe->dtv_property_cache.isdbt_layer_enabled == 0) + fe->dtv_property_cache.isdbt_layer_enabled = 7; + return tc90522_set_if_agc(fe, true); +} + + +/* + * tuner I2C adapter functions + */ + +static int +tc90522_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct tc90522_state *state; + struct i2c_msg *new_msgs; + int i, j; + int ret, rd_num; + u8 wbuf[256]; + u8 *p, *bufend; + + if (num <= 0) + return -EINVAL; + + rd_num = 0; + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RD) + rd_num++; + new_msgs = kmalloc_array(num + rd_num, sizeof(*new_msgs), GFP_KERNEL); + if (!new_msgs) + return -ENOMEM; + + state = i2c_get_adapdata(adap); + p = wbuf; + bufend = wbuf + sizeof(wbuf); + for (i = 0, j = 0; i < num; i++, j++) { + new_msgs[j].addr = state->i2c_client->addr; + new_msgs[j].flags = msgs[i].flags; + + if (msgs[i].flags & I2C_M_RD) { + new_msgs[j].flags &= ~I2C_M_RD; + if (p + 2 > bufend) + break; + p[0] = TC90522_I2C_THRU_REG; + p[1] = msgs[i].addr << 1 | 0x01; + new_msgs[j].buf = p; + new_msgs[j].len = 2; + p += 2; + j++; + new_msgs[j].addr = state->i2c_client->addr; + new_msgs[j].flags = msgs[i].flags; + new_msgs[j].buf = msgs[i].buf; + new_msgs[j].len = msgs[i].len; + continue; + } + + if (p + msgs[i].len + 2 > bufend) + break; + p[0] = TC90522_I2C_THRU_REG; + p[1] = msgs[i].addr << 1; + memcpy(p + 2, msgs[i].buf, msgs[i].len); + new_msgs[j].buf = p; + new_msgs[j].len = msgs[i].len + 2; + p += new_msgs[j].len; + } + + if (i < num) { + ret = -ENOMEM; + } else if (!state->cfg.split_tuner_read_i2c || rd_num == 0) { + ret = i2c_transfer(state->i2c_client->adapter, new_msgs, j); + } else { + /* + * Split transactions at each I2C_M_RD message. + * Some of the parent device require this, + * such as Friio (see. dvb-usb-gl861). + */ + int from, to; + + ret = 0; + from = 0; + do { + int r; + + to = from + 1; + while (to < j && !(new_msgs[to].flags & I2C_M_RD)) + to++; + r = i2c_transfer(state->i2c_client->adapter, + &new_msgs[from], to - from); + ret = (r <= 0) ? r : ret + r; + from = to; + } while (from < j && ret > 0); + } + + if (ret >= 0 && ret < j) + ret = -EIO; + kfree(new_msgs); + return (ret == j) ? num : ret; +} + +static u32 tc90522_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm tc90522_tuner_i2c_algo = { + .master_xfer = &tc90522_master_xfer, + .functionality = &tc90522_functionality, +}; + + +/* + * I2C driver functions + */ + +static const struct dvb_frontend_ops tc90522_ops_sat = { + .delsys = { SYS_ISDBS }, + .info = { + .name = "Toshiba TC90522 ISDB-S module", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .init = tc90522_init, + .sleep = tc90522_sleep, + .set_frontend = tc90522_set_frontend, + .get_tune_settings = tc90522_get_tune_settings, + + .get_frontend = tc90522s_get_frontend, + .read_status = tc90522s_read_status, +}; + +static const struct dvb_frontend_ops tc90522_ops_ter = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "Toshiba TC90522 ISDB-T module", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 770 * MHz, + .frequency_stepsize_hz = 142857, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .init = tc90522_init, + .sleep = tc90522_sleep, + .set_frontend = tc90522_set_frontend, + .get_tune_settings = tc90522_get_tune_settings, + + .get_frontend = tc90522t_get_frontend, + .read_status = tc90522t_read_status, +}; + + +static int tc90522_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct tc90522_state *state; + struct tc90522_config *cfg; + const struct dvb_frontend_ops *ops; + struct i2c_adapter *adap; + int ret; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + state->i2c_client = client; + + cfg = client->dev.platform_data; + memcpy(&state->cfg, cfg, sizeof(state->cfg)); + cfg->fe = state->cfg.fe = &state->fe; + ops = id->driver_data == 0 ? &tc90522_ops_sat : &tc90522_ops_ter; + memcpy(&state->fe.ops, ops, sizeof(*ops)); + state->fe.demodulator_priv = state; + + adap = &state->tuner_i2c; + adap->owner = THIS_MODULE; + adap->algo = &tc90522_tuner_i2c_algo; + adap->dev.parent = &client->dev; + strscpy(adap->name, "tc90522_sub", sizeof(adap->name)); + i2c_set_adapdata(adap, state); + ret = i2c_add_adapter(adap); + if (ret < 0) + goto free_state; + cfg->tuner_i2c = state->cfg.tuner_i2c = adap; + + i2c_set_clientdata(client, &state->cfg); + dev_info(&client->dev, "Toshiba TC90522 attached.\n"); + return 0; +free_state: + kfree(state); + return ret; +} + +static void tc90522_remove(struct i2c_client *client) +{ + struct tc90522_state *state; + + state = cfg_to_state(i2c_get_clientdata(client)); + i2c_del_adapter(&state->tuner_i2c); + kfree(state); +} + + +static const struct i2c_device_id tc90522_id[] = { + { TC90522_I2C_DEV_SAT, 0 }, + { TC90522_I2C_DEV_TER, 1 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tc90522_id); + +static struct i2c_driver tc90522_driver = { + .driver = { + .name = "tc90522", + }, + .probe = tc90522_probe, + .remove = tc90522_remove, + .id_table = tc90522_id, +}; + +module_i2c_driver(tc90522_driver); + +MODULE_DESCRIPTION("Toshiba TC90522 frontend"); +MODULE_AUTHOR("Akihiro TSUKADA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h new file mode 100644 index 0000000000..07e3813bf5 --- /dev/null +++ b/drivers/media/dvb-frontends/tc90522.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Toshiba TC90522 Demodulator + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> + */ + +/* + * The demod has 4 input (2xISDB-T and 2xISDB-S), + * and provides independent sub modules for each input. + * As the sub modules work in parallel and have the separate i2c addr's, + * this driver treats each sub module as one demod device. + */ + +#ifndef TC90522_H +#define TC90522_H + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +/* I2C device types */ +#define TC90522_I2C_DEV_SAT "tc90522sat" +#define TC90522_I2C_DEV_TER "tc90522ter" + +struct tc90522_config { + /* [OUT] frontend returned by driver */ + struct dvb_frontend *fe; + + /* [OUT] tuner I2C adapter returned by driver */ + struct i2c_adapter *tuner_i2c; + + /* [IN] use two separate I2C transactions for one tuner read */ + bool split_tuner_read_i2c; +}; + +#endif /* TC90522_H */ diff --git a/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c new file mode 100644 index 0000000000..462e12ab6b --- /dev/null +++ b/drivers/media/dvb-frontends/tda10021.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + TDA10021 - Single Chip Cable Channel Receiver driver module + used on the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> + Support for TDA10021 + +*/ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "tda1002x.h" + + +struct tda10021_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct tda1002x_config* config; + struct dvb_frontend frontend; + + u8 pwm; + u8 reg0; +}; + + +#if 0 +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +static int verbose; + +#define XIN 57840000UL + +#define FIN (XIN >> 4) + +static int tda10021_inittab_size = 0x40; +static u8 tda10021_inittab[0x40]= +{ + 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, + 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, + 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, + 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, + 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00, + 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, +}; + +static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer (state->i2c, &msg, 1); + if (ret != 1) + printk("DVB: TDA10021(%d): %s, writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + state->frontend.dvb->num, __func__, reg, data, ret); + + msleep(10); + return (ret != 1) ? -EREMOTEIO : 0; +} + +static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) +{ + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + int ret; + + ret = i2c_transfer (state->i2c, msg, 2); + // Don't print an error message if the id is read. + if (ret != 2 && reg != 0x1a) + printk("DVB: TDA10021: %s: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} + +//get access to tuner +static int lock_tuner(struct tda10021_state* state) +{ + u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 }; + struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg, 1) != 1) + { + printk("tda10021: lock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +//release access from tuner +static int unlock_tuner(struct tda10021_state* state) +{ + u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f }; + struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg_post, 1) != 1) + { + printk("tda10021: unlock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +static int tda10021_setup_reg0(struct tda10021_state *state, u8 reg0, + enum fe_spectral_inversion inversion) +{ + reg0 |= state->reg0 & 0x63; + + if ((INVERSION_ON == inversion) ^ (state->config->invert == 0)) + reg0 &= ~0x20; + else + reg0 |= 0x20; + + _tda10021_writereg (state, 0x00, reg0 & 0xfe); + _tda10021_writereg (state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + return 0; +} + +static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate) +{ + s32 BDR; + s32 BDRI; + s16 SFIL = 0; + u16 NDEC = 0; + u32 tmp, ratio; + + if (symbolrate > XIN / 2) + symbolrate = XIN / 2; + else if (symbolrate < 500000) + symbolrate = 500000; + + if (symbolrate < XIN / 16) + NDEC = 1; + if (symbolrate < XIN / 32) + NDEC = 2; + if (symbolrate < XIN / 64) + NDEC = 3; + + if (symbolrate < XIN * 10 / 123) + SFIL = 1; + if (symbolrate < XIN * 10 / 160) + SFIL = 0; + if (symbolrate < XIN * 10 / 246) + SFIL = 1; + if (symbolrate < XIN * 10 / 320) + SFIL = 0; + if (symbolrate < XIN * 10 / 492) + SFIL = 1; + if (symbolrate < XIN * 10 / 640) + SFIL = 0; + if (symbolrate < XIN * 10 / 984) + SFIL = 1; + + symbolrate <<= NDEC; + ratio = (symbolrate << 4) / FIN; + tmp = ((symbolrate << 4) % FIN) << 8; + ratio = (ratio << 8) + tmp / FIN; + tmp = (tmp % FIN) << 8; + ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN); + + BDR = ratio; + BDRI = (((XIN << 5) / symbolrate) + 1) / 2; + + if (BDRI > 0xFF) + BDRI = 0xFF; + + SFIL = (SFIL << 4) | tda10021_inittab[0x0E]; + + NDEC = (NDEC << 6) | tda10021_inittab[0x03]; + + _tda10021_writereg (state, 0x03, NDEC); + _tda10021_writereg (state, 0x0a, BDR&0xff); + _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); + _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); + + _tda10021_writereg (state, 0x0d, BDRI); + _tda10021_writereg (state, 0x0e, SFIL); + + return 0; +} + +static int tda10021_init (struct dvb_frontend *fe) +{ + struct tda10021_state* state = fe->demodulator_priv; + int i; + + dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); + + //_tda10021_writereg (fe, 0, 0); + + for (i=0; i<tda10021_inittab_size; i++) + _tda10021_writereg (state, i, tda10021_inittab[i]); + + _tda10021_writereg (state, 0x34, state->pwm); + + //Comment by markus + //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) + //0x2A[4] == BYPPLL -> Power down mode (default 1) + //0x2A[5] == LCK -> PLL Lock Flag + //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) + + //Activate PLL + _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); + return 0; +} + +struct qam_params { + u8 conf, agcref, lthr, mseth, aref; +}; + +static int tda10021_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + unsigned qam = c->modulation; + bool is_annex_c; + u32 reg0x3d; + struct tda10021_state* state = fe->demodulator_priv; + static const struct qam_params qam_params[] = { + /* Modulation Conf AGCref LTHR MSETH AREF */ + [QPSK] = { 0x14, 0x78, 0x78, 0x8c, 0x96 }, + [QAM_16] = { 0x00, 0x8c, 0x87, 0xa2, 0x91 }, + [QAM_32] = { 0x04, 0x8c, 0x64, 0x74, 0x96 }, + [QAM_64] = { 0x08, 0x6a, 0x46, 0x43, 0x6a }, + [QAM_128] = { 0x0c, 0x78, 0x36, 0x34, 0x7e }, + [QAM_256] = { 0x10, 0x5c, 0x26, 0x23, 0x6b }, + }; + + switch (delsys) { + case SYS_DVBC_ANNEX_A: + is_annex_c = false; + break; + case SYS_DVBC_ANNEX_C: + is_annex_c = true; + break; + default: + return -EINVAL; + } + + /* + * gcc optimizes the code below the same way as it would code: + * "if (qam > 5) return -EINVAL;" + * Yet, the code is clearer, as it shows what QAM standards are + * supported by the driver, and avoids the usage of magic numbers on + * it. + */ + switch (qam) { + case QPSK: + case QAM_16: + case QAM_32: + case QAM_64: + case QAM_128: + case QAM_256: + break; + default: + return -EINVAL; + } + + if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF) + return -EINVAL; + + /*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/ + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + tda10021_set_symbolrate(state, c->symbol_rate); + _tda10021_writereg(state, 0x34, state->pwm); + + _tda10021_writereg(state, 0x01, qam_params[qam].agcref); + _tda10021_writereg(state, 0x05, qam_params[qam].lthr); + _tda10021_writereg(state, 0x08, qam_params[qam].mseth); + _tda10021_writereg(state, 0x09, qam_params[qam].aref); + + /* + * Bit 0 == 0 means roll-off = 0.15 (Annex A) + * == 1 means roll-off = 0.13 (Annex C) + */ + reg0x3d = tda10021_readreg (state, 0x3d); + if (is_annex_c) + _tda10021_writereg (state, 0x3d, 0x01 | reg0x3d); + else + _tda10021_writereg (state, 0x3d, 0xfe & reg0x3d); + tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion); + + return 0; +} + +static int tda10021_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct tda10021_state* state = fe->demodulator_priv; + int sync; + + *status = 0; + //0x11[0] == EQALGO -> Equalizer algorithms state + //0x11[1] == CARLOCK -> Carrier locked + //0x11[2] == FSYNC -> Frame synchronisation + //0x11[3] == FEL -> Front End locked + //0x11[6] == NODVB -> DVB Mode Information + sync = tda10021_readreg (state, 0x11); + + if (sync & 2) + *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_SYNC|FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10021_state* state = fe->demodulator_priv; + + u32 _ber = tda10021_readreg(state, 0x14) | + (tda10021_readreg(state, 0x15) << 8) | + ((tda10021_readreg(state, 0x16) & 0x0f) << 16); + _tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0) + | (tda10021_inittab[0x10] & 0xc0)); + *ber = 10 * _ber; + + return 0; +} + +static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda10021_state* state = fe->demodulator_priv; + + u8 config = tda10021_readreg(state, 0x02); + u8 gain = tda10021_readreg(state, 0x17); + if (config & 0x02) + /* the agc value is inverted */ + gain = ~gain; + *strength = (gain << 8) | gain; + + return 0; +} + +static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda10021_state* state = fe->demodulator_priv; + + u8 quality = ~tda10021_readreg(state, 0x18); + *snr = (quality << 8) | quality; + + return 0; +} + +static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10021_state* state = fe->demodulator_priv; + + *ucblocks = tda10021_readreg (state, 0x13) & 0x7f; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; + + /* reset uncorrected block counter */ + _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); + _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); + + return 0; +} + +static int tda10021_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct tda10021_state* state = fe->demodulator_priv; + int sync; + s8 afc = 0; + + sync = tda10021_readreg(state, 0x11); + afc = tda10021_readreg(state, 0x19); + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" : + "DVB: TDA10021(%d): [AFC (%d) %dHz]\n", + state->frontend.dvb->num, afc, + -((s32)p->symbol_rate * afc) >> 10); + } + + p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF; + p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->fec_inner = FEC_NONE; + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + + if (sync & 2) + p->frequency -= ((s32)p->symbol_rate * afc) >> 10; + + return 0; +} + +static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10021_state* state = fe->demodulator_priv; + + if (enable) { + lock_tuner(state); + } else { + unlock_tuner(state); + } + return 0; +} + +static int tda10021_sleep(struct dvb_frontend* fe) +{ + struct tda10021_state* state = fe->demodulator_priv; + + _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ + _tda10021_writereg (state, 0x00, 0x80); /* standby */ + + return 0; +} + +static void tda10021_release(struct dvb_frontend* fe) +{ + struct tda10021_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops tda10021_ops; + +struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, + u8 pwm) +{ + struct tda10021_state* state = NULL; + u8 id; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->pwm = pwm; + state->reg0 = tda10021_inittab[0]; + + /* check if the demod is there */ + id = tda10021_readreg(state, 0x1a); + if ((id & 0xf0) != 0x70) goto error; + + /* Don't claim TDA10023 */ + if (id == 0x7d) + goto error; + + printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n", + state->config->demod_address, id); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops tda10021_ops = { + .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, + .info = { + .name = "Philips TDA10021 DVB-C", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = (XIN / 2) / 64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (XIN / 2) / 4, /* SACLK/4 */ + #if 0 + .frequency_tolerance = ???, + .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ + #endif + .caps = 0x400 | //FE_CAN_QAM_4 + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = tda10021_release, + + .init = tda10021_init, + .sleep = tda10021_sleep, + .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, + + .set_frontend = tda10021_set_parameters, + .get_frontend = tda10021_get_frontend, + + .read_status = tda10021_read_status, + .read_ber = tda10021_read_ber, + .read_signal_strength = tda10021_read_signal_strength, + .read_snr = tda10021_read_snr, + .read_ucblocks = tda10021_read_ucblocks, +}; + +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); + +MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(tda10021_attach); diff --git a/drivers/media/dvb-frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c new file mode 100644 index 0000000000..4c2541ecd7 --- /dev/null +++ b/drivers/media/dvb-frontends/tda10023.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + TDA10023 - DVB-C decoder + (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card) + + Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de) + Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com) + + Remotely based on tda10021.c + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> + Support for TDA10021 + +*/ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "tda1002x.h" + +#define REG0_INIT_VAL 0x23 + +struct tda10023_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct tda10023_config *config; + struct dvb_frontend frontend; + + u8 pwm; + u8 reg0; + + /* clock settings */ + u32 xtal; + u8 pll_m; + u8 pll_p; + u8 pll_n; + u32 sysclk; +}; + +#define dprintk(x...) + +static int verbose; + +static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) +{ + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + int ret; + + ret = i2c_transfer (state->i2c, msg, 2); + if (ret != 2) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error (reg == 0x%02x, ret == %i)\n", + num, __func__, reg, ret); + } + return b1[0]; +} + +static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer (state->i2c, &msg, 1); + if (ret != 1) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + num, __func__, reg, data, ret); + } + return (ret != 1) ? -EREMOTEIO : 0; +} + + +static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data) +{ + if (mask==0xff) + return tda10023_writereg(state, reg, data); + else { + u8 val; + val=tda10023_readreg(state,reg); + val&=~mask; + val|=(data&mask); + return tda10023_writereg(state, reg, val); + } +} + +static void tda10023_writetab(struct tda10023_state* state, u8* tab) +{ + u8 r,m,v; + while (1) { + r=*tab++; + m=*tab++; + v=*tab++; + if (r==0xff) { + if (m==0xff) + break; + else + msleep(m); + } + else + tda10023_writebit(state,r,m,v); + } +} + +//get access to tuner +static int lock_tuner(struct tda10023_state* state) +{ + u8 buf[2] = { 0x0f, 0xc0 }; + struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg, 1) != 1) + { + printk("tda10023: lock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +//release access from tuner +static int unlock_tuner(struct tda10023_state* state) +{ + u8 buf[2] = { 0x0f, 0x40 }; + struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg_post, 1) != 1) + { + printk("tda10023: unlock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0) +{ + reg0 |= state->reg0 & 0x63; + + tda10023_writereg (state, 0x00, reg0 & 0xfe); + tda10023_writereg (state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + return 0; +} + +static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) +{ + s32 BDR; + s32 BDRI; + s16 SFIL=0; + u16 NDEC = 0; + + /* avoid floating point operations multiplying syscloc and divider + by 10 */ + u32 sysclk_x_10 = state->sysclk * 10; + + if (sr < (u32)(sysclk_x_10/984)) { + NDEC=3; + SFIL=1; + } else if (sr < (u32)(sysclk_x_10/640)) { + NDEC=3; + SFIL=0; + } else if (sr < (u32)(sysclk_x_10/492)) { + NDEC=2; + SFIL=1; + } else if (sr < (u32)(sysclk_x_10/320)) { + NDEC=2; + SFIL=0; + } else if (sr < (u32)(sysclk_x_10/246)) { + NDEC=1; + SFIL=1; + } else if (sr < (u32)(sysclk_x_10/160)) { + NDEC=1; + SFIL=0; + } else if (sr < (u32)(sysclk_x_10/123)) { + NDEC=0; + SFIL=1; + } + + BDRI = (state->sysclk)*16; + BDRI>>=NDEC; + BDRI +=sr/2; + BDRI /=sr; + + if (BDRI>255) + BDRI=255; + + { + u64 BDRX; + + BDRX=1<<(24+NDEC); + BDRX*=sr; + do_div(BDRX, state->sysclk); /* BDRX/=SYSCLK; */ + + BDR=(s32)BDRX; + } + dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n", + sr, BDR, BDRI, NDEC); + tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); + tda10023_writereg (state, 0x0a, BDR&255); + tda10023_writereg (state, 0x0b, (BDR>>8)&255); + tda10023_writereg (state, 0x0c, (BDR>>16)&31); + tda10023_writereg (state, 0x0d, BDRI); + tda10023_writereg (state, 0x3d, (SFIL<<7)); + return 0; +} + +static int tda10023_init (struct dvb_frontend *fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 tda10023_inittab[] = { +/* reg mask val */ +/* 000 */ 0x2a, 0xff, 0x02, /* PLL3, Bypass, Power Down */ +/* 003 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 006 */ 0x2a, 0xff, 0x03, /* PLL3, Bypass, Power Down */ +/* 009 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ + /* PLL1 */ +/* 012 */ 0x28, 0xff, (state->pll_m-1), + /* PLL2 */ +/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1), + /* GPR FSAMPLING=1 */ +/* 018 */ 0x00, 0xff, REG0_INIT_VAL, +/* 021 */ 0x2a, 0xff, 0x08, /* PLL3 PSACLK=1 */ +/* 024 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 027 */ 0x1f, 0xff, 0x00, /* RESET */ +/* 030 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 033 */ 0xe6, 0x0c, 0x04, /* RSCFG_IND */ +/* 036 */ 0x10, 0xc0, 0x80, /* DECDVBCFG1 PBER=1 */ + +/* 039 */ 0x0e, 0xff, 0x82, /* GAIN1 */ +/* 042 */ 0x03, 0x08, 0x08, /* CLKCONF DYN=1 */ +/* 045 */ 0x2e, 0xbf, 0x30, /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 + PPWMTUN=0 PPWMIF=0 */ +/* 048 */ 0x01, 0xff, 0x30, /* AGCREF */ +/* 051 */ 0x1e, 0x84, 0x84, /* CONTROL SACLK_ON=1 */ +/* 054 */ 0x1b, 0xff, 0xc8, /* ADC TWOS=1 */ +/* 057 */ 0x3b, 0xff, 0xff, /* IFMAX */ +/* 060 */ 0x3c, 0xff, 0x00, /* IFMIN */ +/* 063 */ 0x34, 0xff, 0x00, /* PWMREF */ +/* 066 */ 0x35, 0xff, 0xff, /* TUNMAX */ +/* 069 */ 0x36, 0xff, 0x00, /* TUNMIN */ +/* 072 */ 0x06, 0xff, 0x7f, /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */ +/* 075 */ 0x1c, 0x30, 0x30, /* EQCONF2 STEPALGO=SGNALGO=1 */ +/* 078 */ 0x37, 0xff, 0xf6, /* DELTAF_LSB */ +/* 081 */ 0x38, 0xff, 0xff, /* DELTAF_MSB */ +/* 084 */ 0x02, 0xff, 0x93, /* AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 */ +/* 087 */ 0x2d, 0xff, 0xf6, /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */ +/* 090 */ 0x04, 0x10, 0x00, /* SWRAMP=1 */ +/* 093 */ 0x12, 0xff, TDA10023_OUTPUT_MODE_PARALLEL_B, /* + INTP1 POCLKP=1 FEL=1 MFS=0 */ +/* 096 */ 0x2b, 0x01, 0xa1, /* INTS1 */ +/* 099 */ 0x20, 0xff, 0x04, /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */ +/* 102 */ 0x2c, 0xff, 0x0d, /* INTP/S TRIP=0 TRIS=0 */ +/* 105 */ 0xc4, 0xff, 0x00, +/* 108 */ 0xc3, 0x30, 0x00, +/* 111 */ 0xb5, 0xff, 0x19, /* ERAGC_THD */ +/* 114 */ 0x00, 0x03, 0x01, /* GPR, CLBS soft reset */ +/* 117 */ 0x00, 0x03, 0x03, /* GPR, CLBS soft reset */ +/* 120 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 123 */ 0xff, 0xff, 0xff +}; + dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num); + + /* override default values if set in config */ + if (state->config->deltaf) { + tda10023_inittab[80] = (state->config->deltaf & 0xff); + tda10023_inittab[83] = (state->config->deltaf >> 8); + } + + if (state->config->output_mode) + tda10023_inittab[95] = state->config->output_mode; + + tda10023_writetab(state, tda10023_inittab); + + return 0; +} + +struct qam_params { + u8 qam, lockthr, mseth, aref, agcrefnyq, eragnyq_thd; +}; + +static int tda10023_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + unsigned qam = c->modulation; + bool is_annex_c; + struct tda10023_state* state = fe->demodulator_priv; + static const struct qam_params qam_params[] = { + /* Modulation QAM LOCKTHR MSETH AREF AGCREFNYQ ERAGCNYQ_THD */ + [QPSK] = { (5<<2), 0x78, 0x8c, 0x96, 0x78, 0x4c }, + [QAM_16] = { (0<<2), 0x87, 0xa2, 0x91, 0x8c, 0x57 }, + [QAM_32] = { (1<<2), 0x64, 0x74, 0x96, 0x8c, 0x57 }, + [QAM_64] = { (2<<2), 0x46, 0x43, 0x6a, 0x6a, 0x44 }, + [QAM_128] = { (3<<2), 0x36, 0x34, 0x7e, 0x78, 0x4c }, + [QAM_256] = { (4<<2), 0x26, 0x23, 0x6c, 0x5c, 0x3c }, + }; + + switch (delsys) { + case SYS_DVBC_ANNEX_A: + is_annex_c = false; + break; + case SYS_DVBC_ANNEX_C: + is_annex_c = true; + break; + default: + return -EINVAL; + } + + /* + * gcc optimizes the code below the same way as it would code: + * "if (qam > 5) return -EINVAL;" + * Yet, the code is clearer, as it shows what QAM standards are + * supported by the driver, and avoids the usage of magic numbers on + * it. + */ + switch (qam) { + case QPSK: + case QAM_16: + case QAM_32: + case QAM_64: + case QAM_128: + case QAM_256: + break; + default: + return -EINVAL; + } + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + tda10023_set_symbolrate(state, c->symbol_rate); + tda10023_writereg(state, 0x05, qam_params[qam].lockthr); + tda10023_writereg(state, 0x08, qam_params[qam].mseth); + tda10023_writereg(state, 0x09, qam_params[qam].aref); + tda10023_writereg(state, 0xb4, qam_params[qam].agcrefnyq); + tda10023_writereg(state, 0xb6, qam_params[qam].eragnyq_thd); +#if 0 + tda10023_writereg(state, 0x04, (c->inversion ? 0x12 : 0x32)); + tda10023_writebit(state, 0x04, 0x60, (c->inversion ? 0 : 0x20)); +#endif + tda10023_writebit(state, 0x04, 0x40, 0x40); + + if (is_annex_c) + tda10023_writebit(state, 0x3d, 0xfc, 0x03); + else + tda10023_writebit(state, 0x3d, 0xfc, 0x02); + + tda10023_setup_reg0(state, qam_params[qam].qam); + + return 0; +} + +static int tda10023_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct tda10023_state* state = fe->demodulator_priv; + int sync; + + *status = 0; + + //0x11[1] == CARLOCK -> Carrier locked + //0x11[2] == FSYNC -> Frame synchronisation + //0x11[3] == FEL -> Front End locked + //0x11[6] == NODVB -> DVB Mode Information + sync = tda10023_readreg (state, 0x11); + + if (sync & 2) + *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_SYNC|FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 a,b,c; + a=tda10023_readreg(state, 0x14); + b=tda10023_readreg(state, 0x15); + c=tda10023_readreg(state, 0x16)&0xf; + tda10023_writebit (state, 0x10, 0xc0, 0x00); + + *ber = a | (b<<8)| (c<<16); + return 0; +} + +static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 ifgain=tda10023_readreg(state, 0x2f); + + u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16; + // Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90 + if (gain>0x90) + gain=gain+2*(gain-0x90); + if (gain>255) + gain=255; + + *strength = (gain<<8)|gain; + return 0; +} + +static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda10023_state* state = fe->demodulator_priv; + + u8 quality = ~tda10023_readreg(state, 0x18); + *snr = (quality << 8) | quality; + return 0; +} + +static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 a,b,c,d; + a= tda10023_readreg (state, 0x74); + b= tda10023_readreg (state, 0x75); + c= tda10023_readreg (state, 0x76); + d= tda10023_readreg (state, 0x77); + *ucblocks = a | (b<<8)|(c<<16)|(d<<24); + + tda10023_writebit (state, 0x10, 0x20,0x00); + tda10023_writebit (state, 0x10, 0x20,0x20); + tda10023_writebit (state, 0x13, 0x01, 0x00); + + return 0; +} + +static int tda10023_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct tda10023_state* state = fe->demodulator_priv; + int sync,inv; + s8 afc = 0; + + sync = tda10023_readreg(state, 0x11); + afc = tda10023_readreg(state, 0x19); + inv = tda10023_readreg(state, 0x04); + + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" : + "DVB: TDA10023(%d): [AFC (%d) %dHz]\n", + state->frontend.dvb->num, afc, + -((s32)p->symbol_rate * afc) >> 10); + } + + p->inversion = (inv&0x20?0:1); + p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->fec_inner = FEC_NONE; + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + + if (sync & 2) + p->frequency -= ((s32)p->symbol_rate * afc) >> 10; + + return 0; +} + +static int tda10023_sleep(struct dvb_frontend* fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + + tda10023_writereg (state, 0x1b, 0x02); /* pdown ADC */ + tda10023_writereg (state, 0x00, 0x80); /* standby */ + + return 0; +} + +static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10023_state* state = fe->demodulator_priv; + + if (enable) { + lock_tuner(state); + } else { + unlock_tuner(state); + } + return 0; +} + +static void tda10023_release(struct dvb_frontend* fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops tda10023_ops; + +struct dvb_frontend *tda10023_attach(const struct tda10023_config *config, + struct i2c_adapter *i2c, + u8 pwm) +{ + struct tda10023_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* wakeup if in standby */ + tda10023_writereg (state, 0x00, 0x33); + /* check if the demod is there */ + if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + state->reg0 = REG0_INIT_VAL; + if (state->config->xtal) { + state->xtal = state->config->xtal; + state->pll_m = state->config->pll_m; + state->pll_p = state->config->pll_p; + state->pll_n = state->config->pll_n; + } else { + /* set default values if not defined in config */ + state->xtal = 28920000; + state->pll_m = 8; + state->pll_p = 4; + state->pll_n = 1; + } + + /* calc sysclk */ + state->sysclk = (state->xtal * state->pll_m / \ + (state->pll_n * state->pll_p)); + + state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64; + state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4; + + dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n", + __func__, state->xtal, state->pll_m, state->pll_p, + state->pll_n); + + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops tda10023_ops = { + .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, + .info = { + .name = "Philips TDA10023 DVB-C", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 0, /* set in tda10023_attach */ + .symbol_rate_max = 0, /* set in tda10023_attach */ + .caps = 0x400 | //FE_CAN_QAM_4 + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = tda10023_release, + + .init = tda10023_init, + .sleep = tda10023_sleep, + .i2c_gate_ctrl = tda10023_i2c_gate_ctrl, + + .set_frontend = tda10023_set_parameters, + .get_frontend = tda10023_get_frontend, + .read_status = tda10023_read_status, + .read_ber = tda10023_read_ber, + .read_signal_strength = tda10023_read_signal_strength, + .read_snr = tda10023_read_snr, + .read_ucblocks = tda10023_read_ucblocks, +}; + + +MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver"); +MODULE_AUTHOR("Georg Acher, Hartmut Birr"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(tda10023_attach); diff --git a/drivers/media/dvb-frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h new file mode 100644 index 0000000000..00491bea99 --- /dev/null +++ b/drivers/media/dvb-frontends/tda1002x.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module + used on the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> + Support for TDA10021 + +*/ + +#ifndef TDA1002x_H +#define TDA1002x_H + +#include <linux/dvb/frontend.h> + +struct tda1002x_config { + /* the demodulator's i2c address */ + u8 demod_address; + u8 invert; +}; + +enum tda10023_output_mode { + TDA10023_OUTPUT_MODE_PARALLEL_A = 0xe0, + TDA10023_OUTPUT_MODE_PARALLEL_B = 0xa1, + TDA10023_OUTPUT_MODE_PARALLEL_C = 0xa0, + TDA10023_OUTPUT_MODE_SERIAL, /* TODO: not implemented */ +}; + +struct tda10023_config { + /* the demodulator's i2c address */ + u8 demod_address; + u8 invert; + + /* clock settings */ + u32 xtal; /* defaults: 28920000 */ + u8 pll_m; /* defaults: 8 */ + u8 pll_p; /* defaults: 4 */ + u8 pll_n; /* defaults: 1 */ + + /* MPEG2 TS output mode */ + u8 output_mode; + + /* input freq offset + baseband conversion type */ + u16 deltaf; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA10021) +extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm); +#else +static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_TDA10021 + +#if IS_REACHABLE(CONFIG_DVB_TDA10023) +extern struct dvb_frontend *tda10023_attach( + const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm); +#else +static inline struct dvb_frontend *tda10023_attach( + const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_TDA10023 + +#endif // TDA1002x_H diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c new file mode 100644 index 0000000000..5d5e4e9e44 --- /dev/null +++ b/drivers/media/dvb-frontends/tda10048.c @@ -0,0 +1,1175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + NXP TDA10048HN DVB OFDM demodulator driver + + Copyright (C) 2009 Steven Toth <stoth@kernellabs.com> + + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/math64.h> +#include <asm/div64.h> +#include <media/dvb_frontend.h> +#include <linux/int_log.h> +#include "tda10048.h" + +#define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw" +#define TDA10048_DEFAULT_FIRMWARE_SIZE 24878 + +/* Register name definitions */ +#define TDA10048_IDENTITY 0x00 +#define TDA10048_VERSION 0x01 +#define TDA10048_DSP_CODE_CPT 0x0C +#define TDA10048_DSP_CODE_IN 0x0E +#define TDA10048_IN_CONF1 0x10 +#define TDA10048_IN_CONF2 0x11 +#define TDA10048_IN_CONF3 0x12 +#define TDA10048_OUT_CONF1 0x14 +#define TDA10048_OUT_CONF2 0x15 +#define TDA10048_OUT_CONF3 0x16 +#define TDA10048_AUTO 0x18 +#define TDA10048_SYNC_STATUS 0x1A +#define TDA10048_CONF_C4_1 0x1E +#define TDA10048_CONF_C4_2 0x1F +#define TDA10048_CODE_IN_RAM 0x20 +#define TDA10048_CHANNEL_INFO1_R 0x22 +#define TDA10048_CHANNEL_INFO2_R 0x23 +#define TDA10048_CHANNEL_INFO1 0x24 +#define TDA10048_CHANNEL_INFO2 0x25 +#define TDA10048_TIME_ERROR_R 0x26 +#define TDA10048_TIME_ERROR 0x27 +#define TDA10048_FREQ_ERROR_LSB_R 0x28 +#define TDA10048_FREQ_ERROR_MSB_R 0x29 +#define TDA10048_FREQ_ERROR_LSB 0x2A +#define TDA10048_FREQ_ERROR_MSB 0x2B +#define TDA10048_IT_SEL 0x30 +#define TDA10048_IT_STAT 0x32 +#define TDA10048_DSP_AD_LSB 0x3C +#define TDA10048_DSP_AD_MSB 0x3D +#define TDA10048_DSP_REG_LSB 0x3E +#define TDA10048_DSP_REG_MSB 0x3F +#define TDA10048_CONF_TRISTATE1 0x44 +#define TDA10048_CONF_TRISTATE2 0x45 +#define TDA10048_CONF_POLARITY 0x46 +#define TDA10048_GPIO_SP_DS0 0x48 +#define TDA10048_GPIO_SP_DS1 0x49 +#define TDA10048_GPIO_SP_DS2 0x4A +#define TDA10048_GPIO_SP_DS3 0x4B +#define TDA10048_GPIO_OUT_SEL 0x4C +#define TDA10048_GPIO_SELECT 0x4D +#define TDA10048_IC_MODE 0x4E +#define TDA10048_CONF_XO 0x50 +#define TDA10048_CONF_PLL1 0x51 +#define TDA10048_CONF_PLL2 0x52 +#define TDA10048_CONF_PLL3 0x53 +#define TDA10048_CONF_ADC 0x54 +#define TDA10048_CONF_ADC_2 0x55 +#define TDA10048_CONF_C1_1 0x60 +#define TDA10048_CONF_C1_3 0x62 +#define TDA10048_AGC_CONF 0x70 +#define TDA10048_AGC_THRESHOLD_LSB 0x72 +#define TDA10048_AGC_THRESHOLD_MSB 0x73 +#define TDA10048_AGC_RENORM 0x74 +#define TDA10048_AGC_GAINS 0x76 +#define TDA10048_AGC_TUN_MIN 0x78 +#define TDA10048_AGC_TUN_MAX 0x79 +#define TDA10048_AGC_IF_MIN 0x7A +#define TDA10048_AGC_IF_MAX 0x7B +#define TDA10048_AGC_TUN_LEVEL 0x7E +#define TDA10048_AGC_IF_LEVEL 0x7F +#define TDA10048_DIG_AGC_LEVEL 0x81 +#define TDA10048_FREQ_PHY2_LSB 0x86 +#define TDA10048_FREQ_PHY2_MSB 0x87 +#define TDA10048_TIME_INVWREF_LSB 0x88 +#define TDA10048_TIME_INVWREF_MSB 0x89 +#define TDA10048_TIME_WREF_LSB 0x8A +#define TDA10048_TIME_WREF_MID1 0x8B +#define TDA10048_TIME_WREF_MID2 0x8C +#define TDA10048_TIME_WREF_MSB 0x8D +#define TDA10048_NP_OUT 0xA2 +#define TDA10048_CELL_ID_LSB 0xA4 +#define TDA10048_CELL_ID_MSB 0xA5 +#define TDA10048_EXTTPS_ODD 0xAA +#define TDA10048_EXTTPS_EVEN 0xAB +#define TDA10048_TPS_LENGTH 0xAC +#define TDA10048_FREE_REG_1 0xB2 +#define TDA10048_FREE_REG_2 0xB3 +#define TDA10048_CONF_C3_1 0xC0 +#define TDA10048_CVBER_CTRL 0xC2 +#define TDA10048_CBER_NMAX_LSB 0xC4 +#define TDA10048_CBER_NMAX_MSB 0xC5 +#define TDA10048_CBER_LSB 0xC6 +#define TDA10048_CBER_MSB 0xC7 +#define TDA10048_VBER_LSB 0xC8 +#define TDA10048_VBER_MID 0xC9 +#define TDA10048_VBER_MSB 0xCA +#define TDA10048_CVBER_LUT 0xCC +#define TDA10048_UNCOR_CTRL 0xCD +#define TDA10048_UNCOR_CPT_LSB 0xCE +#define TDA10048_UNCOR_CPT_MSB 0xCF +#define TDA10048_SOFT_IT_C3 0xD6 +#define TDA10048_CONF_TS2 0xE0 +#define TDA10048_CONF_TS1 0xE1 + +static unsigned int debug; + +#define dprintk(level, fmt, arg...)\ + do { if (debug >= level)\ + printk(KERN_DEBUG "tda10048: " fmt, ## arg);\ + } while (0) + +struct tda10048_state { + + struct i2c_adapter *i2c; + + /* We'll cache and update the attach config settings */ + struct tda10048_config config; + struct dvb_frontend frontend; + + int fwloaded; + + u32 freq_if_hz; + u32 xtal_hz; + u32 pll_mfactor; + u32 pll_nfactor; + u32 pll_pfactor; + u32 sample_freq; + + u32 bandwidth; +}; + +static struct init_tab { + u8 reg; + u16 data; +} init_tab[] = { + { TDA10048_CONF_PLL1, 0x08 }, + { TDA10048_CONF_ADC_2, 0x00 }, + { TDA10048_CONF_C4_1, 0x00 }, + { TDA10048_CONF_PLL1, 0x0f }, + { TDA10048_CONF_PLL2, 0x0a }, + { TDA10048_CONF_PLL3, 0x43 }, + { TDA10048_FREQ_PHY2_LSB, 0x02 }, + { TDA10048_FREQ_PHY2_MSB, 0x0a }, + { TDA10048_TIME_WREF_LSB, 0xbd }, + { TDA10048_TIME_WREF_MID1, 0xe4 }, + { TDA10048_TIME_WREF_MID2, 0xa8 }, + { TDA10048_TIME_WREF_MSB, 0x02 }, + { TDA10048_TIME_INVWREF_LSB, 0x04 }, + { TDA10048_TIME_INVWREF_MSB, 0x06 }, + { TDA10048_CONF_C4_1, 0x00 }, + { TDA10048_CONF_C1_1, 0xa8 }, + { TDA10048_AGC_CONF, 0x16 }, + { TDA10048_CONF_C1_3, 0x0b }, + { TDA10048_AGC_TUN_MIN, 0x00 }, + { TDA10048_AGC_TUN_MAX, 0xff }, + { TDA10048_AGC_IF_MIN, 0x00 }, + { TDA10048_AGC_IF_MAX, 0xff }, + { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, + { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, + { TDA10048_CVBER_CTRL, 0x38 }, + { TDA10048_AGC_GAINS, 0x12 }, + { TDA10048_CONF_XO, 0x00 }, + { TDA10048_CONF_TS1, 0x07 }, + { TDA10048_IC_MODE, 0x00 }, + { TDA10048_CONF_TS2, 0xc0 }, + { TDA10048_CONF_TRISTATE1, 0x21 }, + { TDA10048_CONF_TRISTATE2, 0x00 }, + { TDA10048_CONF_POLARITY, 0x00 }, + { TDA10048_CONF_C4_2, 0x04 }, + { TDA10048_CONF_ADC, 0x60 }, + { TDA10048_CONF_ADC_2, 0x10 }, + { TDA10048_CONF_ADC, 0x60 }, + { TDA10048_CONF_ADC_2, 0x00 }, + { TDA10048_CONF_C1_1, 0xa8 }, + { TDA10048_UNCOR_CTRL, 0x00 }, + { TDA10048_CONF_C4_2, 0x04 }, +}; + +static struct pll_tab { + u32 clk_freq_khz; + u32 if_freq_khz; +} pll_tab[] = { + { TDA10048_CLK_4000, TDA10048_IF_36130 }, + { TDA10048_CLK_16000, TDA10048_IF_3300 }, + { TDA10048_CLK_16000, TDA10048_IF_3500 }, + { TDA10048_CLK_16000, TDA10048_IF_3800 }, + { TDA10048_CLK_16000, TDA10048_IF_4000 }, + { TDA10048_CLK_16000, TDA10048_IF_4300 }, + { TDA10048_CLK_16000, TDA10048_IF_4500 }, + { TDA10048_CLK_16000, TDA10048_IF_5000 }, + { TDA10048_CLK_16000, TDA10048_IF_36130 }, +}; + +static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data) +{ + struct tda10048_config *config = &state->config; + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + + dprintk(2, "%s(reg = 0x%02x, data = 0x%02x)\n", __func__, reg, data); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (ret == %i)\n", __func__, ret); + + return (ret != 1) ? -1 : 0; +} + +static u8 tda10048_readreg(struct tda10048_state *state, u8 reg) +{ + struct tda10048_config *config = &state->config; + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = config->demod_address, + .flags = 0, .buf = b0, .len = 1 }, + { .addr = config->demod_address, + .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + dprintk(2, "%s(reg = 0x%02x)\n", __func__, reg); + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + + return b1[0]; +} + +static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg, + const u8 *data, u16 len) +{ + struct tda10048_config *config = &state->config; + int ret = -EREMOTEIO; + struct i2c_msg msg; + u8 *buf; + + dprintk(2, "%s(%d, ?, len = %d)\n", __func__, reg, len); + + buf = kmalloc(len + 1, GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + goto error; + } + + *buf = reg; + memcpy(buf + 1, data, len); + + msg.addr = config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = len + 1; + + dprintk(2, "%s(): write len = %d\n", + __func__, msg.len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s(): writereg error err %i\n", + __func__, ret); + ret = -EREMOTEIO; + } + +error: + kfree(buf); + + return ret; +} + +static int tda10048_set_phy2(struct dvb_frontend *fe, u32 sample_freq_hz, + u32 if_hz) +{ + struct tda10048_state *state = fe->demodulator_priv; + u64 t; + + dprintk(1, "%s()\n", __func__); + + if (sample_freq_hz == 0) + return -EINVAL; + + if (if_hz < (sample_freq_hz / 2)) { + /* PHY2 = (if2/fs) * 2^15 */ + t = if_hz; + t *= 10; + t *= 32768; + do_div(t, sample_freq_hz); + t += 5; + do_div(t, 10); + } else { + /* PHY2 = ((IF1-fs)/fs) * 2^15 */ + t = sample_freq_hz - if_hz; + t *= 10; + t *= 32768; + do_div(t, sample_freq_hz); + t += 5; + do_div(t, 10); + t = ~t + 1; + } + + tda10048_writereg(state, TDA10048_FREQ_PHY2_LSB, (u8)t); + tda10048_writereg(state, TDA10048_FREQ_PHY2_MSB, (u8)(t >> 8)); + + return 0; +} + +static int tda10048_set_wref(struct dvb_frontend *fe, u32 sample_freq_hz, + u32 bw) +{ + struct tda10048_state *state = fe->demodulator_priv; + u64 t, z; + + dprintk(1, "%s()\n", __func__); + + if (sample_freq_hz == 0) + return -EINVAL; + + /* WREF = (B / (7 * fs)) * 2^31 */ + t = bw * 10; + /* avoid warning: this decimal constant is unsigned only in ISO C90 */ + /* t *= 2147483648 on 32bit platforms */ + t *= (2048 * 1024); + t *= 1024; + z = 7 * sample_freq_hz; + do_div(t, z); + t += 5; + do_div(t, 10); + + tda10048_writereg(state, TDA10048_TIME_WREF_LSB, (u8)t); + tda10048_writereg(state, TDA10048_TIME_WREF_MID1, (u8)(t >> 8)); + tda10048_writereg(state, TDA10048_TIME_WREF_MID2, (u8)(t >> 16)); + tda10048_writereg(state, TDA10048_TIME_WREF_MSB, (u8)(t >> 24)); + + return 0; +} + +static int tda10048_set_invwref(struct dvb_frontend *fe, u32 sample_freq_hz, + u32 bw) +{ + struct tda10048_state *state = fe->demodulator_priv; + u64 t; + + dprintk(1, "%s()\n", __func__); + + if (sample_freq_hz == 0) + return -EINVAL; + + /* INVWREF = ((7 * fs) / B) * 2^5 */ + t = sample_freq_hz; + t *= 7; + t *= 32; + t *= 10; + do_div(t, bw); + t += 5; + do_div(t, 10); + + tda10048_writereg(state, TDA10048_TIME_INVWREF_LSB, (u8)t); + tda10048_writereg(state, TDA10048_TIME_INVWREF_MSB, (u8)(t >> 8)); + + return 0; +} + +static int tda10048_set_bandwidth(struct dvb_frontend *fe, + u32 bw) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s(bw=%d)\n", __func__, bw); + + /* Bandwidth setting may need to be adjusted */ + switch (bw) { + case 6000000: + case 7000000: + case 8000000: + tda10048_set_wref(fe, state->sample_freq, bw); + tda10048_set_invwref(fe, state->sample_freq, bw); + break; + default: + printk(KERN_ERR "%s() invalid bandwidth\n", __func__); + return -EINVAL; + } + + state->bandwidth = bw; + + return 0; +} + +static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) +{ + struct tda10048_state *state = fe->demodulator_priv; + struct tda10048_config *config = &state->config; + int i; + u32 if_freq_khz; + + dprintk(1, "%s(bw = %d)\n", __func__, bw); + + /* based on target bandwidth and clk we calculate pll factors */ + switch (bw) { + case 6000000: + if_freq_khz = config->dtv6_if_freq_khz; + break; + case 7000000: + if_freq_khz = config->dtv7_if_freq_khz; + break; + case 8000000: + if_freq_khz = config->dtv8_if_freq_khz; + break; + default: + printk(KERN_ERR "%s() no default\n", __func__); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(pll_tab); i++) { + if ((pll_tab[i].clk_freq_khz == config->clk_freq_khz) && + (pll_tab[i].if_freq_khz == if_freq_khz)) { + + state->freq_if_hz = pll_tab[i].if_freq_khz * 1000; + state->xtal_hz = pll_tab[i].clk_freq_khz * 1000; + break; + } + } + if (i == ARRAY_SIZE(pll_tab)) { + printk(KERN_ERR "%s() Incorrect attach settings\n", + __func__); + return -EINVAL; + } + + dprintk(1, "- freq_if_hz = %d\n", state->freq_if_hz); + dprintk(1, "- xtal_hz = %d\n", state->xtal_hz); + dprintk(1, "- pll_mfactor = %d\n", state->pll_mfactor); + dprintk(1, "- pll_nfactor = %d\n", state->pll_nfactor); + dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor); + + /* Calculate the sample frequency */ + state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45); + state->sample_freq /= (state->pll_nfactor + 1); + state->sample_freq /= (state->pll_pfactor + 4); + dprintk(1, "- sample_freq = %d\n", state->sample_freq); + + /* Update the I/F */ + tda10048_set_phy2(fe, state->sample_freq, state->freq_if_hz); + + return 0; +} + +static int tda10048_firmware_upload(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + struct tda10048_config *config = &state->config; + const struct firmware *fw; + int ret; + int pos = 0; + int cnt; + u8 wlen = config->fwbulkwritelen; + + if ((wlen != TDA10048_BULKWRITE_200) && (wlen != TDA10048_BULKWRITE_50)) + wlen = TDA10048_BULKWRITE_200; + + /* request the firmware, this will block and timeout */ + printk(KERN_INFO "%s: waiting for firmware upload (%s)...\n", + __func__, + TDA10048_DEFAULT_FIRMWARE); + + ret = request_firmware(&fw, TDA10048_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + if (ret) { + printk(KERN_ERR "%s: Upload failed. (file not found?)\n", + __func__); + return -EIO; + } else { + printk(KERN_INFO "%s: firmware read %zu bytes.\n", + __func__, + fw->size); + ret = 0; + } + + if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { + printk(KERN_ERR "%s: firmware incorrect size\n", __func__); + ret = -EIO; + } else { + printk(KERN_INFO "%s: firmware uploading\n", __func__); + + /* Soft reset */ + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, + tda10048_readreg(state, TDA10048_CONF_TRISTATE1) + & 0xfe); + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, + tda10048_readreg(state, TDA10048_CONF_TRISTATE1) + | 0x01); + + /* Put the demod into host download mode */ + tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xf9); + + /* Boot the DSP */ + tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x08); + + /* Prepare for download */ + tda10048_writereg(state, TDA10048_DSP_CODE_CPT, 0); + + /* Download the firmware payload */ + while (pos < fw->size) { + + if ((fw->size - pos) > wlen) + cnt = wlen; + else + cnt = fw->size - pos; + + tda10048_writeregbulk(state, TDA10048_DSP_CODE_IN, + &fw->data[pos], cnt); + + pos += cnt; + } + + ret = -EIO; + /* Wait up to 250ms for the DSP to boot */ + for (cnt = 0; cnt < 250 ; cnt += 10) { + + msleep(10); + + if (tda10048_readreg(state, TDA10048_SYNC_STATUS) + & 0x40) { + ret = 0; + break; + } + } + } + + release_firmware(fw); + + if (ret == 0) { + printk(KERN_INFO "%s: firmware uploaded\n", __func__); + state->fwloaded = 1; + } else + printk(KERN_ERR "%s: firmware upload failed\n", __func__); + + return ret; +} + +static int tda10048_set_inversion(struct dvb_frontend *fe, int inversion) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s(%d)\n", __func__, inversion); + + if (inversion == TDA10048_INVERSION_ON) + tda10048_writereg(state, TDA10048_CONF_C1_1, + tda10048_readreg(state, TDA10048_CONF_C1_1) | 0x20); + else + tda10048_writereg(state, TDA10048_CONF_C1_1, + tda10048_readreg(state, TDA10048_CONF_C1_1) & 0xdf); + + return 0; +} + +/* Retrieve the demod settings */ +static int tda10048_get_tps(struct tda10048_state *state, + struct dtv_frontend_properties *p) +{ + u8 val; + + /* Make sure the TPS regs are valid */ + if (!(tda10048_readreg(state, TDA10048_AUTO) & 0x01)) + return -EAGAIN; + + val = tda10048_readreg(state, TDA10048_OUT_CONF2); + switch ((val & 0x60) >> 5) { + case 0: + p->modulation = QPSK; + break; + case 1: + p->modulation = QAM_16; + break; + case 2: + p->modulation = QAM_64; + break; + } + switch ((val & 0x18) >> 3) { + case 0: + p->hierarchy = HIERARCHY_NONE; + break; + case 1: + p->hierarchy = HIERARCHY_1; + break; + case 2: + p->hierarchy = HIERARCHY_2; + break; + case 3: + p->hierarchy = HIERARCHY_4; + break; + } + switch (val & 0x07) { + case 0: + p->code_rate_HP = FEC_1_2; + break; + case 1: + p->code_rate_HP = FEC_2_3; + break; + case 2: + p->code_rate_HP = FEC_3_4; + break; + case 3: + p->code_rate_HP = FEC_5_6; + break; + case 4: + p->code_rate_HP = FEC_7_8; + break; + } + + val = tda10048_readreg(state, TDA10048_OUT_CONF3); + switch (val & 0x07) { + case 0: + p->code_rate_LP = FEC_1_2; + break; + case 1: + p->code_rate_LP = FEC_2_3; + break; + case 2: + p->code_rate_LP = FEC_3_4; + break; + case 3: + p->code_rate_LP = FEC_5_6; + break; + case 4: + p->code_rate_LP = FEC_7_8; + break; + } + + val = tda10048_readreg(state, TDA10048_OUT_CONF1); + switch ((val & 0x0c) >> 2) { + case 0: + p->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + p->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + p->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + p->guard_interval = GUARD_INTERVAL_1_4; + break; + } + switch (val & 0x03) { + case 0: + p->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + p->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + return 0; +} + +static int tda10048_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct tda10048_state *state = fe->demodulator_priv; + struct tda10048_config *config = &state->config; + dprintk(1, "%s(%d)\n", __func__, enable); + + if (config->disable_gate_access) + return 0; + + if (enable) + return tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x02); + else + return tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xfd); +} + +static int tda10048_output_mode(struct dvb_frontend *fe, int serial) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s(%d)\n", __func__, serial); + + /* Ensure pins are out of tri-state */ + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, 0x21); + tda10048_writereg(state, TDA10048_CONF_TRISTATE2, 0x00); + + if (serial) { + tda10048_writereg(state, TDA10048_IC_MODE, 0x80 | 0x20); + tda10048_writereg(state, TDA10048_CONF_TS2, 0xc0); + } else { + tda10048_writereg(state, TDA10048_IC_MODE, 0x00); + tda10048_writereg(state, TDA10048_CONF_TS2, 0x01); + } + + return 0; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +/* TODO: Support manual tuning with specific params */ +static int tda10048_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s(frequency=%d)\n", __func__, p->frequency); + + /* Update the I/F pll's if the bandwidth changes */ + if (p->bandwidth_hz != state->bandwidth) { + tda10048_set_if(fe, p->bandwidth_hz); + tda10048_set_bandwidth(fe, p->bandwidth_hz); + } + + if (fe->ops.tuner_ops.set_params) { + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.set_params(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Enable demod TPS auto detection and begin acquisition */ + tda10048_writereg(state, TDA10048_AUTO, 0x57); + /* trigger cber and vber acquisition */ + tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x3B); + + return 0; +} + +/* Establish sane defaults and load firmware. */ +static int tda10048_init(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + struct tda10048_config *config = &state->config; + int ret = 0, i; + + dprintk(1, "%s()\n", __func__); + + /* PLL */ + init_tab[4].data = (u8)(state->pll_mfactor); + init_tab[5].data = (u8)(state->pll_nfactor) | 0x40; + + /* Apply register defaults */ + for (i = 0; i < ARRAY_SIZE(init_tab); i++) + tda10048_writereg(state, init_tab[i].reg, init_tab[i].data); + + if (state->fwloaded == 0) + ret = tda10048_firmware_upload(fe); + + /* Set either serial or parallel */ + tda10048_output_mode(fe, config->output_mode); + + /* Set inversion */ + tda10048_set_inversion(fe, config->inversion); + + /* Establish default RF values */ + tda10048_set_if(fe, 8000000); + tda10048_set_bandwidth(fe, 8000000); + + /* Ensure we leave the gate closed */ + tda10048_i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int tda10048_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 reg; + + *status = 0; + + reg = tda10048_readreg(state, TDA10048_SYNC_STATUS); + + dprintk(1, "%s() status =0x%02x\n", __func__, reg); + + if (reg & 0x02) + *status |= FE_HAS_CARRIER; + + if (reg & 0x04) + *status |= FE_HAS_SIGNAL; + + if (reg & 0x08) { + *status |= FE_HAS_LOCK; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + } + + return 0; +} + +static int tda10048_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10048_state *state = fe->demodulator_priv; + static u32 cber_current; + u32 cber_nmax; + u64 cber_tmp; + + dprintk(1, "%s()\n", __func__); + + /* update cber on interrupt */ + if (tda10048_readreg(state, TDA10048_SOFT_IT_C3) & 0x01) { + cber_tmp = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | + tda10048_readreg(state, TDA10048_CBER_LSB); + cber_nmax = tda10048_readreg(state, TDA10048_CBER_NMAX_MSB) << 8 | + tda10048_readreg(state, TDA10048_CBER_NMAX_LSB); + cber_tmp *= 100000000; + cber_tmp *= 2; + cber_tmp = div_u64(cber_tmp, (cber_nmax * 32) + 1); + cber_current = (u32)cber_tmp; + /* retrigger cber acquisition */ + tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x39); + } + /* actual cber is (*ber)/1e8 */ + *ber = cber_current; + + return 0; +} + +static int tda10048_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 v; + + dprintk(1, "%s()\n", __func__); + + *signal_strength = 65535; + + v = tda10048_readreg(state, TDA10048_NP_OUT); + if (v > 0) + *signal_strength -= (v << 8) | v; + + return 0; +} + +/* SNR lookup table */ +static struct snr_tab { + u8 val; + u8 data; +} snr_tab[] = { + { 0, 0 }, + { 1, 246 }, + { 2, 215 }, + { 3, 198 }, + { 4, 185 }, + { 5, 176 }, + { 6, 168 }, + { 7, 161 }, + { 8, 155 }, + { 9, 150 }, + { 10, 146 }, + { 11, 141 }, + { 12, 138 }, + { 13, 134 }, + { 14, 131 }, + { 15, 128 }, + { 16, 125 }, + { 17, 122 }, + { 18, 120 }, + { 19, 118 }, + { 20, 115 }, + { 21, 113 }, + { 22, 111 }, + { 23, 109 }, + { 24, 107 }, + { 25, 106 }, + { 26, 104 }, + { 27, 102 }, + { 28, 101 }, + { 29, 99 }, + { 30, 98 }, + { 31, 96 }, + { 32, 95 }, + { 33, 94 }, + { 34, 92 }, + { 35, 91 }, + { 36, 90 }, + { 37, 89 }, + { 38, 88 }, + { 39, 86 }, + { 40, 85 }, + { 41, 84 }, + { 42, 83 }, + { 43, 82 }, + { 44, 81 }, + { 45, 80 }, + { 46, 79 }, + { 47, 78 }, + { 48, 77 }, + { 49, 76 }, + { 50, 76 }, + { 51, 75 }, + { 52, 74 }, + { 53, 73 }, + { 54, 72 }, + { 56, 71 }, + { 57, 70 }, + { 58, 69 }, + { 60, 68 }, + { 61, 67 }, + { 63, 66 }, + { 64, 65 }, + { 66, 64 }, + { 67, 63 }, + { 68, 62 }, + { 69, 62 }, + { 70, 61 }, + { 72, 60 }, + { 74, 59 }, + { 75, 58 }, + { 77, 57 }, + { 79, 56 }, + { 81, 55 }, + { 83, 54 }, + { 85, 53 }, + { 87, 52 }, + { 89, 51 }, + { 91, 50 }, + { 93, 49 }, + { 95, 48 }, + { 97, 47 }, + { 100, 46 }, + { 102, 45 }, + { 104, 44 }, + { 107, 43 }, + { 109, 42 }, + { 112, 41 }, + { 114, 40 }, + { 117, 39 }, + { 120, 38 }, + { 123, 37 }, + { 125, 36 }, + { 128, 35 }, + { 131, 34 }, + { 134, 33 }, + { 138, 32 }, + { 141, 31 }, + { 144, 30 }, + { 147, 29 }, + { 151, 28 }, + { 154, 27 }, + { 158, 26 }, + { 162, 25 }, + { 165, 24 }, + { 169, 23 }, + { 173, 22 }, + { 177, 21 }, + { 181, 20 }, + { 186, 19 }, + { 190, 18 }, + { 194, 17 }, + { 199, 16 }, + { 204, 15 }, + { 208, 14 }, + { 213, 13 }, + { 218, 12 }, + { 223, 11 }, + { 229, 10 }, + { 234, 9 }, + { 239, 8 }, + { 245, 7 }, + { 251, 6 }, + { 255, 5 }, +}; + +static int tda10048_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 v; + int i, ret = -EINVAL; + + dprintk(1, "%s()\n", __func__); + + v = tda10048_readreg(state, TDA10048_NP_OUT); + for (i = 0; i < ARRAY_SIZE(snr_tab); i++) { + if (v <= snr_tab[i].val) { + *snr = snr_tab[i].data; + ret = 0; + break; + } + } + + return ret; +} + +static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + *ucblocks = tda10048_readreg(state, TDA10048_UNCOR_CPT_MSB) << 8 | + tda10048_readreg(state, TDA10048_UNCOR_CPT_LSB); + /* clear the uncorrected TS packets counter when saturated */ + if (*ucblocks == 0xFFFF) + tda10048_writereg(state, TDA10048_UNCOR_CTRL, 0x80); + + return 0; +} + +static int tda10048_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + p->inversion = tda10048_readreg(state, TDA10048_CONF_C1_1) + & 0x20 ? INVERSION_ON : INVERSION_OFF; + + return tda10048_get_tps(state, p); +} + +static int tda10048_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void tda10048_release(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s()\n", __func__); + kfree(state); +} + +static void tda10048_establish_defaults(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + struct tda10048_config *config = &state->config; + + /* Validate/default the config */ + if (config->dtv6_if_freq_khz == 0) { + config->dtv6_if_freq_khz = TDA10048_IF_4300; + printk(KERN_WARNING "%s() tda10048_config.dtv6_if_freq_khz is not set (defaulting to %d)\n", + __func__, + config->dtv6_if_freq_khz); + } + + if (config->dtv7_if_freq_khz == 0) { + config->dtv7_if_freq_khz = TDA10048_IF_4300; + printk(KERN_WARNING "%s() tda10048_config.dtv7_if_freq_khz is not set (defaulting to %d)\n", + __func__, + config->dtv7_if_freq_khz); + } + + if (config->dtv8_if_freq_khz == 0) { + config->dtv8_if_freq_khz = TDA10048_IF_4300; + printk(KERN_WARNING "%s() tda10048_config.dtv8_if_freq_khz is not set (defaulting to %d)\n", + __func__, + config->dtv8_if_freq_khz); + } + + if (config->clk_freq_khz == 0) { + config->clk_freq_khz = TDA10048_CLK_16000; + printk(KERN_WARNING "%s() tda10048_config.clk_freq_khz is not set (defaulting to %d)\n", + __func__, + config->clk_freq_khz); + } +} + +static const struct dvb_frontend_ops tda10048_ops; + +struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, + struct i2c_adapter *i2c) +{ + struct tda10048_state *state = NULL; + + dprintk(1, "%s()\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state and clone the config */ + memcpy(&state->config, config, sizeof(*config)); + state->i2c = i2c; + state->fwloaded = config->no_firmware; + state->bandwidth = 8000000; + + /* check if the demod is present */ + if (tda10048_readreg(state, TDA10048_IDENTITY) != 0x048) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10048_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* set pll */ + if (config->set_pll) { + state->pll_mfactor = config->pll_m; + state->pll_nfactor = config->pll_n; + state->pll_pfactor = config->pll_p; + } else { + state->pll_mfactor = 10; + state->pll_nfactor = 3; + state->pll_pfactor = 0; + } + + /* Establish any defaults the user didn't pass */ + tda10048_establish_defaults(&state->frontend); + + /* Set the xtal and freq defaults */ + if (tda10048_set_if(&state->frontend, 8000000) != 0) + goto error; + + /* Default bandwidth */ + if (tda10048_set_bandwidth(&state->frontend, 8000000) != 0) + goto error; + + /* Leave the gate closed */ + tda10048_i2c_gate_ctrl(&state->frontend, 0); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(tda10048_attach); + +static const struct dvb_frontend_ops tda10048_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "NXP TDA10048HN DVB-T", + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + + .release = tda10048_release, + .init = tda10048_init, + .i2c_gate_ctrl = tda10048_i2c_gate_ctrl, + .set_frontend = tda10048_set_frontend, + .get_frontend = tda10048_get_frontend, + .get_tune_settings = tda10048_get_tune_settings, + .read_status = tda10048_read_status, + .read_ber = tda10048_read_ber, + .read_signal_strength = tda10048_read_signal_strength, + .read_snr = tda10048_read_snr, + .read_ucblocks = tda10048_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("NXP TDA10048HN DVB-T Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tda10048.h b/drivers/media/dvb-frontends/tda10048.h new file mode 100644 index 0000000000..774fc8957b --- /dev/null +++ b/drivers/media/dvb-frontends/tda10048.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + NXP TDA10048HN DVB OFDM demodulator driver + + Copyright (C) 2009 Steven Toth <stoth@kernellabs.com> + + +*/ + +#ifndef TDA10048_H +#define TDA10048_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct tda10048_config { + + /* the demodulator's i2c address */ + u8 demod_address; + + /* serial/parallel output */ +#define TDA10048_PARALLEL_OUTPUT 0 +#define TDA10048_SERIAL_OUTPUT 1 + u8 output_mode; + +#define TDA10048_BULKWRITE_200 200 +#define TDA10048_BULKWRITE_50 50 + u8 fwbulkwritelen; + + /* Spectral Inversion */ +#define TDA10048_INVERSION_OFF 0 +#define TDA10048_INVERSION_ON 1 + u8 inversion; + +#define TDA10048_IF_3300 3300 +#define TDA10048_IF_3500 3500 +#define TDA10048_IF_3800 3800 +#define TDA10048_IF_4000 4000 +#define TDA10048_IF_4300 4300 +#define TDA10048_IF_4500 4500 +#define TDA10048_IF_4750 4750 +#define TDA10048_IF_5000 5000 +#define TDA10048_IF_36130 36130 + u16 dtv6_if_freq_khz; + u16 dtv7_if_freq_khz; + u16 dtv8_if_freq_khz; + +#define TDA10048_CLK_4000 4000 +#define TDA10048_CLK_16000 16000 + u16 clk_freq_khz; + + /* Disable I2C gate access */ + u8 disable_gate_access; + + bool no_firmware; + + bool set_pll; + u8 pll_m; + u8 pll_p; + u8 pll_n; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA10048) +extern struct dvb_frontend *tda10048_attach( + const struct tda10048_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *tda10048_attach( + const struct tda10048_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_TDA10048 */ + +#endif /* TDA10048_H */ diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c new file mode 100644 index 0000000000..6f306db6c6 --- /dev/null +++ b/drivers/media/dvb-frontends/tda1004x.c @@ -0,0 +1,1382 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* + Driver for Philips tda1004xh OFDM Demodulator + + (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach + + + */ +/* + * This driver needs external firmware. Please use the commands + * "<kerneldir>/scripts/get_dvb_firmware tda10045", + * "<kerneldir>/scripts/get_dvb_firmware tda10046" to + * download/extract them, and then copy them to /usr/lib/hotplug/firmware + * or /lib/firmware (depending on configuration of firmware hotplug). + */ +#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw" +#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw" + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "tda1004x.h" + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda1004x: " args); \ + } while (0) + +#define TDA1004X_CHIPID 0x00 +#define TDA1004X_AUTO 0x01 +#define TDA1004X_IN_CONF1 0x02 +#define TDA1004X_IN_CONF2 0x03 +#define TDA1004X_OUT_CONF1 0x04 +#define TDA1004X_OUT_CONF2 0x05 +#define TDA1004X_STATUS_CD 0x06 +#define TDA1004X_CONFC4 0x07 +#define TDA1004X_DSSPARE2 0x0C +#define TDA10045H_CODE_IN 0x0D +#define TDA10045H_FWPAGE 0x0E +#define TDA1004X_SCAN_CPT 0x10 +#define TDA1004X_DSP_CMD 0x11 +#define TDA1004X_DSP_ARG 0x12 +#define TDA1004X_DSP_DATA1 0x13 +#define TDA1004X_DSP_DATA2 0x14 +#define TDA1004X_CONFADC1 0x15 +#define TDA1004X_CONFC1 0x16 +#define TDA10045H_S_AGC 0x1a +#define TDA10046H_AGC_TUN_LEVEL 0x1a +#define TDA1004X_SNR 0x1c +#define TDA1004X_CONF_TS1 0x1e +#define TDA1004X_CONF_TS2 0x1f +#define TDA1004X_CBER_RESET 0x20 +#define TDA1004X_CBER_MSB 0x21 +#define TDA1004X_CBER_LSB 0x22 +#define TDA1004X_CVBER_LUT 0x23 +#define TDA1004X_VBER_MSB 0x24 +#define TDA1004X_VBER_MID 0x25 +#define TDA1004X_VBER_LSB 0x26 +#define TDA1004X_UNCOR 0x27 + +#define TDA10045H_CONFPLL_P 0x2D +#define TDA10045H_CONFPLL_M_MSB 0x2E +#define TDA10045H_CONFPLL_M_LSB 0x2F +#define TDA10045H_CONFPLL_N 0x30 + +#define TDA10046H_CONFPLL1 0x2D +#define TDA10046H_CONFPLL2 0x2F +#define TDA10046H_CONFPLL3 0x30 +#define TDA10046H_TIME_WREF1 0x31 +#define TDA10046H_TIME_WREF2 0x32 +#define TDA10046H_TIME_WREF3 0x33 +#define TDA10046H_TIME_WREF4 0x34 +#define TDA10046H_TIME_WREF5 0x35 + +#define TDA10045H_UNSURW_MSB 0x31 +#define TDA10045H_UNSURW_LSB 0x32 +#define TDA10045H_WREF_MSB 0x33 +#define TDA10045H_WREF_MID 0x34 +#define TDA10045H_WREF_LSB 0x35 +#define TDA10045H_MUXOUT 0x36 +#define TDA1004X_CONFADC2 0x37 + +#define TDA10045H_IOFFSET 0x38 + +#define TDA10046H_CONF_TRISTATE1 0x3B +#define TDA10046H_CONF_TRISTATE2 0x3C +#define TDA10046H_CONF_POLARITY 0x3D +#define TDA10046H_FREQ_OFFSET 0x3E +#define TDA10046H_GPIO_OUT_SEL 0x41 +#define TDA10046H_GPIO_SELECT 0x42 +#define TDA10046H_AGC_CONF 0x43 +#define TDA10046H_AGC_THR 0x44 +#define TDA10046H_AGC_RENORM 0x45 +#define TDA10046H_AGC_GAINS 0x46 +#define TDA10046H_AGC_TUN_MIN 0x47 +#define TDA10046H_AGC_TUN_MAX 0x48 +#define TDA10046H_AGC_IF_MIN 0x49 +#define TDA10046H_AGC_IF_MAX 0x4A + +#define TDA10046H_FREQ_PHY2_MSB 0x4D +#define TDA10046H_FREQ_PHY2_LSB 0x4E + +#define TDA10046H_CVBER_CTRL 0x4F +#define TDA10046H_AGC_IF_LEVEL 0x52 +#define TDA10046H_CODE_CPT 0x57 +#define TDA10046H_CODE_IN 0x58 + + +static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + dprintk("%s: reg=0x%x, data=0x%x\n", __func__, reg, data); + + msg.addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__, + reg, data, ret); + return (ret != 1) ? -1 : 0; +} + +static int tda1004x_read_byte(struct tda1004x_state *state, int reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, + { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; + + dprintk("%s: reg=0x%x\n", __func__, reg); + + msg[0].addr = state->config->demod_address; + msg[1].addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, + ret); + return -EINVAL; + } + + dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__, + reg, b1[0], ret); + return b1[0]; +} + +static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data) +{ + int val; + dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __func__, reg, + mask, data); + + // read a byte and check + val = tda1004x_read_byte(state, reg); + if (val < 0) + return val; + + // mask if off + val = val & ~mask; + val |= data & 0xff; + + // write it out again + return tda1004x_write_byteI(state, reg, val); +} + +static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len) +{ + int i; + int result; + + dprintk("%s: reg=0x%x, len=0x%x\n", __func__, reg, len); + + result = 0; + for (i = 0; i < len; i++) { + result = tda1004x_write_byteI(state, reg + i, buf[i]); + if (result != 0) + break; + } + + return result; +} + +static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state) +{ + int result; + dprintk("%s\n", __func__); + + result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2); + msleep(20); + return result; +} + +static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state) +{ + dprintk("%s\n", __func__); + + return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0); +} + +static int tda10045h_set_bandwidth(struct tda1004x_state *state, + u32 bandwidth) +{ + static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f }; + static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb }; + static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 }; + + switch (bandwidth) { + case 6000000: + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); + break; + + case 7000000: + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); + break; + + case 8000000: + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); + break; + + default: + return -EINVAL; + } + + tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0); + + return 0; +} + +static int tda10046h_set_bandwidth(struct tda1004x_state *state, + u32 bandwidth) +{ + static u8 bandwidth_6mhz_53M[] = { 0x7b, 0x2e, 0x11, 0xf0, 0xd2 }; + static u8 bandwidth_7mhz_53M[] = { 0x6a, 0x02, 0x6a, 0x43, 0x9f }; + static u8 bandwidth_8mhz_53M[] = { 0x5c, 0x32, 0xc2, 0x96, 0x6d }; + + static u8 bandwidth_6mhz_48M[] = { 0x70, 0x02, 0x49, 0x24, 0x92 }; + static u8 bandwidth_7mhz_48M[] = { 0x60, 0x02, 0xaa, 0xaa, 0xab }; + static u8 bandwidth_8mhz_48M[] = { 0x54, 0x03, 0x0c, 0x30, 0xc3 }; + int tda10046_clk53m; + + if ((state->config->if_freq == TDA10046_FREQ_045) || + (state->config->if_freq == TDA10046_FREQ_052)) + tda10046_clk53m = 0; + else + tda10046_clk53m = 1; + switch (bandwidth) { + case 6000000: + if (tda10046_clk53m) + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_53M, + sizeof(bandwidth_6mhz_53M)); + else + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_48M, + sizeof(bandwidth_6mhz_48M)); + if (state->config->if_freq == TDA10046_FREQ_045) { + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xab); + } + break; + + case 7000000: + if (tda10046_clk53m) + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_53M, + sizeof(bandwidth_7mhz_53M)); + else + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_48M, + sizeof(bandwidth_7mhz_48M)); + if (state->config->if_freq == TDA10046_FREQ_045) { + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00); + } + break; + + case 8000000: + if (tda10046_clk53m) + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_53M, + sizeof(bandwidth_8mhz_53M)); + else + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_48M, + sizeof(bandwidth_8mhz_48M)); + if (state->config->if_freq == TDA10046_FREQ_045) { + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x55); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int tda1004x_do_upload(struct tda1004x_state *state, + const unsigned char *mem, unsigned int len, + u8 dspCodeCounterReg, u8 dspCodeInReg) +{ + u8 buf[65]; + struct i2c_msg fw_msg = { .flags = 0, .buf = buf, .len = 0 }; + int tx_size; + int pos = 0; + + /* clear code counter */ + tda1004x_write_byteI(state, dspCodeCounterReg, 0); + fw_msg.addr = state->config->demod_address; + + i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT); + buf[0] = dspCodeInReg; + while (pos != len) { + // work out how much to send this time + tx_size = len - pos; + if (tx_size > 0x10) + tx_size = 0x10; + + // send the chunk + memcpy(buf + 1, mem + pos, tx_size); + fw_msg.len = tx_size + 1; + if (__i2c_transfer(state->i2c, &fw_msg, 1) != 1) { + printk(KERN_ERR "tda1004x: Error during firmware upload\n"); + i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT); + return -EIO; + } + pos += tx_size; + + dprintk("%s: fw_pos=0x%x\n", __func__, pos); + } + i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT); + + /* give the DSP a chance to settle 03/10/05 Hac */ + msleep(100); + + return 0; +} + +static int tda1004x_check_upload_ok(struct tda1004x_state *state) +{ + u8 data1, data2; + unsigned long timeout; + + if (state->demod_type == TDA1004X_DEMOD_TDA10046) { + timeout = jiffies + 2 * HZ; + while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n"); + break; + } + msleep(1); + } + } else + msleep(100); + + // check upload was OK + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP + tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67); + + data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1); + data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2); + if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) { + printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2); + return -EIO; + } + printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2); + return 0; +} + +static int tda10045_fwupload(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int ret; + const struct firmware *fw; + + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state) == 0) + return 0; + + /* request the firmware, this will block until someone uploads it */ + printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE); + ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); + if (ret) { + printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); + return ret; + } + + /* reset chip */ + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); + msleep(10); + + /* set parameters */ + tda10045h_set_bandwidth(state, 8000000); + + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN); + release_firmware(fw); + if (ret) + return ret; + printk(KERN_INFO "tda1004x: firmware upload complete\n"); + + /* wait for DSP to initialise */ + /* DSPREADY doesn't seem to work on the TDA10045H */ + msleep(100); + + return tda1004x_check_upload_ok(state); +} + +static void tda10046_init_plls(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int tda10046_clk53m; + + if ((state->config->if_freq == TDA10046_FREQ_045) || + (state->config->if_freq == TDA10046_FREQ_052)) + tda10046_clk53m = 0; + else + tda10046_clk53m = 1; + + tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0); + if(tda10046_clk53m) { + printk(KERN_INFO "tda1004x: setting up plls for 53MHz sampling clock\n"); + tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x08); // PLL M = 8 + } else { + printk(KERN_INFO "tda1004x: setting up plls for 48MHz sampling clock\n"); + tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x03); // PLL M = 3 + } + if (state->config->xtal_freq == TDA10046_XTAL_4M ) { + dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __func__); + tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 + } else { + dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __func__); + tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3 + } + if(tda10046_clk53m) + tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x67); + else + tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x72); + /* Note clock frequency is handled implicitly */ + switch (state->config->if_freq) { + case TDA10046_FREQ_045: + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00); + break; + case TDA10046_FREQ_052: + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xc7); + break; + case TDA10046_FREQ_3617: + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x59); + break; + case TDA10046_FREQ_3613: + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x3f); + break; + } + tda10046h_set_bandwidth(state, 8000000); /* default bandwidth 8 MHz */ + /* let the PLLs settle */ + msleep(120); +} + +static int tda10046_fwupload(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int ret, confc4; + const struct firmware *fw; + + /* reset + wake up chip */ + if (state->config->xtal_freq == TDA10046_XTAL_4M) { + confc4 = 0; + } else { + dprintk("%s: 16MHz Xtal, reducing I2C speed\n", __func__); + confc4 = 0x80; + } + tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4); + + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); + /* set GPIO 1 and 3 */ + if (state->config->gpio_config != TDA10046_GPTRI) { + tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33); + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f); + } + /* let the clocks recover from sleep */ + msleep(10); + + /* The PLLs need to be reprogrammed after sleep */ + tda10046_init_plls(fe); + tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0); + + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state) == 0) + return 0; + + /* + For i2c normal work, we need to slow down the bus speed. + However, the slow down breaks the eeprom firmware load. + So, use normal speed for eeprom booting and then restore the + i2c speed after that. Tested with MSI TV @nyware A/D board, + that comes with firmware version 29 inside their eeprom. + + It should also be noticed that no other I2C transfer should + be in course while booting from eeprom, otherwise, tda10046 + goes into an instable state. So, proper locking are needed + at the i2c bus master. + */ + printk(KERN_INFO "tda1004x: trying to boot from eeprom\n"); + tda1004x_write_byteI(state, TDA1004X_CONFC4, 4); + msleep(300); + tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4); + + /* Checks if eeprom firmware went without troubles */ + if (tda1004x_check_upload_ok(state) == 0) + return 0; + + /* eeprom firmware didn't work. Load one manually. */ + + if (state->config->request_firmware != NULL) { + /* request the firmware, this will block until someone uploads it */ + printk(KERN_INFO "tda1004x: waiting for firmware upload...\n"); + ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); + if (ret) { + /* remain compatible to old bug: try to load with tda10045 image name */ + ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); + if (ret) { + printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); + return ret; + } else { + printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n", + TDA10046_DEFAULT_FIRMWARE); + } + } + } else { + printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n"); + return -EIO; + } + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); + release_firmware(fw); + return tda1004x_check_upload_ok(state); +} + +static int tda1004x_encode_fec(int fec) +{ + // convert known FEC values + switch (fec) { + case FEC_1_2: + return 0; + case FEC_2_3: + return 1; + case FEC_3_4: + return 2; + case FEC_5_6: + return 3; + case FEC_7_8: + return 4; + } + + // unsupported + return -EINVAL; +} + +static int tda1004x_decode_fec(int tdafec) +{ + // convert known FEC values + switch (tdafec) { + case 0: + return FEC_1_2; + case 1: + return FEC_2_3; + case 2: + return FEC_3_4; + case 3: + return FEC_5_6; + case 4: + return FEC_7_8; + } + + // unsupported + return -1; +} + +static int tda1004x_write(struct dvb_frontend* fe, const u8 buf[], int len) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return tda1004x_write_byteI(state, buf[0], buf[1]); +} + +static int tda10045_init(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (tda10045_fwupload(fe)) { + printk("tda1004x: firmware upload failed\n"); + return -EIO; + } + + tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC + + // tda setup + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer + tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset + tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface + tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface + tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity + tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e); + + tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk); + + return 0; +} + +static int tda10046_init(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + dprintk("%s\n", __func__); + + if (tda10046_fwupload(fe)) { + printk("tda1004x: firmware upload failed\n"); + return -EIO; + } + + // tda setup + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream + tda1004x_write_byteI(state, TDA1004X_CONFC1, 0x88); // enable pulse killer + + switch (state->config->agc_config) { + case TDA10046_AGC_DEFAULT: + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities + break; + case TDA10046_AGC_IFO_AUTO_NEG: + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities + break; + case TDA10046_AGC_IFO_AUTO_POS: + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities + break; + case TDA10046_AGC_TDA827X: + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup + tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold + tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities + break; + } + if (state->config->ts_mode == 0) { + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40); + tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); + } else { + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80); + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10, + state->config->invert_oclk << 4); + } + tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); + tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on + tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } + tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values + tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } + tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // } + tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 0x12); // IF gain 2, TUN gain 1 + tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits + tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config + tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config + // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes + + return 0; +} + +static int tda1004x_set_fe(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; + struct tda1004x_state* state = fe->demodulator_priv; + int tmp; + int inversion; + + dprintk("%s\n", __func__); + + if (state->demod_type == TDA1004X_DEMOD_TDA10046) { + // setup auto offset + tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0); + + // disable agc_conf[2] + tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0); + } + + // set frequency + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + // Hardcoded to use auto as much as possible on the TDA10045 as it + // is very unreliable if AUTO mode is _not_ used. + if (state->demod_type == TDA1004X_DEMOD_TDA10045) { + fe_params->code_rate_HP = FEC_AUTO; + fe_params->guard_interval = GUARD_INTERVAL_AUTO; + fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; + } + + // Set standard params.. or put them to auto + if ((fe_params->code_rate_HP == FEC_AUTO) || + (fe_params->code_rate_LP == FEC_AUTO) || + (fe_params->modulation == QAM_AUTO) || + (fe_params->hierarchy == HIERARCHY_AUTO)) { + tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); /* turn off modulation bits */ + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits + } else { + tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto + + // set HP FEC + tmp = tda1004x_encode_fec(fe_params->code_rate_HP); + if (tmp < 0) + return tmp; + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp); + + // set LP FEC + tmp = tda1004x_encode_fec(fe_params->code_rate_LP); + if (tmp < 0) + return tmp; + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3); + + /* set modulation */ + switch (fe_params->modulation) { + case QPSK: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0); + break; + + case QAM_16: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1); + break; + + case QAM_64: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2); + break; + + default: + return -EINVAL; + } + + // set hierarchy + switch (fe_params->hierarchy) { + case HIERARCHY_NONE: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5); + break; + + case HIERARCHY_1: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5); + break; + + case HIERARCHY_2: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5); + break; + + case HIERARCHY_4: + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5); + break; + + default: + return -EINVAL; + } + } + + // set bandwidth + switch (state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda10045h_set_bandwidth(state, fe_params->bandwidth_hz); + break; + + case TDA1004X_DEMOD_TDA10046: + tda10046h_set_bandwidth(state, fe_params->bandwidth_hz); + break; + } + + // set inversion + inversion = fe_params->inversion; + if (state->config->invert) + inversion = inversion ? INVERSION_OFF : INVERSION_ON; + switch (inversion) { + case INVERSION_OFF: + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0); + break; + + case INVERSION_ON: + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20); + break; + + default: + return -EINVAL; + } + + // set guard interval + switch (fe_params->guard_interval) { + case GUARD_INTERVAL_1_32: + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); + break; + + case GUARD_INTERVAL_1_16: + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); + break; + + case GUARD_INTERVAL_1_8: + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); + break; + + case GUARD_INTERVAL_1_4: + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); + break; + + case GUARD_INTERVAL_AUTO: + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); + break; + + default: + return -EINVAL; + } + + // set transmission mode + switch (fe_params->transmission_mode) { + case TRANSMISSION_MODE_2K: + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4); + break; + + case TRANSMISSION_MODE_8K: + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4); + break; + + case TRANSMISSION_MODE_AUTO: + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0); + break; + + default: + return -EINVAL; + } + + // start the lock + switch (state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); + break; + + case TDA1004X_DEMOD_TDA10046: + tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40); + msleep(1); + tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 1); + break; + } + + msleep(10); + + return 0; +} + +static int tda1004x_get_fe(struct dvb_frontend *fe, + struct dtv_frontend_properties *fe_params) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int status; + + dprintk("%s\n", __func__); + + status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); + if (status == -1) + return -EIO; + + /* Only update the properties cache if device is locked */ + if (!(status & 8)) + return 0; + + // inversion status + fe_params->inversion = INVERSION_OFF; + if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) + fe_params->inversion = INVERSION_ON; + if (state->config->invert) + fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; + + // bandwidth + switch (state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) { + case 0x14: + fe_params->bandwidth_hz = 8000000; + break; + case 0xdb: + fe_params->bandwidth_hz = 7000000; + break; + case 0x4f: + fe_params->bandwidth_hz = 6000000; + break; + } + break; + case TDA1004X_DEMOD_TDA10046: + switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) { + case 0x5c: + case 0x54: + fe_params->bandwidth_hz = 8000000; + break; + case 0x6a: + case 0x60: + fe_params->bandwidth_hz = 7000000; + break; + case 0x7b: + case 0x70: + fe_params->bandwidth_hz = 6000000; + break; + } + break; + } + + // FEC + fe_params->code_rate_HP = + tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7); + fe_params->code_rate_LP = + tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7); + + /* modulation */ + switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) { + case 0: + fe_params->modulation = QPSK; + break; + case 1: + fe_params->modulation = QAM_16; + break; + case 2: + fe_params->modulation = QAM_64; + break; + } + + // transmission mode + fe_params->transmission_mode = TRANSMISSION_MODE_2K; + if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) + fe_params->transmission_mode = TRANSMISSION_MODE_8K; + + // guard interval + switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { + case 0: + fe_params->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + fe_params->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + fe_params->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + fe_params->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + // hierarchy + switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { + case 0: + fe_params->hierarchy = HIERARCHY_NONE; + break; + case 1: + fe_params->hierarchy = HIERARCHY_1; + break; + case 2: + fe_params->hierarchy = HIERARCHY_2; + break; + case 3: + fe_params->hierarchy = HIERARCHY_4; + break; + } + + return 0; +} + +static int tda1004x_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int status; + int cber; + int vber; + + dprintk("%s\n", __func__); + + // read status + status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); + if (status == -1) + return -EIO; + + // decode + *fe_status = 0; + if (status & 4) + *fe_status |= FE_HAS_SIGNAL; + if (status & 2) + *fe_status |= FE_HAS_CARRIER; + if (status & 8) + *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + + // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi + // is getting anything valid + if (!(*fe_status & FE_HAS_VITERBI)) { + // read the CBER + cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB); + if (cber == -1) + return -EIO; + status = tda1004x_read_byte(state, TDA1004X_CBER_MSB); + if (status == -1) + return -EIO; + cber |= (status << 8); + // The address 0x20 should be read to cope with a TDA10046 bug + tda1004x_read_byte(state, TDA1004X_CBER_RESET); + + if (cber != 65535) + *fe_status |= FE_HAS_VITERBI; + } + + // if we DO have some valid VITERBI output, but don't already have SYNC + // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid. + if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) { + // read the VBER + vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB); + if (vber == -1) + return -EIO; + status = tda1004x_read_byte(state, TDA1004X_VBER_MID); + if (status == -1) + return -EIO; + vber |= (status << 8); + status = tda1004x_read_byte(state, TDA1004X_VBER_MSB); + if (status == -1) + return -EIO; + vber |= (status & 0x0f) << 16; + // The CVBER_LUT should be read to cope with TDA10046 hardware bug + tda1004x_read_byte(state, TDA1004X_CVBER_LUT); + + // if RS has passed some valid TS packets, then we must be + // getting some SYNC bytes + if (vber < 16632) + *fe_status |= FE_HAS_SYNC; + } + + // success + dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); + return 0; +} + +static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int tmp; + int reg = 0; + + dprintk("%s\n", __func__); + + // determine the register to use + switch (state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + reg = TDA10045H_S_AGC; + break; + + case TDA1004X_DEMOD_TDA10046: + reg = TDA10046H_AGC_IF_LEVEL; + break; + } + + // read it + tmp = tda1004x_read_byte(state, reg); + if (tmp < 0) + return -EIO; + + *signal = (tmp << 8) | tmp; + dprintk("%s: signal=0x%x\n", __func__, *signal); + return 0; +} + +static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int tmp; + + dprintk("%s\n", __func__); + + // read it + tmp = tda1004x_read_byte(state, TDA1004X_SNR); + if (tmp < 0) + return -EIO; + tmp = 255 - tmp; + + *snr = ((tmp << 8) | tmp); + dprintk("%s: snr=0x%x\n", __func__, *snr); + return 0; +} + +static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int tmp; + int tmp2; + int counter; + + dprintk("%s\n", __func__); + + // read the UCBLOCKS and reset + counter = 0; + tmp = tda1004x_read_byte(state, TDA1004X_UNCOR); + if (tmp < 0) + return -EIO; + tmp &= 0x7f; + while (counter++ < 5) { + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + + tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR); + if (tmp2 < 0) + return -EIO; + tmp2 &= 0x7f; + if ((tmp2 < tmp) || (tmp2 == 0)) + break; + } + + if (tmp != 0x7f) + *ucblocks = tmp; + else + *ucblocks = 0xffffffff; + + dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); + return 0; +} + +static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int tmp; + + dprintk("%s\n", __func__); + + // read it in + tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB); + if (tmp < 0) + return -EIO; + *ber = tmp << 1; + tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB); + if (tmp < 0) + return -EIO; + *ber |= (tmp << 9); + // The address 0x20 should be read to cope with a TDA10046 bug + tda1004x_read_byte(state, TDA1004X_CBER_RESET); + + dprintk("%s: ber=0x%x\n", __func__, *ber); + return 0; +} + +static int tda1004x_sleep(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + int gpio_conf; + + switch (state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10); + break; + + case TDA1004X_DEMOD_TDA10046: + /* set outputs to tristate */ + tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); + /* invert GPIO 1 and 3 if desired*/ + gpio_conf = state->config->gpio_config; + if (gpio_conf >= TDA10046_GP00_I) + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, + (gpio_conf & 0x0f) ^ 0x0a); + + tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); + break; + } + + return 0; +} + +static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + if (enable) { + return tda1004x_enable_tuner_i2c(state); + } else { + return tda1004x_disable_tuner_i2c(state); + } +} + +static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 800; + /* Drift compensation makes no sense for DVB-T */ + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void tda1004x_release(struct dvb_frontend* fe) +{ + struct tda1004x_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops tda10045_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Philips TDA10045H DVB-T", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = tda1004x_release, + + .init = tda10045_init, + .sleep = tda1004x_sleep, + .write = tda1004x_write, + .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, + + .set_frontend = tda1004x_set_fe, + .get_frontend = tda1004x_get_fe, + .get_tune_settings = tda1004x_get_tune_settings, + + .read_status = tda1004x_read_status, + .read_ber = tda1004x_read_ber, + .read_signal_strength = tda1004x_read_signal_strength, + .read_snr = tda1004x_read_snr, + .read_ucblocks = tda1004x_read_ucblocks, +}; + +struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) +{ + struct tda1004x_state *state; + int id; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + if (!state) { + printk(KERN_ERR "Can't allocate memory for tda10045 state\n"); + return NULL; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->demod_type = TDA1004X_DEMOD_TDA10045; + + /* check if the demod is there */ + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + + if (id != 0x25) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); + kfree(state); + return NULL; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +static const struct dvb_frontend_ops tda10046_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Philips TDA10046H DVB-T", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = tda1004x_release, + + .init = tda10046_init, + .sleep = tda1004x_sleep, + .write = tda1004x_write, + .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, + + .set_frontend = tda1004x_set_fe, + .get_frontend = tda1004x_get_fe, + .get_tune_settings = tda1004x_get_tune_settings, + + .read_status = tda1004x_read_status, + .read_ber = tda1004x_read_ber, + .read_signal_strength = tda1004x_read_signal_strength, + .read_snr = tda1004x_read_snr, + .read_ucblocks = tda1004x_read_ucblocks, +}; + +struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) +{ + struct tda1004x_state *state; + int id; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + if (!state) { + printk(KERN_ERR "Can't allocate memory for tda10046 state\n"); + return NULL; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->demod_type = TDA1004X_DEMOD_TDA10046; + + /* check if the demod is there */ + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + if (id != 0x46) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); + kfree(state); + return NULL; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator"); +MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(tda10045_attach); +EXPORT_SYMBOL_GPL(tda10046_attach); diff --git a/drivers/media/dvb-frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h new file mode 100644 index 0000000000..e63578c0a9 --- /dev/null +++ b/drivers/media/dvb-frontends/tda1004x.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* + Driver for Philips tda1004xh OFDM Frontend + + (c) 2004 Andrew de Quincey + + + */ + +#ifndef TDA1004X_H +#define TDA1004X_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +enum tda10046_xtal { + TDA10046_XTAL_4M, + TDA10046_XTAL_16M, +}; + +enum tda10046_agc { + TDA10046_AGC_DEFAULT, /* original configuration */ + TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negative */ + TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ + TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ +}; + +/* Many (hybrid) boards use GPIO 1 and 3 + GPIO1 analog - dvb switch + GPIO3 firmware eeprom address switch +*/ +enum tda10046_gpio { + TDA10046_GPTRI = 0x00, /* All GPIOs tristate */ + TDA10046_GP00 = 0x40, /* GPIO3=0, GPIO1=0 */ + TDA10046_GP01 = 0x42, /* GPIO3=0, GPIO1=1 */ + TDA10046_GP10 = 0x48, /* GPIO3=1, GPIO1=0 */ + TDA10046_GP11 = 0x4a, /* GPIO3=1, GPIO1=1 */ + TDA10046_GP00_I = 0x80, /* GPIO3=0, GPIO1=0, invert in sleep mode*/ + TDA10046_GP01_I = 0x82, /* GPIO3=0, GPIO1=1, invert in sleep mode */ + TDA10046_GP10_I = 0x88, /* GPIO3=1, GPIO1=0, invert in sleep mode */ + TDA10046_GP11_I = 0x8a, /* GPIO3=1, GPIO1=1, invert in sleep mode */ +}; + +enum tda10046_if { + TDA10046_FREQ_3617, /* original config, 36,166 MHZ */ + TDA10046_FREQ_3613, /* 36,13 MHZ */ + TDA10046_FREQ_045, /* low IF, 4.0, 4.5, or 5.0 MHZ */ + TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */ +}; + +enum tda10046_tsout { + TDA10046_TS_PARALLEL = 0x00, /* parallel transport stream, default */ + TDA10046_TS_SERIAL = 0x01, /* serial transport stream */ +}; + +struct tda1004x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* does the "inversion" need inverted? */ + u8 invert; + + /* Does the OCLK signal need inverted? */ + u8 invert_oclk; + + /* parallel or serial transport stream */ + enum tda10046_tsout ts_mode; + + /* Xtal frequency, 4 or 16MHz*/ + enum tda10046_xtal xtal_freq; + + /* IF frequency */ + enum tda10046_if if_freq; + + /* AGC configuration */ + enum tda10046_agc agc_config; + + /* setting of GPIO1 and 3 */ + enum tda10046_gpio gpio_config; + + /* slave address and configuration of the tuner */ + u8 tuner_address; + u8 antenna_switch; + + /* if the board uses another I2c Bridge (tda8290), its address */ + u8 i2c_gate; + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +enum tda1004x_demod { + TDA1004X_DEMOD_TDA10045, + TDA1004X_DEMOD_TDA10046, +}; + +struct tda1004x_state { + struct i2c_adapter* i2c; + const struct tda1004x_config* config; + struct dvb_frontend frontend; + + /* private demod data */ + enum tda1004x_demod demod_type; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA1004X) +extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c); + +extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +static inline struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_TDA1004X + +static inline int tda1004x_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} + +#endif // TDA1004X_H diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c new file mode 100644 index 0000000000..6640851d8b --- /dev/null +++ b/drivers/media/dvb-frontends/tda10071.c @@ -0,0 +1,1253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#include "tda10071_priv.h" + +static const struct dvb_frontend_ops tda10071_ops; + +/* + * XXX: regmap_update_bits() does not fit our needs as it does not support + * partially volatile registers. Also it performs register read even mask is as + * wide as register value. + */ +/* write single register with mask */ +static int tda10071_wr_reg_mask(struct tda10071_dev *dev, + u8 reg, u8 val, u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return regmap_bulk_write(dev->regmap, reg, &val, 1); +} + +/* execute firmware command */ +static int tda10071_cmd_execute(struct tda10071_dev *dev, + struct tda10071_cmd *cmd) +{ + struct i2c_client *client = dev->client; + int ret, i; + unsigned int uitmp; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + mutex_lock(&dev->cmd_execute_mutex); + + /* write cmd and args for firmware */ + ret = regmap_bulk_write(dev->regmap, 0x00, cmd->args, cmd->len); + if (ret) + goto error_mutex_unlock; + + /* start cmd execution */ + ret = regmap_write(dev->regmap, 0x1f, 1); + if (ret) + goto error_mutex_unlock; + + /* wait cmd execution terminate */ + for (i = 1000, uitmp = 1; i && uitmp; i--) { + ret = regmap_read(dev->regmap, 0x1f, &uitmp); + if (ret) + goto error_mutex_unlock; + + usleep_range(200, 5000); + } + + mutex_unlock(&dev->cmd_execute_mutex); + dev_dbg(&client->dev, "loop=%d\n", i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + return ret; +error_mutex_unlock: + mutex_unlock(&dev->cmd_execute_mutex); +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode fe_sec_tone_mode) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + int ret; + u8 tone; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + dev_dbg(&client->dev, "tone_mode=%d\n", fe_sec_tone_mode); + + switch (fe_sec_tone_mode) { + case SEC_TONE_ON: + tone = 1; + break; + case SEC_TONE_OFF: + tone = 0; + break; + default: + dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n"); + ret = -EINVAL; + goto error; + } + + cmd.args[0] = CMD_LNB_PCB_CONFIG; + cmd.args[1] = 0; + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.args[4] = tone; + cmd.len = 5; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage fe_sec_voltage) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + int ret; + u8 voltage; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + dev_dbg(&client->dev, "voltage=%d\n", fe_sec_voltage); + + switch (fe_sec_voltage) { + case SEC_VOLTAGE_13: + voltage = 0; + break; + case SEC_VOLTAGE_18: + voltage = 1; + break; + case SEC_VOLTAGE_OFF: + voltage = 0; + break; + default: + dev_dbg(&client->dev, "invalid fe_sec_voltage\n"); + ret = -EINVAL; + goto error; + } + + cmd.args[0] = CMD_LNB_SET_DC_LEVEL; + cmd.args[1] = 0; + cmd.args[2] = voltage; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *diseqc_cmd) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + int ret, i; + unsigned int uitmp; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + dev_dbg(&client->dev, "msg_len=%d\n", diseqc_cmd->msg_len); + + if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { + ret = -EINVAL; + goto error; + } + + /* wait LNB TX */ + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); + if (ret) + goto error; + uitmp = (uitmp >> 0) & 1; + usleep_range(10000, 20000); + } + + dev_dbg(&client->dev, "loop=%d\n", i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00); + if (ret) + goto error; + + cmd.args[0] = CMD_LNB_SEND_DISEQC; + cmd.args[1] = 0; + cmd.args[2] = 0; + cmd.args[3] = 0; + cmd.args[4] = 2; + cmd.args[5] = 0; + cmd.args[6] = diseqc_cmd->msg_len; + memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len); + cmd.len = 7 + diseqc_cmd->msg_len; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, + struct dvb_diseqc_slave_reply *reply) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + int ret, i; + unsigned int uitmp; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + dev_dbg(&client->dev, "\n"); + + /* wait LNB RX */ + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); + if (ret) + goto error; + uitmp = (uitmp >> 1) & 1; + usleep_range(10000, 20000); + } + + dev_dbg(&client->dev, "loop=%d\n", i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + /* reply len */ + ret = regmap_read(dev->regmap, 0x46, &uitmp); + if (ret) + goto error; + + reply->msg_len = uitmp & 0x1f; /* [4:0] */ + if (reply->msg_len > sizeof(reply->msg)) + reply->msg_len = sizeof(reply->msg); /* truncate API max */ + + /* read reply */ + cmd.args[0] = CMD_LNB_UPDATE_REPLY; + cmd.args[1] = 0; + cmd.len = 2; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + ret = regmap_bulk_read(dev->regmap, cmd.len, reply->msg, + reply->msg_len); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd fe_sec_mini_cmd) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + int ret, i; + unsigned int uitmp; + u8 burst; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd); + + switch (fe_sec_mini_cmd) { + case SEC_MINI_A: + burst = 0; + break; + case SEC_MINI_B: + burst = 1; + break; + default: + dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n"); + ret = -EINVAL; + goto error; + } + + /* wait LNB TX */ + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); + if (ret) + goto error; + uitmp = (uitmp >> 0) & 1; + usleep_range(10000, 20000); + } + + dev_dbg(&client->dev, "loop=%d\n", i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00); + if (ret) + goto error; + + cmd.args[0] = CMD_LNB_SEND_TONEBURST; + cmd.args[1] = 0; + cmd.args[2] = burst; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda10071_cmd cmd; + int ret; + unsigned int uitmp; + u8 buf[8]; + + *status = 0; + + if (!dev->warm) { + ret = 0; + goto error; + } + + ret = regmap_read(dev->regmap, 0x39, &uitmp); + if (ret) + goto error; + + /* 0x39[0] tuner PLL */ + if (uitmp & 0x02) /* demod PLL */ + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + if (uitmp & 0x04) /* viterbi or LDPC*/ + *status |= FE_HAS_VITERBI; + if (uitmp & 0x08) /* RS or BCH */ + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + dev->fe_status = *status; + + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + cmd.args[0] = CMD_GET_AGCACC; + cmd.args[1] = 0; + cmd.len = 2; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + /* input power estimate dBm */ + ret = regmap_read(dev->regmap, 0x50, &uitmp); + if (ret) + goto error; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = (int) (uitmp - 256) * 1000; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + /* Es/No */ + ret = regmap_bulk_read(dev->regmap, 0x3a, buf, 2); + if (ret) + goto error; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = (buf[0] << 8 | buf[1] << 0) * 100; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* UCB/PER/BER */ + if (dev->fe_status & FE_HAS_LOCK) { + /* TODO: report total bits/packets */ + u8 delivery_system, reg, len; + + switch (dev->delivery_system) { + case SYS_DVBS: + reg = 0x4c; + len = 8; + delivery_system = 1; + break; + case SYS_DVBS2: + reg = 0x4d; + len = 4; + delivery_system = 0; + break; + default: + ret = -EINVAL; + goto error; + } + + ret = regmap_read(dev->regmap, reg, &uitmp); + if (ret) + goto error; + + if (dev->meas_count == uitmp) { + dev_dbg(&client->dev, "meas not ready=%02x\n", uitmp); + ret = 0; + goto error; + } else { + dev->meas_count = uitmp; + } + + cmd.args[0] = CMD_BER_UPDATE_COUNTERS; + cmd.args[1] = 0; + cmd.args[2] = delivery_system; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, len); + if (ret) + goto error; + + if (dev->delivery_system == SYS_DVBS) { + u32 bit_error = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + + dev->dvbv3_ber = bit_error; + dev->post_bit_error += bit_error; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + dev->block_error += buf[4] << 8 | buf[5] << 0; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = dev->block_error; + } else { + dev->dvbv3_ber = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += buf[0] << 8 | buf[1] << 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + return 0; +} + +static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + unsigned int uitmp; + + if (c->strength.stat[0].scale == FE_SCALE_DECIBEL) { + uitmp = div_s64(c->strength.stat[0].svalue, 1000) + 256; + uitmp = clamp(uitmp, 181U, 236U); /* -75dBm - -20dBm */ + /* scale value to 0x0000-0xffff */ + *strength = (uitmp-181) * 0xffff / (236-181); + } else { + *strength = 0; + } + return 0; +} + +static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + + *ber = dev->dvbv3_ber; + return 0; +} + +static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->block_error.stat[0].scale == FE_SCALE_COUNTER) + *ucblocks = c->block_error.stat[0].uvalue; + else + *ucblocks = 0; + return 0; +} + +static int tda10071_set_frontend(struct dvb_frontend *fe) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u8 mode, rolloff, pilot, inversion, div; + enum fe_modulation modulation; + + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + c->delivery_system, c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); + + dev->delivery_system = SYS_UNDEFINED; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + switch (c->inversion) { + case INVERSION_OFF: + inversion = 1; + break; + case INVERSION_ON: + inversion = 0; + break; + case INVERSION_AUTO: + /* 2 = auto; try first on then off + * 3 = auto; try first off then on */ + inversion = 3; + break; + default: + dev_dbg(&client->dev, "invalid inversion\n"); + ret = -EINVAL; + goto error; + } + + switch (c->delivery_system) { + case SYS_DVBS: + modulation = QPSK; + rolloff = 0; + pilot = 2; + break; + case SYS_DVBS2: + modulation = c->modulation; + + switch (c->rolloff) { + case ROLLOFF_20: + rolloff = 2; + break; + case ROLLOFF_25: + rolloff = 1; + break; + case ROLLOFF_35: + rolloff = 0; + break; + case ROLLOFF_AUTO: + default: + dev_dbg(&client->dev, "invalid rolloff\n"); + ret = -EINVAL; + goto error; + } + + switch (c->pilot) { + case PILOT_OFF: + pilot = 0; + break; + case PILOT_ON: + pilot = 1; + break; + case PILOT_AUTO: + pilot = 2; + break; + default: + dev_dbg(&client->dev, "invalid pilot\n"); + ret = -EINVAL; + goto error; + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto error; + } + + for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { + if (c->delivery_system == TDA10071_MODCOD[i].delivery_system && + modulation == TDA10071_MODCOD[i].modulation && + c->fec_inner == TDA10071_MODCOD[i].fec) { + mode = TDA10071_MODCOD[i].val; + dev_dbg(&client->dev, "mode found=%02x\n", mode); + break; + } + } + + if (mode == 0xff) { + dev_dbg(&client->dev, "invalid parameter combination\n"); + ret = -EINVAL; + goto error; + } + + if (c->symbol_rate <= 5000000) + div = 14; + else + div = 4; + + ret = regmap_write(dev->regmap, 0x81, div); + if (ret) + goto error; + + ret = regmap_write(dev->regmap, 0xe3, div); + if (ret) + goto error; + + cmd.args[0] = CMD_CHANGE_CHANNEL; + cmd.args[1] = 0; + cmd.args[2] = mode; + cmd.args[3] = (c->frequency >> 16) & 0xff; + cmd.args[4] = (c->frequency >> 8) & 0xff; + cmd.args[5] = (c->frequency >> 0) & 0xff; + cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff; + cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff; + cmd.args[8] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 8) & 0xff; + cmd.args[9] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 0) & 0xff; + cmd.args[10] = rolloff; + cmd.args[11] = inversion; + cmd.args[12] = pilot; + cmd.args[13] = 0x00; + cmd.args[14] = 0x00; + cmd.len = 15; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + dev->delivery_system = c->delivery_system; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret, i; + u8 buf[5], tmp; + + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + ret = 0; + goto error; + } + + ret = regmap_bulk_read(dev->regmap, 0x30, buf, 5); + if (ret) + goto error; + + tmp = buf[0] & 0x3f; + for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { + if (tmp == TDA10071_MODCOD[i].val) { + c->modulation = TDA10071_MODCOD[i].modulation; + c->fec_inner = TDA10071_MODCOD[i].fec; + c->delivery_system = TDA10071_MODCOD[i].delivery_system; + } + } + + switch ((buf[1] >> 0) & 0x01) { + case 0: + c->inversion = INVERSION_ON; + break; + case 1: + c->inversion = INVERSION_OFF; + break; + } + + switch ((buf[1] >> 7) & 0x01) { + case 0: + c->pilot = PILOT_OFF; + break; + case 1: + c->pilot = PILOT_ON; + break; + } + + c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); + + ret = regmap_bulk_read(dev->regmap, 0x52, buf, 3); + if (ret) + goto error; + + c->symbol_rate = ((buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0)) * 1000; + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_init(struct dvb_frontend *fe) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda10071_cmd cmd; + int ret, i, len, remaining, fw_size; + unsigned int uitmp; + const struct firmware *fw; + u8 *fw_file = TDA10071_FIRMWARE; + u8 tmp, buf[4]; + struct tda10071_reg_val_mask tab[] = { + { 0xcd, 0x00, 0x07 }, + { 0x80, 0x00, 0x02 }, + { 0xcd, 0x00, 0xc0 }, + { 0xce, 0x00, 0x1b }, + { 0x9d, 0x00, 0x01 }, + { 0x9d, 0x00, 0x02 }, + { 0x9e, 0x00, 0x01 }, + { 0x87, 0x00, 0x80 }, + { 0xce, 0x00, 0x08 }, + { 0xce, 0x00, 0x10 }, + }; + struct tda10071_reg_val_mask tab2[] = { + { 0xf1, 0x70, 0xff }, + { 0x88, dev->pll_multiplier, 0x3f }, + { 0x89, 0x00, 0x10 }, + { 0x89, 0x10, 0x10 }, + { 0xc0, 0x01, 0x01 }, + { 0xc0, 0x00, 0x01 }, + { 0xe0, 0xff, 0xff }, + { 0xe0, 0x00, 0xff }, + { 0x96, 0x1e, 0x7e }, + { 0x8b, 0x08, 0x08 }, + { 0x8b, 0x00, 0x08 }, + { 0x8f, 0x1a, 0x7e }, + { 0x8c, 0x68, 0xff }, + { 0x8d, 0x08, 0xff }, + { 0x8e, 0x4c, 0xff }, + { 0x8f, 0x01, 0x01 }, + { 0x8b, 0x04, 0x04 }, + { 0x8b, 0x00, 0x04 }, + { 0x87, 0x05, 0x07 }, + { 0x80, 0x00, 0x20 }, + { 0xc8, 0x01, 0xff }, + { 0xb4, 0x47, 0xff }, + { 0xb5, 0x9c, 0xff }, + { 0xb6, 0x7d, 0xff }, + { 0xba, 0x00, 0x03 }, + { 0xb7, 0x47, 0xff }, + { 0xb8, 0x9c, 0xff }, + { 0xb9, 0x7d, 0xff }, + { 0xba, 0x00, 0x0c }, + { 0xc8, 0x00, 0xff }, + { 0xcd, 0x00, 0x04 }, + { 0xcd, 0x00, 0x20 }, + { 0xe8, 0x02, 0xff }, + { 0xcf, 0x20, 0xff }, + { 0x9b, 0xd7, 0xff }, + { 0x9a, 0x01, 0x03 }, + { 0xa8, 0x05, 0x0f }, + { 0xa8, 0x65, 0xf0 }, + { 0xa6, 0xa0, 0xf0 }, + { 0x9d, 0x50, 0xfc }, + { 0x9e, 0x20, 0xe0 }, + { 0xa3, 0x1c, 0x7c }, + { 0xd5, 0x03, 0x03 }, + }; + + if (dev->warm) { + /* warm state - wake up device from sleep */ + + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = tda10071_wr_reg_mask(dev, tab[i].reg, + tab[i].val, tab[i].mask); + if (ret) + goto error; + } + + cmd.args[0] = CMD_SET_SLEEP_MODE; + cmd.args[1] = 0; + cmd.args[2] = 0; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + } else { + /* cold state - try to download firmware */ + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &client->dev); + if (ret) { + dev_err(&client->dev, + "did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware\n", + fw_file, ret); + goto error; + } + + /* init */ + for (i = 0; i < ARRAY_SIZE(tab2); i++) { + ret = tda10071_wr_reg_mask(dev, tab2[i].reg, + tab2[i].val, tab2[i].mask); + if (ret) + goto error_release_firmware; + } + + /* download firmware */ + ret = regmap_write(dev->regmap, 0xe0, 0x7f); + if (ret) + goto error_release_firmware; + + ret = regmap_write(dev->regmap, 0xf7, 0x81); + if (ret) + goto error_release_firmware; + + ret = regmap_write(dev->regmap, 0xf8, 0x00); + if (ret) + goto error_release_firmware; + + ret = regmap_write(dev->regmap, 0xf9, 0x00); + if (ret) + goto error_release_firmware; + + dev_info(&client->dev, + "found a '%s' in cold state, will try to load a firmware\n", + tda10071_ops.info.name); + dev_info(&client->dev, "downloading firmware from file '%s'\n", + fw_file); + + /* do not download last byte */ + fw_size = fw->size - 1; + + for (remaining = fw_size; remaining > 0; + remaining -= (dev->i2c_wr_max - 1)) { + len = remaining; + if (len > (dev->i2c_wr_max - 1)) + len = (dev->i2c_wr_max - 1); + + ret = regmap_bulk_write(dev->regmap, 0xfa, + (u8 *) &fw->data[fw_size - remaining], len); + if (ret) { + dev_err(&client->dev, + "firmware download failed=%d\n", ret); + goto error_release_firmware; + } + } + release_firmware(fw); + + ret = regmap_write(dev->regmap, 0xf7, 0x0c); + if (ret) + goto error; + + ret = regmap_write(dev->regmap, 0xe0, 0x00); + if (ret) + goto error; + + /* wait firmware start */ + msleep(250); + + /* firmware status */ + ret = regmap_read(dev->regmap, 0x51, &uitmp); + if (ret) + goto error; + + if (uitmp) { + dev_info(&client->dev, "firmware did not run\n"); + ret = -EFAULT; + goto error; + } else { + dev->warm = true; + } + + cmd.args[0] = CMD_GET_FW_VERSION; + cmd.len = 1; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, 4); + if (ret) + goto error; + + dev_info(&client->dev, "firmware version %d.%d.%d.%d\n", + buf[0], buf[1], buf[2], buf[3]); + dev_info(&client->dev, "found a '%s' in warm state\n", + tda10071_ops.info.name); + + ret = regmap_bulk_read(dev->regmap, 0x81, buf, 2); + if (ret) + goto error; + + cmd.args[0] = CMD_DEMOD_INIT; + cmd.args[1] = ((dev->clk / 1000) >> 8) & 0xff; + cmd.args[2] = ((dev->clk / 1000) >> 0) & 0xff; + cmd.args[3] = buf[0]; + cmd.args[4] = buf[1]; + cmd.args[5] = dev->pll_multiplier; + cmd.args[6] = dev->spec_inv; + cmd.args[7] = 0x00; + cmd.len = 8; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + if (dev->tuner_i2c_addr) + tmp = dev->tuner_i2c_addr; + else + tmp = 0x14; + + cmd.args[0] = CMD_TUNER_INIT; + cmd.args[1] = 0x00; + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.args[4] = 0x00; + cmd.args[5] = tmp; + cmd.args[6] = 0x00; + cmd.args[7] = 0x03; + cmd.args[8] = 0x02; + cmd.args[9] = 0x02; + cmd.args[10] = 0x00; + cmd.args[11] = 0x00; + cmd.args[12] = 0x00; + cmd.args[13] = 0x00; + cmd.args[14] = 0x00; + cmd.len = 15; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + cmd.args[0] = CMD_MPEG_CONFIG; + cmd.args[1] = 0; + cmd.args[2] = dev->ts_mode; + cmd.args[3] = 0x00; + cmd.args[4] = 0x04; + cmd.args[5] = 0x00; + cmd.len = 6; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + ret = regmap_update_bits(dev->regmap, 0xf0, 0x01, 0x01); + if (ret) + goto error; + + cmd.args[0] = CMD_LNB_CONFIG; + cmd.args[1] = 0; + cmd.args[2] = 150; + cmd.args[3] = 3; + cmd.args[4] = 22; + cmd.args[5] = 1; + cmd.args[6] = 1; + cmd.args[7] = 30; + cmd.args[8] = 30; + cmd.args[9] = 30; + cmd.args[10] = 30; + cmd.len = 11; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + cmd.args[0] = CMD_BER_CONTROL; + cmd.args[1] = 0; + cmd.args[2] = 14; + cmd.args[3] = 14; + cmd.len = 4; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + } + + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return ret; +error_release_firmware: + release_firmware(fw); +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_sleep(struct dvb_frontend *fe) +{ + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct tda10071_cmd cmd; + int ret, i; + struct tda10071_reg_val_mask tab[] = { + { 0xcd, 0x07, 0x07 }, + { 0x80, 0x02, 0x02 }, + { 0xcd, 0xc0, 0xc0 }, + { 0xce, 0x1b, 0x1b }, + { 0x9d, 0x01, 0x01 }, + { 0x9d, 0x02, 0x02 }, + { 0x9e, 0x01, 0x01 }, + { 0x87, 0x80, 0x80 }, + { 0xce, 0x08, 0x08 }, + { 0xce, 0x10, 0x10 }, + }; + + if (!dev->warm) { + ret = -EFAULT; + goto error; + } + + cmd.args[0] = CMD_SET_SLEEP_MODE; + cmd.args[1] = 0; + cmd.args[2] = 1; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; + + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = tda10071_wr_reg_mask(dev, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret) + goto error; + } + + return ret; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 8000; + s->step_size = 0; + s->max_drift = 0; + + return 0; +} + +static const struct dvb_frontend_ops tda10071_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "NXP TDA10071", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_tolerance_hz = 5 * MHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_RECOVER | + FE_CAN_2G_MODULATION + }, + + .get_tune_settings = tda10071_get_tune_settings, + + .init = tda10071_init, + .sleep = tda10071_sleep, + + .set_frontend = tda10071_set_frontend, + .get_frontend = tda10071_get_frontend, + + .read_status = tda10071_read_status, + .read_snr = tda10071_read_snr, + .read_signal_strength = tda10071_read_signal_strength, + .read_ber = tda10071_read_ber, + .read_ucblocks = tda10071_read_ucblocks, + + .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd, + .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply, + .diseqc_send_burst = tda10071_diseqc_send_burst, + + .set_tone = tda10071_set_tone, + .set_voltage = tda10071_set_voltage, +}; + +static struct dvb_frontend *tda10071_get_dvb_frontend(struct i2c_client *client) +{ + struct tda10071_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static int tda10071_probe(struct i2c_client *client) +{ + struct tda10071_dev *dev; + struct tda10071_platform_data *pdata = client->dev.platform_data; + int ret; + unsigned int uitmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->client = client; + mutex_init(&dev->cmd_execute_mutex); + dev->clk = pdata->clk; + dev->i2c_wr_max = pdata->i2c_wr_max; + dev->ts_mode = pdata->ts_mode; + dev->spec_inv = pdata->spec_inv; + dev->pll_multiplier = pdata->pll_multiplier; + dev->tuner_i2c_addr = pdata->tuner_i2c_addr; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + /* chip ID */ + ret = regmap_read(dev->regmap, 0xff, &uitmp); + if (ret) + goto err_kfree; + if (uitmp != 0x0f) { + ret = -ENODEV; + goto err_kfree; + } + + /* chip type */ + ret = regmap_read(dev->regmap, 0xdd, &uitmp); + if (ret) + goto err_kfree; + if (uitmp != 0x00) { + ret = -ENODEV; + goto err_kfree; + } + + /* chip version */ + ret = regmap_read(dev->regmap, 0xfe, &uitmp); + if (ret) + goto err_kfree; + if (uitmp != 0x01) { + ret = -ENODEV; + goto err_kfree; + } + + /* create dvb_frontend */ + memcpy(&dev->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + i2c_set_clientdata(client, dev); + + /* setup callbacks */ + pdata->get_dvb_frontend = tda10071_get_dvb_frontend; + + dev_info(&client->dev, "NXP TDA10071 successfully identified\n"); + return 0; +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void tda10071_remove(struct i2c_client *client) +{ + struct tda10071_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(dev); +} + +static const struct i2c_device_id tda10071_id_table[] = { + {"tda10071_cx24118", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tda10071_id_table); + +static struct i2c_driver tda10071_driver = { + .driver = { + .name = "tda10071", + .suppress_bind_attrs = true, + }, + .probe = tda10071_probe, + .remove = tda10071_remove, + .id_table = tda10071_id_table, +}; + +module_i2c_driver(tda10071_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(TDA10071_FIRMWARE); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h new file mode 100644 index 0000000000..c282c836f5 --- /dev/null +++ b/drivers/media/dvb-frontends/tda10071.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#ifndef TDA10071_H +#define TDA10071_H + +#include <linux/dvb/frontend.h> + +/* + * I2C address + * 0x05, 0x55, + */ + +/** + * struct tda10071_platform_data - Platform data for the tda10071 driver + * @clk: Clock frequency. + * @i2c_wr_max: Max bytes I2C adapter can write at once. + * @ts_mode: TS mode. + * @spec_inv: Input spectrum inversion. + * @pll_multiplier: PLL multiplier. + * @tuner_i2c_addr: CX24118A tuner I2C address (0x14, 0x54, ...). + * @get_dvb_frontend: Get DVB frontend. + */ +struct tda10071_platform_data { + u32 clk; + u16 i2c_wr_max; +#define TDA10071_TS_SERIAL 0 +#define TDA10071_TS_PARALLEL 1 + u8 ts_mode; + bool spec_inv; + u8 pll_multiplier; + u8 tuner_i2c_addr; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); +}; + +#endif /* TDA10071_H */ diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h new file mode 100644 index 0000000000..2d86508352 --- /dev/null +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + */ + +#ifndef TDA10071_PRIV +#define TDA10071_PRIV + +#include <media/dvb_frontend.h> +#include "tda10071.h" +#include <linux/firmware.h> +#include <linux/regmap.h> + +struct tda10071_dev { + struct dvb_frontend fe; + struct i2c_client *client; + struct regmap *regmap; + struct mutex cmd_execute_mutex; + u32 clk; + u16 i2c_wr_max; + u8 ts_mode; + bool spec_inv; + u8 pll_multiplier; + u8 tuner_i2c_addr; + + u8 meas_count; + u32 dvbv3_ber; + enum fe_status fe_status; + enum fe_delivery_system delivery_system; + bool warm; /* FW running */ + u64 post_bit_error; + u64 block_error; +}; + +static struct tda10071_modcod { + enum fe_delivery_system delivery_system; + enum fe_modulation modulation; + enum fe_code_rate fec; + u8 val; +} TDA10071_MODCOD[] = { + /* NBC-QPSK */ + { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 }, + { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, + /* 8PSK */ + { SYS_DVBS2, PSK_8, FEC_AUTO, 0x00 }, + { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, + /* QPSK */ + { SYS_DVBS, QPSK, FEC_AUTO, 0x2d }, + { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, + { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, + { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, + { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, + { SYS_DVBS, QPSK, FEC_7_8, 0x32 }, +}; + +struct tda10071_reg_val_mask { + u8 reg; + u8 val; + u8 mask; +}; + +/* firmware filename */ +#define TDA10071_FIRMWARE "dvb-fe-tda10071.fw" + +/* firmware commands */ +#define CMD_DEMOD_INIT 0x10 +#define CMD_CHANGE_CHANNEL 0x11 +#define CMD_MPEG_CONFIG 0x13 +#define CMD_TUNER_INIT 0x15 +#define CMD_GET_AGCACC 0x1a + +#define CMD_LNB_CONFIG 0x20 +#define CMD_LNB_SEND_DISEQC 0x21 +#define CMD_LNB_SET_DC_LEVEL 0x22 +#define CMD_LNB_PCB_CONFIG 0x23 +#define CMD_LNB_SEND_TONEBURST 0x24 +#define CMD_LNB_UPDATE_REPLY 0x25 + +#define CMD_GET_FW_VERSION 0x35 +#define CMD_SET_SLEEP_MODE 0x36 +#define CMD_BER_CONTROL 0x3e +#define CMD_BER_UPDATE_COUNTERS 0x3f + +/* firmware command struct */ +#define TDA10071_ARGLEN 30 +struct tda10071_cmd { + u8 args[TDA10071_ARGLEN]; + u8 len; +}; + + +#endif /* TDA10071_PRIV */ diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c new file mode 100644 index 0000000000..b449514ae5 --- /dev/null +++ b/drivers/media/dvb-frontends/tda10086.c @@ -0,0 +1,767 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* + Driver for Philips tda10086 DVBS Demodulator + + (c) 2006 Andrew de Quincey + + + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "tda10086.h" + +#define SACLK 96000000U + +struct tda10086_state { + struct i2c_adapter* i2c; + const struct tda10086_config* config; + struct dvb_frontend frontend; + + /* private demod data */ + u32 frequency; + u32 symbol_rate; + bool has_lock; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda10086: " args); \ + } while (0) + +static int tda10086_write_byte(struct tda10086_state *state, int reg, int data) +{ + int ret; + u8 b0[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 }; + + msg.addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? ret : 0; +} + +static int tda10086_read_byte(struct tda10086_state *state, int reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, + { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; + + msg[0].addr = state->config->demod_address; + msg[1].addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, + ret); + return ret; + } + + return b1[0]; +} + +static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data) +{ + int val; + + /* read a byte and check */ + val = tda10086_read_byte(state, reg); + if (val < 0) + return val; + + /* mask if off */ + val = val & ~mask; + val |= data & 0xff; + + /* write it out again */ + return tda10086_write_byte(state, reg, val); +} + +static int tda10086_init(struct dvb_frontend* fe) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 t22k_off = 0x80; + + dprintk ("%s\n", __func__); + + if (state->config->diseqc_tone) + t22k_off = 0; + /* reset */ + tda10086_write_byte(state, 0x00, 0x00); + msleep(10); + + /* misc setup */ + tda10086_write_byte(state, 0x01, 0x94); + tda10086_write_byte(state, 0x02, 0x35); /* NOTE: TT drivers appear to disable CSWP */ + tda10086_write_byte(state, 0x03, 0xe4); + tda10086_write_byte(state, 0x04, 0x43); + tda10086_write_byte(state, 0x0c, 0x0c); + tda10086_write_byte(state, 0x1b, 0xb0); /* noise threshold */ + tda10086_write_byte(state, 0x20, 0x89); /* misc */ + tda10086_write_byte(state, 0x30, 0x04); /* acquisition period length */ + tda10086_write_byte(state, 0x32, 0x00); /* irq off */ + tda10086_write_byte(state, 0x31, 0x56); /* setup AFC */ + + /* setup PLL (this assumes SACLK = 96MHz) */ + tda10086_write_byte(state, 0x55, 0x2c); /* misc PLL setup */ + if (state->config->xtal_freq == TDA10086_XTAL_16M) { + tda10086_write_byte(state, 0x3a, 0x0b); /* M=12 */ + tda10086_write_byte(state, 0x3b, 0x01); /* P=2 */ + } else { + tda10086_write_byte(state, 0x3a, 0x17); /* M=24 */ + tda10086_write_byte(state, 0x3b, 0x00); /* P=1 */ + } + tda10086_write_mask(state, 0x55, 0x20, 0x00); /* powerup PLL */ + + /* setup TS interface */ + tda10086_write_byte(state, 0x11, 0x81); + tda10086_write_byte(state, 0x12, 0x81); + tda10086_write_byte(state, 0x19, 0x40); /* parallel mode A + MSBFIRST */ + tda10086_write_byte(state, 0x56, 0x80); /* powerdown WPLL - unused in the mode we use */ + tda10086_write_byte(state, 0x57, 0x08); /* bypass WPLL - unused in the mode we use */ + tda10086_write_byte(state, 0x10, 0x2a); + + /* setup ADC */ + tda10086_write_byte(state, 0x58, 0x61); /* ADC setup */ + tda10086_write_mask(state, 0x58, 0x01, 0x00); /* powerup ADC */ + + /* setup AGC */ + tda10086_write_byte(state, 0x05, 0x0B); + tda10086_write_byte(state, 0x37, 0x63); + tda10086_write_byte(state, 0x3f, 0x0a); /* NOTE: flydvb varies it */ + tda10086_write_byte(state, 0x40, 0x64); + tda10086_write_byte(state, 0x41, 0x4f); + tda10086_write_byte(state, 0x42, 0x43); + + /* setup viterbi */ + tda10086_write_byte(state, 0x1a, 0x11); /* VBER 10^6, DVB, QPSK */ + + /* setup carrier recovery */ + tda10086_write_byte(state, 0x3d, 0x80); + + /* setup SEC */ + tda10086_write_byte(state, 0x36, t22k_off); /* all SEC off, 22k tone */ + tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); + tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); + + return 0; +} + +static void tda10086_diseqc_wait(struct tda10086_state *state) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(200); + while (!(tda10086_read_byte(state, 0x50) & 0x01)) { + if(time_after(jiffies, timeout)) { + printk("%s: diseqc queue not ready, command may be lost.\n", __func__); + break; + } + msleep(10); + } +} + +static int tda10086_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 t22k_off = 0x80; + + dprintk ("%s\n", __func__); + + if (state->config->diseqc_tone) + t22k_off = 0; + + switch (tone) { + case SEC_TONE_OFF: + tda10086_write_byte(state, 0x36, t22k_off); + break; + + case SEC_TONE_ON: + tda10086_write_byte(state, 0x36, 0x01 + t22k_off); + break; + } + + return 0; +} + +static int tda10086_send_master_cmd (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct tda10086_state* state = fe->demodulator_priv; + int i; + u8 oldval; + u8 t22k_off = 0x80; + + dprintk ("%s\n", __func__); + + if (state->config->diseqc_tone) + t22k_off = 0; + + if (cmd->msg_len > 6) + return -EINVAL; + oldval = tda10086_read_byte(state, 0x36); + + for(i=0; i< cmd->msg_len; i++) { + tda10086_write_byte(state, 0x48+i, cmd->msg[i]); + } + tda10086_write_byte(state, 0x36, (0x08 + t22k_off) + | ((cmd->msg_len - 1) << 4)); + + tda10086_diseqc_wait(state); + + tda10086_write_byte(state, 0x36, oldval); + + return 0; +} + +static int tda10086_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 oldval = tda10086_read_byte(state, 0x36); + u8 t22k_off = 0x80; + + dprintk ("%s\n", __func__); + + if (state->config->diseqc_tone) + t22k_off = 0; + + switch(minicmd) { + case SEC_MINI_A: + tda10086_write_byte(state, 0x36, 0x04 + t22k_off); + break; + + case SEC_MINI_B: + tda10086_write_byte(state, 0x36, 0x06 + t22k_off); + break; + } + + tda10086_diseqc_wait(state); + + tda10086_write_byte(state, 0x36, oldval); + + return 0; +} + +static int tda10086_set_inversion(struct tda10086_state *state, + struct dtv_frontend_properties *fe_params) +{ + u8 invval = 0x80; + + dprintk ("%s %i %i\n", __func__, fe_params->inversion, state->config->invert); + + switch(fe_params->inversion) { + case INVERSION_OFF: + if (state->config->invert) + invval = 0x40; + break; + case INVERSION_ON: + if (!state->config->invert) + invval = 0x40; + break; + case INVERSION_AUTO: + invval = 0x00; + break; + } + tda10086_write_mask(state, 0x0c, 0xc0, invval); + + return 0; +} + +static int tda10086_set_symbol_rate(struct tda10086_state *state, + struct dtv_frontend_properties *fe_params) +{ + u8 dfn = 0; + u8 afs = 0; + u8 byp = 0; + u8 reg37 = 0x43; + u8 reg42 = 0x43; + u64 big; + u32 tmp; + u32 bdr; + u32 bdri; + u32 symbol_rate = fe_params->symbol_rate; + + dprintk ("%s %i\n", __func__, symbol_rate); + + /* setup the decimation and anti-aliasing filters.. */ + if (symbol_rate < SACLK / 10000 * 137) { + dfn=4; + afs=1; + } else if (symbol_rate < SACLK / 10000 * 208) { + dfn=4; + afs=0; + } else if (symbol_rate < SACLK / 10000 * 270) { + dfn=3; + afs=1; + } else if (symbol_rate < SACLK / 10000 * 416) { + dfn=3; + afs=0; + } else if (symbol_rate < SACLK / 10000 * 550) { + dfn=2; + afs=1; + } else if (symbol_rate < SACLK / 10000 * 833) { + dfn=2; + afs=0; + } else if (symbol_rate < SACLK / 10000 * 1100) { + dfn=1; + afs=1; + } else if (symbol_rate < SACLK / 10000 * 1666) { + dfn=1; + afs=0; + } else if (symbol_rate < SACLK / 10000 * 2200) { + dfn=0; + afs=1; + } else if (symbol_rate < SACLK / 10000 * 3333) { + dfn=0; + afs=0; + } else { + reg37 = 0x63; + reg42 = 0x4f; + byp=1; + } + + /* calculate BDR */ + big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<<dfn); + big += ((SACLK/1000ULL)-1ULL); + do_div(big, (SACLK/1000ULL)); + bdr = big & 0xfffff; + + /* calculate BDRI */ + tmp = (1<<dfn)*(symbol_rate/1000); + bdri = ((32 * (SACLK/1000)) + (tmp-1)) / tmp; + + tda10086_write_byte(state, 0x21, (afs << 7) | dfn); + tda10086_write_mask(state, 0x20, 0x08, byp << 3); + tda10086_write_byte(state, 0x06, bdr); + tda10086_write_byte(state, 0x07, bdr >> 8); + tda10086_write_byte(state, 0x08, bdr >> 16); + tda10086_write_byte(state, 0x09, bdri); + tda10086_write_byte(state, 0x37, reg37); + tda10086_write_byte(state, 0x42, reg42); + + return 0; +} + +static int tda10086_set_fec(struct tda10086_state *state, + struct dtv_frontend_properties *fe_params) +{ + u8 fecval; + + dprintk("%s %i\n", __func__, fe_params->fec_inner); + + switch (fe_params->fec_inner) { + case FEC_1_2: + fecval = 0x00; + break; + case FEC_2_3: + fecval = 0x01; + break; + case FEC_3_4: + fecval = 0x02; + break; + case FEC_4_5: + fecval = 0x03; + break; + case FEC_5_6: + fecval = 0x04; + break; + case FEC_6_7: + fecval = 0x05; + break; + case FEC_7_8: + fecval = 0x06; + break; + case FEC_8_9: + fecval = 0x07; + break; + case FEC_AUTO: + fecval = 0x08; + break; + default: + return -1; + } + tda10086_write_byte(state, 0x0d, fecval); + + return 0; +} + +static int tda10086_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; + struct tda10086_state *state = fe->demodulator_priv; + int ret; + u32 freq = 0; + int freqoff; + + dprintk ("%s\n", __func__); + + /* modify parameters for tuning */ + tda10086_write_byte(state, 0x02, 0x35); + state->has_lock = false; + + /* set params */ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (fe->ops.tuner_ops.get_frequency) + fe->ops.tuner_ops.get_frequency(fe, &freq); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* calculate the frequency offset (in *Hz* not kHz) */ + freqoff = fe_params->frequency - freq; + freqoff = ((1<<16) * freqoff) / (SACLK/1000); + tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f)); + tda10086_write_byte(state, 0x3e, freqoff); + + if ((ret = tda10086_set_inversion(state, fe_params)) < 0) + return ret; + if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0) + return ret; + if ((ret = tda10086_set_fec(state, fe_params)) < 0) + return ret; + + /* soft reset + disable TS output until lock */ + tda10086_write_mask(state, 0x10, 0x40, 0x40); + tda10086_write_mask(state, 0x00, 0x01, 0x00); + + state->symbol_rate = fe_params->symbol_rate; + state->frequency = fe_params->frequency; + return 0; +} + +static int tda10086_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *fe_params) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 val; + int tmp; + u64 tmp64; + + dprintk ("%s\n", __func__); + + /* check for invalid symbol rate */ + if (fe_params->symbol_rate < 500000) + return -EINVAL; + + /* calculate the updated frequency (note: we convert from Hz->kHz) */ + tmp64 = ((u64)tda10086_read_byte(state, 0x52) + | (tda10086_read_byte(state, 0x51) << 8)); + if (tmp64 & 0x8000) + tmp64 |= 0xffffffffffff0000ULL; + tmp64 = (tmp64 * (SACLK/1000ULL)); + do_div(tmp64, (1ULL<<15) * (1ULL<<1)); + fe_params->frequency = (int) state->frequency + (int) tmp64; + + /* the inversion */ + val = tda10086_read_byte(state, 0x0c); + if (val & 0x80) { + switch(val & 0x40) { + case 0x00: + fe_params->inversion = INVERSION_OFF; + if (state->config->invert) + fe_params->inversion = INVERSION_ON; + break; + default: + fe_params->inversion = INVERSION_ON; + if (state->config->invert) + fe_params->inversion = INVERSION_OFF; + break; + } + } else { + tda10086_read_byte(state, 0x0f); + switch(val & 0x02) { + case 0x00: + fe_params->inversion = INVERSION_OFF; + if (state->config->invert) + fe_params->inversion = INVERSION_ON; + break; + default: + fe_params->inversion = INVERSION_ON; + if (state->config->invert) + fe_params->inversion = INVERSION_OFF; + break; + } + } + + /* calculate the updated symbol rate */ + tmp = tda10086_read_byte(state, 0x1d); + if (tmp & 0x80) + tmp |= 0xffffff00; + tmp = (tmp * 480 * (1<<1)) / 128; + tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000); + fe_params->symbol_rate = state->symbol_rate + tmp; + + /* the FEC */ + val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4; + switch(val) { + case 0x00: + fe_params->fec_inner = FEC_1_2; + break; + case 0x01: + fe_params->fec_inner = FEC_2_3; + break; + case 0x02: + fe_params->fec_inner = FEC_3_4; + break; + case 0x03: + fe_params->fec_inner = FEC_4_5; + break; + case 0x04: + fe_params->fec_inner = FEC_5_6; + break; + case 0x05: + fe_params->fec_inner = FEC_6_7; + break; + case 0x06: + fe_params->fec_inner = FEC_7_8; + break; + case 0x07: + fe_params->fec_inner = FEC_8_9; + break; + } + + return 0; +} + +static int tda10086_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 val; + + dprintk ("%s\n", __func__); + + val = tda10086_read_byte(state, 0x0e); + *fe_status = 0; + if (val & 0x01) + *fe_status |= FE_HAS_SIGNAL; + if (val & 0x02) + *fe_status |= FE_HAS_CARRIER; + if (val & 0x04) + *fe_status |= FE_HAS_VITERBI; + if (val & 0x08) + *fe_status |= FE_HAS_SYNC; + if (val & 0x10) { + *fe_status |= FE_HAS_LOCK; + if (!state->has_lock) { + state->has_lock = true; + /* modify parameters for stable reception */ + tda10086_write_byte(state, 0x02, 0x00); + } + } + + return 0; +} + +static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 _str; + + dprintk ("%s\n", __func__); + + _str = 0xff - tda10086_read_byte(state, 0x43); + *signal = (_str << 8) | _str; + + return 0; +} + +static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr) +{ + struct tda10086_state* state = fe->demodulator_priv; + u8 _snr; + + dprintk ("%s\n", __func__); + + _snr = 0xff - tda10086_read_byte(state, 0x1c); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __func__); + + /* read it */ + *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f; + + /* reset counter */ + tda10086_write_byte(state, 0x18, 0x00); + tda10086_write_byte(state, 0x18, 0x80); + + return 0; +} + +static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __func__); + + /* read it */ + *ber = 0; + *ber |= tda10086_read_byte(state, 0x15); + *ber |= tda10086_read_byte(state, 0x16) << 8; + *ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16; + + return 0; +} + +static int tda10086_sleep(struct dvb_frontend* fe) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __func__); + + tda10086_write_mask(state, 0x00, 0x08, 0x08); + + return 0; +} + +static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10086_state* state = fe->demodulator_priv; + + dprintk ("%s\n", __func__); + + if (enable) { + tda10086_write_mask(state, 0x00, 0x10, 0x10); + } else { + tda10086_write_mask(state, 0x00, 0x10, 0x00); + } + + return 0; +} + +static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->symbol_rate > 20000000) { + fesettings->min_delay_ms = 50; + fesettings->step_size = 2000; + fesettings->max_drift = 8000; + } else if (p->symbol_rate > 12000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 1500; + fesettings->max_drift = 9000; + } else if (p->symbol_rate > 8000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 1000; + fesettings->max_drift = 8000; + } else if (p->symbol_rate > 4000000) { + fesettings->min_delay_ms = 100; + fesettings->step_size = 500; + fesettings->max_drift = 7000; + } else if (p->symbol_rate > 2000000) { + fesettings->min_delay_ms = 200; + fesettings->step_size = p->symbol_rate / 8000; + fesettings->max_drift = 14 * fesettings->step_size; + } else { + fesettings->min_delay_ms = 200; + fesettings->step_size = p->symbol_rate / 8000; + fesettings->max_drift = 18 * fesettings->step_size; + } + + return 0; +} + +static void tda10086_release(struct dvb_frontend* fe) +{ + struct tda10086_state *state = fe->demodulator_priv; + tda10086_sleep(fe); + kfree(state); +} + +static const struct dvb_frontend_ops tda10086_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Philips TDA10086 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = tda10086_release, + + .init = tda10086_init, + .sleep = tda10086_sleep, + .i2c_gate_ctrl = tda10086_i2c_gate_ctrl, + + .set_frontend = tda10086_set_frontend, + .get_frontend = tda10086_get_frontend, + .get_tune_settings = tda10086_get_tune_settings, + + .read_status = tda10086_read_status, + .read_ber = tda10086_read_ber, + .read_signal_strength = tda10086_read_signal_strength, + .read_snr = tda10086_read_snr, + .read_ucblocks = tda10086_read_ucblocks, + + .diseqc_send_master_cmd = tda10086_send_master_cmd, + .diseqc_send_burst = tda10086_send_burst, + .set_tone = tda10086_set_tone, +}; + +struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, + struct i2c_adapter* i2c) +{ + struct tda10086_state *state; + + dprintk ("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL); + if (!state) + return NULL; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if (tda10086_read_byte(state, 0x1e) != 0xe1) { + kfree(state); + return NULL; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(tda10086_attach); diff --git a/drivers/media/dvb-frontends/tda10086.h b/drivers/media/dvb-frontends/tda10086.h new file mode 100644 index 0000000000..8776fadbdc --- /dev/null +++ b/drivers/media/dvb-frontends/tda10086.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* + Driver for Philips tda10086 DVBS Frontend + + (c) 2006 Andrew de Quincey + + + */ + +#ifndef TDA10086_H +#define TDA10086_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +enum tda10086_xtal { + TDA10086_XTAL_16M, + TDA10086_XTAL_4M +}; + +struct tda10086_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* does the "inversion" need inverted? */ + u8 invert; + + /* do we need the diseqc signal with carrier? */ + u8 diseqc_tone; + + /* frequency of the reference xtal */ + enum tda10086_xtal xtal_freq; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA10086) +extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_TDA10086 */ + +#endif /* TDA10086_H */ diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c new file mode 100644 index 0000000000..a348344879 --- /dev/null +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -0,0 +1,1235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tda18271c2dd: Driver for the TDA18271C2 tuner + * + * Copyright (C) 2010 Digital Devices GmbH + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "tda18271c2dd.h" + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +struct SStandardParam { + s32 m_IFFrequency; + u32 m_BandWidth; + u8 m_EP3_4_0; + u8 m_EB22; +}; + +struct SMap { + u32 m_Frequency; + u8 m_Param; +}; + +struct SMapI { + u32 m_Frequency; + s32 m_Param; +}; + +struct SMap2 { + u32 m_Frequency; + u8 m_Param1; + u8 m_Param2; +}; + +struct SRFBandMap { + u32 m_RF_max; + u32 m_RF1_Default; + u32 m_RF2_Default; + u32 m_RF3_Default; +}; + +enum ERegister { + ID = 0, + TM, + PL, + EP1, EP2, EP3, EP4, EP5, + CPD, CD1, CD2, CD3, + MPD, MD1, MD2, MD3, + EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10, + EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20, + EB21, EB22, EB23, + NUM_REGS +}; + +struct tda_state { + struct i2c_adapter *i2c; + u8 adr; + + u32 m_Frequency; + u32 IF; + + u8 m_IFLevelAnalog; + u8 m_IFLevelDigital; + u8 m_IFLevelDVBC; + u8 m_IFLevelDVBT; + + u8 m_EP4; + u8 m_EP3_Standby; + + bool m_bMaster; + + s32 m_SettlingTime; + + u8 m_Regs[NUM_REGS]; + + /* Tracking filter settings for band 0..6 */ + u32 m_RF1[7]; + s32 m_RF_A1[7]; + s32 m_RF_B1[7]; + u32 m_RF2[7]; + s32 m_RF_A2[7]; + s32 m_RF_B2[7]; + u32 m_RF3[7]; + + u8 m_TMValue_RFCal; /* Calibration temperature */ + + bool m_bFMInput; /* true to use Pin 8 for FM Radio */ + +}; + +static int PowerScan(struct tda_state *state, + u8 RFBand, u32 RF_in, + u32 *pRF_Out, bool *pbcal); + +static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len) +{ + struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = len} }; + return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +} + +static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, + .buf = data, .len = len}; + + if (i2c_transfer(adap, &msg, 1) != 1) { + printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr); + return -1; + } + return 0; +} + +static int WriteRegs(struct tda_state *state, + u8 SubAddr, u8 *Regs, u16 nRegs) +{ + u8 data[MAX_XFER_SIZE]; + + if (1 + nRegs > sizeof(data)) { + printk(KERN_WARNING + "%s: i2c wr: len=%d is too big!\n", + KBUILD_MODNAME, nRegs); + return -EINVAL; + } + + data[0] = SubAddr; + memcpy(data + 1, Regs, nRegs); + return i2c_write(state->i2c, state->adr, data, nRegs + 1); +} + +static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg) +{ + u8 msg[2] = {SubAddr, Reg}; + + return i2c_write(state->i2c, state->adr, msg, 2); +} + +static int Read(struct tda_state *state, u8 * Regs) +{ + return i2c_readn(state->i2c, state->adr, Regs, 16); +} + +static int ReadExtented(struct tda_state *state, u8 * Regs) +{ + return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS); +} + +static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo) +{ + return WriteRegs(state, RegFrom, + &state->m_Regs[RegFrom], RegTo-RegFrom+1); +} +static int UpdateReg(struct tda_state *state, u8 Reg) +{ + return WriteReg(state, Reg, state->m_Regs[Reg]); +} + +#include "tda18271c2dd_maps.h" + +static void reset(struct tda_state *state) +{ + u32 ulIFLevelAnalog = 0; + u32 ulIFLevelDigital = 2; + u32 ulIFLevelDVBC = 7; + u32 ulIFLevelDVBT = 6; + u32 ulXTOut = 0; + u32 ulStandbyMode = 0x06; /* Send in stdb, but leave osc on */ + u32 ulSlave = 0; + u32 ulFMInput = 0; + u32 ulSettlingTime = 100; + + state->m_Frequency = 0; + state->m_SettlingTime = 100; + state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2; + state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2; + state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2; + state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2; + + state->m_EP4 = 0x20; + if (ulXTOut != 0) + state->m_EP4 |= 0x40; + + state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F; + state->m_bMaster = (ulSlave == 0); + + state->m_SettlingTime = ulSettlingTime; + + state->m_bFMInput = (ulFMInput == 2); +} + +static bool SearchMap1(const struct SMap map[], u32 frequency, u8 *param) +{ + int i = 0; + + while ((map[i].m_Frequency != 0) && (frequency > map[i].m_Frequency)) + i += 1; + if (map[i].m_Frequency == 0) + return false; + *param = map[i].m_Param; + return true; +} + +static bool SearchMap2(const struct SMapI map[], u32 frequency, s32 *param) +{ + int i = 0; + + while ((map[i].m_Frequency != 0) && + (frequency > map[i].m_Frequency)) + i += 1; + if (map[i].m_Frequency == 0) + return false; + *param = map[i].m_Param; + return true; +} + +static bool SearchMap3(const struct SMap2 map[], u32 frequency, u8 *param1, + u8 *param2) +{ + int i = 0; + + while ((map[i].m_Frequency != 0) && + (frequency > map[i].m_Frequency)) + i += 1; + if (map[i].m_Frequency == 0) + return false; + *param1 = map[i].m_Param1; + *param2 = map[i].m_Param2; + return true; +} + +static bool SearchMap4(const struct SRFBandMap map[], u32 frequency, u8 *rfband) +{ + int i = 0; + + while (i < 7 && (frequency > map[i].m_RF_max)) + i += 1; + if (i == 7) + return false; + *rfband = i; + return true; +} + +static int ThermometerRead(struct tda_state *state, u8 *pTM_Value) +{ + int status = 0; + + do { + u8 Regs[16]; + state->m_Regs[TM] |= 0x10; + status = UpdateReg(state, TM); + if (status < 0) + break; + status = Read(state, Regs); + if (status < 0) + break; + if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) || + ((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) { + state->m_Regs[TM] ^= 0x20; + status = UpdateReg(state, TM); + if (status < 0) + break; + msleep(10); + status = Read(state, Regs); + if (status < 0) + break; + } + *pTM_Value = (Regs[TM] & 0x20) + ? m_Thermometer_Map_2[Regs[TM] & 0x0F] + : m_Thermometer_Map_1[Regs[TM] & 0x0F] ; + state->m_Regs[TM] &= ~0x10; /* Thermometer off */ + status = UpdateReg(state, TM); + if (status < 0) + break; + state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 ????????? */ + status = UpdateReg(state, EP4); + if (status < 0) + break; + } while (0); + + return status; +} + +static int StandBy(struct tda_state *state) +{ + int status = 0; + do { + state->m_Regs[EB12] &= ~0x20; /* PD_AGC1_Det = 0 */ + status = UpdateReg(state, EB12); + if (status < 0) + break; + state->m_Regs[EB18] &= ~0x83; /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */ + status = UpdateReg(state, EB18); + if (status < 0) + break; + state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */ + state->m_Regs[EP3] = state->m_EP3_Standby; + status = UpdateReg(state, EP3); + if (status < 0) + break; + state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */ + status = UpdateRegs(state, EB21, EB23); + if (status < 0) + break; + } while (0); + return status; +} + +static int CalcMainPLL(struct tda_state *state, u32 freq) +{ + + u8 PostDiv; + u8 Div; + u64 OscFreq; + u32 MainDiv; + + if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div)) + return -EINVAL; + + OscFreq = (u64) freq * (u64) Div; + OscFreq *= (u64) 16384; + do_div(OscFreq, (u64)16000000); + MainDiv = OscFreq; + + state->m_Regs[MPD] = PostDiv & 0x77; + state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F); + state->m_Regs[MD2] = ((MainDiv >> 8) & 0xFF); + state->m_Regs[MD3] = (MainDiv & 0xFF); + + return UpdateRegs(state, MPD, MD3); +} + +static int CalcCalPLL(struct tda_state *state, u32 freq) +{ + u8 PostDiv; + u8 Div; + u64 OscFreq; + u32 CalDiv; + + if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div)) + return -EINVAL; + + OscFreq = (u64)freq * (u64)Div; + /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ + OscFreq *= (u64)16384; + do_div(OscFreq, (u64)16000000); + CalDiv = OscFreq; + + state->m_Regs[CPD] = PostDiv; + state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF); + state->m_Regs[CD2] = ((CalDiv >> 8) & 0xFF); + state->m_Regs[CD3] = (CalDiv & 0xFF); + + return UpdateRegs(state, CPD, CD3); +} + +static int CalibrateRF(struct tda_state *state, + u8 RFBand, u32 freq, s32 *pCprog) +{ + int status = 0; + u8 Regs[NUM_REGS]; + do { + u8 BP_Filter = 0; + u8 GainTaper = 0; + u8 RFC_K = 0; + u8 RFC_M = 0; + + state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */ + status = UpdateReg(state, EP4); + if (status < 0) + break; + state->m_Regs[EB18] |= 0x03; /* AGC1_Gain = 3 */ + status = UpdateReg(state, EB18); + if (status < 0) + break; + + /* Switching off LT (as datasheet says) causes calibration on C1 to fail */ + /* (Readout of Cprog is always 255) */ + if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */ + state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */ + + if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) && + SearchMap1(m_GainTaper_Map, freq, &GainTaper) && + SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M))) + return -EINVAL; + + state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter; + state->m_Regs[EP2] = (RFBand << 5) | GainTaper; + + state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2); + + status = UpdateRegs(state, EP1, EP3); + if (status < 0) + break; + status = UpdateReg(state, EB13); + if (status < 0) + break; + + state->m_Regs[EB4] |= 0x20; /* LO_ForceSrce = 1 */ + status = UpdateReg(state, EB4); + if (status < 0) + break; + + state->m_Regs[EB7] |= 0x20; /* CAL_ForceSrce = 1 */ + status = UpdateReg(state, EB7); + if (status < 0) + break; + + state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */ + status = UpdateReg(state, EB14); + if (status < 0) + break; + + state->m_Regs[EB20] &= ~0x20; /* ForceLock = 0; */ + status = UpdateReg(state, EB20); + if (status < 0) + break; + + state->m_Regs[EP4] |= 0x03; /* CAL_Mode = 3 */ + status = UpdateRegs(state, EP4, EP5); + if (status < 0) + break; + + status = CalcCalPLL(state, freq); + if (status < 0) + break; + status = CalcMainPLL(state, freq + 1000000); + if (status < 0) + break; + + msleep(5); + status = UpdateReg(state, EP2); + if (status < 0) + break; + status = UpdateReg(state, EP1); + if (status < 0) + break; + status = UpdateReg(state, EP2); + if (status < 0) + break; + status = UpdateReg(state, EP1); + if (status < 0) + break; + + state->m_Regs[EB4] &= ~0x20; /* LO_ForceSrce = 0 */ + status = UpdateReg(state, EB4); + if (status < 0) + break; + + state->m_Regs[EB7] &= ~0x20; /* CAL_ForceSrce = 0 */ + status = UpdateReg(state, EB7); + if (status < 0) + break; + msleep(10); + + state->m_Regs[EB20] |= 0x20; /* ForceLock = 1; */ + status = UpdateReg(state, EB20); + if (status < 0) + break; + msleep(60); + + state->m_Regs[EP4] &= ~0x03; /* CAL_Mode = 0 */ + state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */ + state->m_Regs[EB18] &= ~0x03; /* AGC1_Gain = 0 */ + status = UpdateReg(state, EB18); + if (status < 0) + break; + status = UpdateRegs(state, EP3, EP4); + if (status < 0) + break; + status = UpdateReg(state, EP1); + if (status < 0) + break; + + status = ReadExtented(state, Regs); + if (status < 0) + break; + + *pCprog = Regs[EB14]; + + } while (0); + return status; +} + +static int RFTrackingFiltersInit(struct tda_state *state, + u8 RFBand) +{ + int status = 0; + + u32 RF1 = m_RF_Band_Map[RFBand].m_RF1_Default; + u32 RF2 = m_RF_Band_Map[RFBand].m_RF2_Default; + u32 RF3 = m_RF_Band_Map[RFBand].m_RF3_Default; + bool bcal = false; + + s32 Cprog_cal1 = 0; + s32 Cprog_table1 = 0; + s32 Cprog_cal2 = 0; + s32 Cprog_table2 = 0; + s32 Cprog_cal3 = 0; + s32 Cprog_table3 = 0; + + state->m_RF_A1[RFBand] = 0; + state->m_RF_B1[RFBand] = 0; + state->m_RF_A2[RFBand] = 0; + state->m_RF_B2[RFBand] = 0; + + do { + status = PowerScan(state, RFBand, RF1, &RF1, &bcal); + if (status < 0) + break; + if (bcal) { + status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1); + if (status < 0) + break; + } + SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1); + if (!bcal) + Cprog_cal1 = Cprog_table1; + state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1; + /* state->m_RF_A1[RF_Band] = ???? */ + + if (RF2 == 0) + break; + + status = PowerScan(state, RFBand, RF2, &RF2, &bcal); + if (status < 0) + break; + if (bcal) { + status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2); + if (status < 0) + break; + } + SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2); + if (!bcal) + Cprog_cal2 = Cprog_table2; + + state->m_RF_A1[RFBand] = + (Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) / + ((s32)(RF2) - (s32)(RF1)); + + if (RF3 == 0) + break; + + status = PowerScan(state, RFBand, RF3, &RF3, &bcal); + if (status < 0) + break; + if (bcal) { + status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3); + if (status < 0) + break; + } + SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3); + if (!bcal) + Cprog_cal3 = Cprog_table3; + state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2)); + state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2; + + } while (0); + + state->m_RF1[RFBand] = RF1; + state->m_RF2[RFBand] = RF2; + state->m_RF3[RFBand] = RF3; + +#if 0 + printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__, + RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2, + state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3); +#endif + + return status; +} + +static int PowerScan(struct tda_state *state, + u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal) +{ + int status = 0; + do { + u8 Gain_Taper = 0; + s32 RFC_Cprog = 0; + u8 CID_Target = 0; + u8 CountLimit = 0; + u32 freq_MainPLL; + u8 Regs[NUM_REGS]; + u8 CID_Gain; + s32 Count = 0; + int sign = 1; + bool wait = false; + + if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) && + SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) && + SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) { + + printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__); + return -EINVAL; + } + + state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper; + state->m_Regs[EB14] = (RFC_Cprog); + status = UpdateReg(state, EP2); + if (status < 0) + break; + status = UpdateReg(state, EB14); + if (status < 0) + break; + + freq_MainPLL = RF_in + 1000000; + status = CalcMainPLL(state, freq_MainPLL); + if (status < 0) + break; + msleep(5); + state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1; /* CAL_mode = 1 */ + status = UpdateReg(state, EP4); + if (status < 0) + break; + status = UpdateReg(state, EP2); /* Launch power measurement */ + if (status < 0) + break; + status = ReadExtented(state, Regs); + if (status < 0) + break; + CID_Gain = Regs[EB10] & 0x3F; + state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workaround in CalibrateRF) */ + + *pRF_Out = RF_in; + + while (CID_Gain < CID_Target) { + freq_MainPLL = RF_in + sign * Count + 1000000; + status = CalcMainPLL(state, freq_MainPLL); + if (status < 0) + break; + msleep(wait ? 5 : 1); + wait = false; + status = UpdateReg(state, EP2); /* Launch power measurement */ + if (status < 0) + break; + status = ReadExtented(state, Regs); + if (status < 0) + break; + CID_Gain = Regs[EB10] & 0x3F; + Count += 200000; + + if (Count < CountLimit * 100000) + continue; + if (sign < 0) + break; + + sign = -sign; + Count = 200000; + wait = true; + } + if (status < 0) + break; + if (CID_Gain >= CID_Target) { + *pbcal = true; + *pRF_Out = freq_MainPLL - 1000000; + } else + *pbcal = false; + } while (0); + + return status; +} + +static int PowerScanInit(struct tda_state *state) +{ + int status = 0; + do { + state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12; + state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */ + status = UpdateRegs(state, EP3, EP4); + if (status < 0) + break; + state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */ + status = UpdateReg(state, EB18); + if (status < 0) + break; + state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */ + state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ + status = UpdateRegs(state, EB21, EB23); + if (status < 0) + break; + } while (0); + return status; +} + +static int CalcRFFilterCurve(struct tda_state *state) +{ + int status = 0; + do { + msleep(200); /* Temperature stabilisation */ + status = PowerScanInit(state); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 0); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 1); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 2); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 3); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 4); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 5); + if (status < 0) + break; + status = RFTrackingFiltersInit(state, 6); + if (status < 0) + break; + status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */ + if (status < 0) + break; + } while (0); + + return status; +} + +static int FixedContentsI2CUpdate(struct tda_state *state) +{ + static u8 InitRegs[] = { + 0x08, 0x80, 0xC6, + 0xDF, 0x16, 0x60, 0x80, + 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFC, 0x01, 0x84, 0x41, + 0x01, 0x84, 0x40, 0x07, + 0x00, 0x00, 0x96, 0x3F, + 0xC1, 0x00, 0x8F, 0x00, + 0x00, 0x8C, 0x00, 0x20, + 0xB3, 0x48, 0xB0, + }; + int status = 0; + memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1); + do { + status = UpdateRegs(state, TM, EB23); + if (status < 0) + break; + + /* AGC1 gain setup */ + state->m_Regs[EB17] = 0x00; + status = UpdateReg(state, EB17); + if (status < 0) + break; + state->m_Regs[EB17] = 0x03; + status = UpdateReg(state, EB17); + if (status < 0) + break; + state->m_Regs[EB17] = 0x43; + status = UpdateReg(state, EB17); + if (status < 0) + break; + state->m_Regs[EB17] = 0x4C; + status = UpdateReg(state, EB17); + if (status < 0) + break; + + /* IRC Cal Low band */ + state->m_Regs[EP3] = 0x1F; + state->m_Regs[EP4] = 0x66; + state->m_Regs[EP5] = 0x81; + state->m_Regs[CPD] = 0xCC; + state->m_Regs[CD1] = 0x6C; + state->m_Regs[CD2] = 0x00; + state->m_Regs[CD3] = 0x00; + state->m_Regs[MPD] = 0xC5; + state->m_Regs[MD1] = 0x77; + state->m_Regs[MD2] = 0x08; + state->m_Regs[MD3] = 0x00; + status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */ + if (status < 0) + break; + +#if 0 + state->m_Regs[EB4] = 0x61; /* missing in sw */ + status = UpdateReg(state, EB4); + if (status < 0) + break; + msleep(1); + state->m_Regs[EB4] = 0x41; + status = UpdateReg(state, EB4); + if (status < 0) + break; +#endif + + msleep(5); + status = UpdateReg(state, EP1); + if (status < 0) + break; + msleep(5); + + state->m_Regs[EP5] = 0x85; + state->m_Regs[CPD] = 0xCB; + state->m_Regs[CD1] = 0x66; + state->m_Regs[CD2] = 0x70; + status = UpdateRegs(state, EP3, CD3); + if (status < 0) + break; + msleep(5); + status = UpdateReg(state, EP2); + if (status < 0) + break; + msleep(30); + + /* IRC Cal mid band */ + state->m_Regs[EP5] = 0x82; + state->m_Regs[CPD] = 0xA8; + state->m_Regs[CD2] = 0x00; + state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */ + state->m_Regs[MD1] = 0x73; + state->m_Regs[MD2] = 0x1A; + status = UpdateRegs(state, EP3, MD3); + if (status < 0) + break; + + msleep(5); + status = UpdateReg(state, EP1); + if (status < 0) + break; + msleep(5); + + state->m_Regs[EP5] = 0x86; + state->m_Regs[CPD] = 0xA8; + state->m_Regs[CD1] = 0x66; + state->m_Regs[CD2] = 0xA0; + status = UpdateRegs(state, EP3, CD3); + if (status < 0) + break; + msleep(5); + status = UpdateReg(state, EP2); + if (status < 0) + break; + msleep(30); + + /* IRC Cal high band */ + state->m_Regs[EP5] = 0x83; + state->m_Regs[CPD] = 0x98; + state->m_Regs[CD1] = 0x65; + state->m_Regs[CD2] = 0x00; + state->m_Regs[MPD] = 0x91; /* Datasheet = 0x91 */ + state->m_Regs[MD1] = 0x71; + state->m_Regs[MD2] = 0xCD; + status = UpdateRegs(state, EP3, MD3); + if (status < 0) + break; + msleep(5); + status = UpdateReg(state, EP1); + if (status < 0) + break; + msleep(5); + state->m_Regs[EP5] = 0x87; + state->m_Regs[CD1] = 0x65; + state->m_Regs[CD2] = 0x50; + status = UpdateRegs(state, EP3, CD3); + if (status < 0) + break; + msleep(5); + status = UpdateReg(state, EP2); + if (status < 0) + break; + msleep(30); + + /* Back to normal */ + state->m_Regs[EP4] = 0x64; + status = UpdateReg(state, EP4); + if (status < 0) + break; + status = UpdateReg(state, EP1); + if (status < 0) + break; + + } while (0); + return status; +} + +static int InitCal(struct tda_state *state) +{ + int status = 0; + + do { + status = FixedContentsI2CUpdate(state); + if (status < 0) + break; + status = CalcRFFilterCurve(state); + if (status < 0) + break; + status = StandBy(state); + if (status < 0) + break; + /* m_bInitDone = true; */ + } while (0); + return status; +}; + +static int RFTrackingFiltersCorrection(struct tda_state *state, + u32 Frequency) +{ + int status = 0; + s32 Cprog_table; + u8 RFBand; + u8 dCoverdT; + + if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) || + !SearchMap4(m_RF_Band_Map, Frequency, &RFBand) || + !SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT)) + + return -EINVAL; + + do { + u8 TMValue_Current; + u32 RF1 = state->m_RF1[RFBand]; + u32 RF2 = state->m_RF1[RFBand]; + u32 RF3 = state->m_RF1[RFBand]; + s32 RF_A1 = state->m_RF_A1[RFBand]; + s32 RF_B1 = state->m_RF_B1[RFBand]; + s32 RF_A2 = state->m_RF_A2[RFBand]; + s32 RF_B2 = state->m_RF_B2[RFBand]; + s32 Capprox = 0; + int TComp; + + state->m_Regs[EP3] &= ~0xE0; /* Power up */ + status = UpdateReg(state, EP3); + if (status < 0) + break; + + status = ThermometerRead(state, &TMValue_Current); + if (status < 0) + break; + + if (RF3 == 0 || Frequency < RF2) + Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table; + else + Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table; + + TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000; + + Capprox += TComp; + + if (Capprox < 0) + Capprox = 0; + else if (Capprox > 255) + Capprox = 255; + + + /* TODO Temperature compensation. There is defenitely a scale factor */ + /* missing in the datasheet, so leave it out for now. */ + state->m_Regs[EB14] = Capprox; + + status = UpdateReg(state, EB14); + if (status < 0) + break; + + } while (0); + return status; +} + +static int ChannelConfiguration(struct tda_state *state, + u32 Frequency, int Standard) +{ + + s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency; + int status = 0; + + u8 BP_Filter = 0; + u8 RF_Band = 0; + u8 GainTaper = 0; + u8 IR_Meas = 0; + + state->IF = IntermediateFrequency; + /* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */ + /* get values from tables */ + + if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) && + SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) && + SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) && + SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) { + + printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__); + return -EINVAL; + } + + do { + state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0; + state->m_Regs[EP3] &= ~0x04; /* switch RFAGC to high speed mode */ + + /* m_EP4 default for XToutOn, CAL_Mode (0) */ + state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog); + /* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */ + if (Standard <= HF_AnalogMax) + state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog; + else if (Standard <= HF_ATSC) + state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT; + else if (Standard <= HF_DVBC) + state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC; + else + state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; + + if ((Standard == HF_FM_Radio) && state->m_bFMInput) + state->m_Regs[EP4] |= 0x80; + + state->m_Regs[MPD] &= ~0x80; + if (Standard > HF_AnalogMax) + state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */ + + state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22; + + /* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */ + if (Standard == HF_FM_Radio) + state->m_Regs[EB23] |= 0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ + else + state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */ + + status = UpdateRegs(state, EB22, EB23); + if (status < 0) + break; + + state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter; /* Dis_Power_level = 1, Filter */ + state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas; + state->m_Regs[EP2] = (RF_Band << 5) | GainTaper; + + state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) | + (state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */ + /* AGC1_always_master = 0 */ + /* AGC_firstn = 0 */ + status = UpdateReg(state, EB1); + if (status < 0) + break; + + if (state->m_bMaster) { + status = CalcMainPLL(state, Frequency + IntermediateFrequency); + if (status < 0) + break; + status = UpdateRegs(state, TM, EP5); + if (status < 0) + break; + state->m_Regs[EB4] |= 0x20; /* LO_forceSrce = 1 */ + status = UpdateReg(state, EB4); + if (status < 0) + break; + msleep(1); + state->m_Regs[EB4] &= ~0x20; /* LO_forceSrce = 0 */ + status = UpdateReg(state, EB4); + if (status < 0) + break; + } else { + u8 PostDiv = 0; + u8 Div; + status = CalcCalPLL(state, Frequency + IntermediateFrequency); + if (status < 0) + break; + + SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div); + state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77); + status = UpdateReg(state, MPD); + if (status < 0) + break; + status = UpdateRegs(state, TM, EP5); + if (status < 0) + break; + + state->m_Regs[EB7] |= 0x20; /* CAL_forceSrce = 1 */ + status = UpdateReg(state, EB7); + if (status < 0) + break; + msleep(1); + state->m_Regs[EB7] &= ~0x20; /* CAL_forceSrce = 0 */ + status = UpdateReg(state, EB7); + if (status < 0) + break; + } + msleep(20); + if (Standard != HF_FM_Radio) + state->m_Regs[EP3] |= 0x04; /* RFAGC to normal mode */ + status = UpdateReg(state, EP3); + if (status < 0) + break; + + } while (0); + return status; +} + +static int sleep(struct dvb_frontend *fe) +{ + struct tda_state *state = fe->tuner_priv; + + StandBy(state); + return 0; +} + +static int init(struct dvb_frontend *fe) +{ + return 0; +} + +static void release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + + +static int set_params(struct dvb_frontend *fe) +{ + struct tda_state *state = fe->tuner_priv; + int status = 0; + int Standard; + u32 bw = fe->dtv_property_cache.bandwidth_hz; + u32 delsys = fe->dtv_property_cache.delivery_system; + + state->m_Frequency = fe->dtv_property_cache.frequency; + + switch (delsys) { + case SYS_DVBT: + case SYS_DVBT2: + switch (bw) { + case 6000000: + Standard = HF_DVBT_6MHZ; + break; + case 7000000: + Standard = HF_DVBT_7MHZ; + break; + case 8000000: + Standard = HF_DVBT_8MHZ; + break; + default: + return -EINVAL; + } + break; + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_C: + if (bw <= 6000000) + Standard = HF_DVBC_6MHZ; + else if (bw <= 7000000) + Standard = HF_DVBC_7MHZ; + else + Standard = HF_DVBC_8MHZ; + break; + default: + return -EINVAL; + } + do { + status = RFTrackingFiltersCorrection(state, state->m_Frequency); + if (status < 0) + break; + status = ChannelConfiguration(state, state->m_Frequency, + Standard); + if (status < 0) + break; + + msleep(state->m_SettlingTime); /* Allow AGC's to settle down */ + } while (0); + return status; +} + +#if 0 +static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc) +{ + if (IFAgc < 500) { + /* Scale this from 0 to 50000 */ + *pSignalStrength = IFAgc * 100; + } else { + /* Scale range 500-1500 to 50000-80000 */ + *pSignalStrength = 50000 + (IFAgc - 500) * 30; + } + + return 0; +} +#endif + +static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda_state *state = fe->tuner_priv; + + *frequency = state->IF; + return 0; +} + +static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + /* struct tda_state *state = fe->tuner_priv; */ + /* *bandwidth = priv->bandwidth; */ + return 0; +} + + +static const struct dvb_tuner_ops tuner_ops = { + .info = { + .name = "NXP TDA18271C2D", + .frequency_min_hz = 47125 * kHz, + .frequency_max_hz = 865 * MHz, + .frequency_step_hz = 62500 + }, + .init = init, + .sleep = sleep, + .set_params = set_params, + .release = release, + .get_if_frequency = get_if_frequency, + .get_bandwidth = get_bandwidth, +}; + +struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 adr) +{ + struct tda_state *state; + + state = kzalloc(sizeof(struct tda_state), GFP_KERNEL); + if (!state) + return NULL; + + fe->tuner_priv = state; + state->adr = adr; + state->i2c = i2c; + memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops)); + reset(state); + InitCal(state); + + return fe; +} +EXPORT_SYMBOL_GPL(tda18271c2dd_attach); + +MODULE_DESCRIPTION("TDA18271C2 driver"); +MODULE_AUTHOR("DD"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tda18271c2dd.h b/drivers/media/dvb-frontends/tda18271c2dd.h new file mode 100644 index 0000000000..afeb9536e9 --- /dev/null +++ b/drivers/media/dvb-frontends/tda18271c2dd.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TDA18271C2DD_H_ +#define _TDA18271C2DD_H_ + +#if IS_REACHABLE(CONFIG_DVB_TDA18271C2DD) +struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 adr); +#else +static inline struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 adr) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/tda18271c2dd_maps.h b/drivers/media/dvb-frontends/tda18271c2dd_maps.h new file mode 100644 index 0000000000..82218e02d7 --- /dev/null +++ b/drivers/media/dvb-frontends/tda18271c2dd_maps.h @@ -0,0 +1,815 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +enum HF_S { + HF_None = 0, HF_B, HF_DK, HF_G, HF_I, HF_L, HF_L1, HF_MN, HF_FM_Radio, + HF_AnalogMax, HF_DVBT_6MHZ, HF_DVBT_7MHZ, HF_DVBT_8MHZ, + HF_DVBT, HF_ATSC, HF_DVBC_6MHZ, HF_DVBC_7MHZ, + HF_DVBC_8MHZ, HF_DVBC +}; + +static const struct SStandardParam m_StandardTable[] = { + { 0, 0, 0x00, 0x00 }, /* HF_None */ + { 6000000, 7000000, 0x1D, 0x2C }, /* HF_B, */ + { 6900000, 8000000, 0x1E, 0x2C }, /* HF_DK, */ + { 7100000, 8000000, 0x1E, 0x2C }, /* HF_G, */ + { 7250000, 8000000, 0x1E, 0x2C }, /* HF_I, */ + { 6900000, 8000000, 0x1E, 0x2C }, /* HF_L, */ + { 1250000, 8000000, 0x1E, 0x2C }, /* HF_L1, */ + { 5400000, 6000000, 0x1C, 0x2C }, /* HF_MN, */ + { 1250000, 500000, 0x18, 0x2C }, /* HF_FM_Radio, */ + { 0, 0, 0x00, 0x00 }, /* HF_AnalogMax (Unused) */ + { 3300000, 6000000, 0x1C, 0x58 }, /* HF_DVBT_6MHZ */ + { 3500000, 7000000, 0x1C, 0x37 }, /* HF_DVBT_7MHZ */ + { 4000000, 8000000, 0x1D, 0x37 }, /* HF_DVBT_8MHZ */ + { 0, 0, 0x00, 0x00 }, /* HF_DVBT (Unused) */ + { 5000000, 6000000, 0x1C, 0x37 }, /* HF_ATSC (center = 3.25 MHz) */ + { 4000000, 6000000, 0x1D, 0x58 }, /* HF_DVBC_6MHZ (Chicago) */ + { 4500000, 7000000, 0x1E, 0x37 }, /* HF_DVBC_7MHZ (not documented by NXP) */ + { 5000000, 8000000, 0x1F, 0x37 }, /* HF_DVBC_8MHZ */ + { 0, 0, 0x00, 0x00 }, /* HF_DVBC (Unused) */ +}; + +static const struct SMap m_BP_Filter_Map[] = { + { 62000000, 0x00 }, + { 84000000, 0x01 }, + { 100000000, 0x02 }, + { 140000000, 0x03 }, + { 170000000, 0x04 }, + { 180000000, 0x05 }, + { 865000000, 0x06 }, + { 0, 0x00 }, /* Table End */ +}; + +static const struct SMapI m_RF_Cal_Map[] = { + { 41000000, 0x0F }, + { 43000000, 0x1C }, + { 45000000, 0x2F }, + { 46000000, 0x39 }, + { 47000000, 0x40 }, + { 47900000, 0x50 }, + { 49100000, 0x16 }, + { 50000000, 0x18 }, + { 51000000, 0x20 }, + { 53000000, 0x28 }, + { 55000000, 0x2B }, + { 56000000, 0x32 }, + { 57000000, 0x35 }, + { 58000000, 0x3E }, + { 59000000, 0x43 }, + { 60000000, 0x4E }, + { 61100000, 0x55 }, + { 63000000, 0x0F }, + { 64000000, 0x11 }, + { 65000000, 0x12 }, + { 66000000, 0x15 }, + { 67000000, 0x16 }, + { 68000000, 0x17 }, + { 70000000, 0x19 }, + { 71000000, 0x1C }, + { 72000000, 0x1D }, + { 73000000, 0x1F }, + { 74000000, 0x20 }, + { 75000000, 0x21 }, + { 76000000, 0x24 }, + { 77000000, 0x25 }, + { 78000000, 0x27 }, + { 80000000, 0x28 }, + { 81000000, 0x29 }, + { 82000000, 0x2D }, + { 83000000, 0x2E }, + { 84000000, 0x2F }, + { 85000000, 0x31 }, + { 86000000, 0x33 }, + { 87000000, 0x34 }, + { 88000000, 0x35 }, + { 89000000, 0x37 }, + { 90000000, 0x38 }, + { 91000000, 0x39 }, + { 93000000, 0x3C }, + { 94000000, 0x3E }, + { 95000000, 0x3F }, + { 96000000, 0x40 }, + { 97000000, 0x42 }, + { 99000000, 0x45 }, + { 100000000, 0x46 }, + { 102000000, 0x48 }, + { 103000000, 0x4A }, + { 105000000, 0x4D }, + { 106000000, 0x4E }, + { 107000000, 0x50 }, + { 108000000, 0x51 }, + { 110000000, 0x54 }, + { 111000000, 0x56 }, + { 112000000, 0x57 }, + { 113000000, 0x58 }, + { 114000000, 0x59 }, + { 115000000, 0x5C }, + { 116000000, 0x5D }, + { 117000000, 0x5F }, + { 119000000, 0x60 }, + { 120000000, 0x64 }, + { 121000000, 0x65 }, + { 122000000, 0x66 }, + { 123000000, 0x68 }, + { 124000000, 0x69 }, + { 125000000, 0x6C }, + { 126000000, 0x6D }, + { 127000000, 0x6E }, + { 128000000, 0x70 }, + { 129000000, 0x71 }, + { 130000000, 0x75 }, + { 131000000, 0x77 }, + { 132000000, 0x78 }, + { 133000000, 0x7B }, + { 134000000, 0x7E }, + { 135000000, 0x81 }, + { 136000000, 0x82 }, + { 137000000, 0x87 }, + { 138000000, 0x88 }, + { 139000000, 0x8D }, + { 140000000, 0x8E }, + { 141000000, 0x91 }, + { 142000000, 0x95 }, + { 143000000, 0x9A }, + { 144000000, 0x9D }, + { 145000000, 0xA1 }, + { 146000000, 0xA2 }, + { 147000000, 0xA4 }, + { 148000000, 0xA9 }, + { 149000000, 0xAE }, + { 150000000, 0xB0 }, + { 151000000, 0xB1 }, + { 152000000, 0xB7 }, + { 152600000, 0xBD }, + { 154000000, 0x20 }, + { 155000000, 0x22 }, + { 156000000, 0x24 }, + { 157000000, 0x25 }, + { 158000000, 0x27 }, + { 159000000, 0x29 }, + { 160000000, 0x2C }, + { 161000000, 0x2D }, + { 163000000, 0x2E }, + { 164000000, 0x2F }, + { 164700000, 0x30 }, + { 166000000, 0x11 }, + { 167000000, 0x12 }, + { 168000000, 0x13 }, + { 169000000, 0x14 }, + { 170000000, 0x15 }, + { 172000000, 0x16 }, + { 173000000, 0x17 }, + { 174000000, 0x18 }, + { 175000000, 0x1A }, + { 176000000, 0x1B }, + { 178000000, 0x1D }, + { 179000000, 0x1E }, + { 180000000, 0x1F }, + { 181000000, 0x20 }, + { 182000000, 0x21 }, + { 183000000, 0x22 }, + { 184000000, 0x24 }, + { 185000000, 0x25 }, + { 186000000, 0x26 }, + { 187000000, 0x27 }, + { 188000000, 0x29 }, + { 189000000, 0x2A }, + { 190000000, 0x2C }, + { 191000000, 0x2D }, + { 192000000, 0x2E }, + { 193000000, 0x2F }, + { 194000000, 0x30 }, + { 195000000, 0x33 }, + { 196000000, 0x35 }, + { 198000000, 0x36 }, + { 200000000, 0x38 }, + { 201000000, 0x3C }, + { 202000000, 0x3D }, + { 203500000, 0x3E }, + { 206000000, 0x0E }, + { 208000000, 0x0F }, + { 212000000, 0x10 }, + { 216000000, 0x11 }, + { 217000000, 0x12 }, + { 218000000, 0x13 }, + { 220000000, 0x14 }, + { 222000000, 0x15 }, + { 225000000, 0x16 }, + { 228000000, 0x17 }, + { 231000000, 0x18 }, + { 234000000, 0x19 }, + { 235000000, 0x1A }, + { 236000000, 0x1B }, + { 237000000, 0x1C }, + { 240000000, 0x1D }, + { 242000000, 0x1E }, + { 244000000, 0x1F }, + { 247000000, 0x20 }, + { 249000000, 0x21 }, + { 252000000, 0x22 }, + { 253000000, 0x23 }, + { 254000000, 0x24 }, + { 256000000, 0x25 }, + { 259000000, 0x26 }, + { 262000000, 0x27 }, + { 264000000, 0x28 }, + { 267000000, 0x29 }, + { 269000000, 0x2A }, + { 271000000, 0x2B }, + { 273000000, 0x2C }, + { 275000000, 0x2D }, + { 277000000, 0x2E }, + { 279000000, 0x2F }, + { 282000000, 0x30 }, + { 284000000, 0x31 }, + { 286000000, 0x32 }, + { 287000000, 0x33 }, + { 290000000, 0x34 }, + { 293000000, 0x35 }, + { 295000000, 0x36 }, + { 297000000, 0x37 }, + { 300000000, 0x38 }, + { 303000000, 0x39 }, + { 305000000, 0x3A }, + { 306000000, 0x3B }, + { 307000000, 0x3C }, + { 310000000, 0x3D }, + { 312000000, 0x3E }, + { 315000000, 0x3F }, + { 318000000, 0x40 }, + { 320000000, 0x41 }, + { 323000000, 0x42 }, + { 324000000, 0x43 }, + { 325000000, 0x44 }, + { 327000000, 0x45 }, + { 331000000, 0x46 }, + { 334000000, 0x47 }, + { 337000000, 0x48 }, + { 339000000, 0x49 }, + { 340000000, 0x4A }, + { 341000000, 0x4B }, + { 343000000, 0x4C }, + { 345000000, 0x4D }, + { 349000000, 0x4E }, + { 352000000, 0x4F }, + { 353000000, 0x50 }, + { 355000000, 0x51 }, + { 357000000, 0x52 }, + { 359000000, 0x53 }, + { 361000000, 0x54 }, + { 362000000, 0x55 }, + { 364000000, 0x56 }, + { 368000000, 0x57 }, + { 370000000, 0x58 }, + { 372000000, 0x59 }, + { 375000000, 0x5A }, + { 376000000, 0x5B }, + { 377000000, 0x5C }, + { 379000000, 0x5D }, + { 382000000, 0x5E }, + { 384000000, 0x5F }, + { 385000000, 0x60 }, + { 386000000, 0x61 }, + { 388000000, 0x62 }, + { 390000000, 0x63 }, + { 393000000, 0x64 }, + { 394000000, 0x65 }, + { 396000000, 0x66 }, + { 397000000, 0x67 }, + { 398000000, 0x68 }, + { 400000000, 0x69 }, + { 402000000, 0x6A }, + { 403000000, 0x6B }, + { 407000000, 0x6C }, + { 408000000, 0x6D }, + { 409000000, 0x6E }, + { 410000000, 0x6F }, + { 411000000, 0x70 }, + { 412000000, 0x71 }, + { 413000000, 0x72 }, + { 414000000, 0x73 }, + { 417000000, 0x74 }, + { 418000000, 0x75 }, + { 420000000, 0x76 }, + { 422000000, 0x77 }, + { 423000000, 0x78 }, + { 424000000, 0x79 }, + { 427000000, 0x7A }, + { 428000000, 0x7B }, + { 429000000, 0x7D }, + { 432000000, 0x7F }, + { 434000000, 0x80 }, + { 435000000, 0x81 }, + { 436000000, 0x83 }, + { 437000000, 0x84 }, + { 438000000, 0x85 }, + { 439000000, 0x86 }, + { 440000000, 0x87 }, + { 441000000, 0x88 }, + { 442000000, 0x89 }, + { 445000000, 0x8A }, + { 446000000, 0x8B }, + { 447000000, 0x8C }, + { 448000000, 0x8E }, + { 449000000, 0x8F }, + { 450000000, 0x90 }, + { 452000000, 0x91 }, + { 453000000, 0x93 }, + { 454000000, 0x94 }, + { 456000000, 0x96 }, + { 457800000, 0x98 }, + { 461000000, 0x11 }, + { 468000000, 0x12 }, + { 472000000, 0x13 }, + { 473000000, 0x14 }, + { 474000000, 0x15 }, + { 481000000, 0x16 }, + { 486000000, 0x17 }, + { 491000000, 0x18 }, + { 498000000, 0x19 }, + { 499000000, 0x1A }, + { 501000000, 0x1B }, + { 506000000, 0x1C }, + { 511000000, 0x1D }, + { 516000000, 0x1E }, + { 520000000, 0x1F }, + { 521000000, 0x20 }, + { 525000000, 0x21 }, + { 529000000, 0x22 }, + { 533000000, 0x23 }, + { 539000000, 0x24 }, + { 541000000, 0x25 }, + { 547000000, 0x26 }, + { 549000000, 0x27 }, + { 551000000, 0x28 }, + { 556000000, 0x29 }, + { 561000000, 0x2A }, + { 563000000, 0x2B }, + { 565000000, 0x2C }, + { 569000000, 0x2D }, + { 571000000, 0x2E }, + { 577000000, 0x2F }, + { 580000000, 0x30 }, + { 582000000, 0x31 }, + { 584000000, 0x32 }, + { 588000000, 0x33 }, + { 591000000, 0x34 }, + { 596000000, 0x35 }, + { 598000000, 0x36 }, + { 603000000, 0x37 }, + { 604000000, 0x38 }, + { 606000000, 0x39 }, + { 612000000, 0x3A }, + { 615000000, 0x3B }, + { 617000000, 0x3C }, + { 621000000, 0x3D }, + { 622000000, 0x3E }, + { 625000000, 0x3F }, + { 632000000, 0x40 }, + { 633000000, 0x41 }, + { 634000000, 0x42 }, + { 642000000, 0x43 }, + { 643000000, 0x44 }, + { 647000000, 0x45 }, + { 650000000, 0x46 }, + { 652000000, 0x47 }, + { 657000000, 0x48 }, + { 661000000, 0x49 }, + { 662000000, 0x4A }, + { 665000000, 0x4B }, + { 667000000, 0x4C }, + { 670000000, 0x4D }, + { 673000000, 0x4E }, + { 676000000, 0x4F }, + { 677000000, 0x50 }, + { 681000000, 0x51 }, + { 683000000, 0x52 }, + { 686000000, 0x53 }, + { 688000000, 0x54 }, + { 689000000, 0x55 }, + { 691000000, 0x56 }, + { 695000000, 0x57 }, + { 698000000, 0x58 }, + { 703000000, 0x59 }, + { 704000000, 0x5A }, + { 705000000, 0x5B }, + { 707000000, 0x5C }, + { 710000000, 0x5D }, + { 712000000, 0x5E }, + { 717000000, 0x5F }, + { 718000000, 0x60 }, + { 721000000, 0x61 }, + { 722000000, 0x62 }, + { 723000000, 0x63 }, + { 725000000, 0x64 }, + { 727000000, 0x65 }, + { 730000000, 0x66 }, + { 732000000, 0x67 }, + { 735000000, 0x68 }, + { 740000000, 0x69 }, + { 741000000, 0x6A }, + { 742000000, 0x6B }, + { 743000000, 0x6C }, + { 745000000, 0x6D }, + { 747000000, 0x6E }, + { 748000000, 0x6F }, + { 750000000, 0x70 }, + { 752000000, 0x71 }, + { 754000000, 0x72 }, + { 757000000, 0x73 }, + { 758000000, 0x74 }, + { 760000000, 0x75 }, + { 763000000, 0x76 }, + { 764000000, 0x77 }, + { 766000000, 0x78 }, + { 767000000, 0x79 }, + { 768000000, 0x7A }, + { 773000000, 0x7B }, + { 774000000, 0x7C }, + { 776000000, 0x7D }, + { 777000000, 0x7E }, + { 778000000, 0x7F }, + { 779000000, 0x80 }, + { 781000000, 0x81 }, + { 783000000, 0x82 }, + { 784000000, 0x83 }, + { 785000000, 0x84 }, + { 786000000, 0x85 }, + { 793000000, 0x86 }, + { 794000000, 0x87 }, + { 795000000, 0x88 }, + { 797000000, 0x89 }, + { 799000000, 0x8A }, + { 801000000, 0x8B }, + { 802000000, 0x8C }, + { 803000000, 0x8D }, + { 804000000, 0x8E }, + { 810000000, 0x90 }, + { 811000000, 0x91 }, + { 812000000, 0x92 }, + { 814000000, 0x93 }, + { 816000000, 0x94 }, + { 817000000, 0x96 }, + { 818000000, 0x97 }, + { 820000000, 0x98 }, + { 821000000, 0x99 }, + { 822000000, 0x9A }, + { 828000000, 0x9B }, + { 829000000, 0x9D }, + { 830000000, 0x9F }, + { 831000000, 0xA0 }, + { 833000000, 0xA1 }, + { 835000000, 0xA2 }, + { 836000000, 0xA3 }, + { 837000000, 0xA4 }, + { 838000000, 0xA6 }, + { 840000000, 0xA8 }, + { 842000000, 0xA9 }, + { 845000000, 0xAA }, + { 846000000, 0xAB }, + { 847000000, 0xAD }, + { 848000000, 0xAE }, + { 852000000, 0xAF }, + { 853000000, 0xB0 }, + { 858000000, 0xB1 }, + { 860000000, 0xB2 }, + { 861000000, 0xB3 }, + { 862000000, 0xB4 }, + { 863000000, 0xB6 }, + { 864000000, 0xB8 }, + { 865000000, 0xB9 }, + { 0, 0x00 }, /* Table End */ +}; + + +static const struct SMap2 m_KM_Map[] = { + { 47900000, 3, 2 }, + { 61100000, 3, 1 }, + { 350000000, 3, 0 }, + { 720000000, 2, 1 }, + { 865000000, 3, 3 }, + { 0, 0x00 }, /* Table End */ +}; + +static const struct SMap2 m_Main_PLL_Map[] = { + { 33125000, 0x57, 0xF0 }, + { 35500000, 0x56, 0xE0 }, + { 38188000, 0x55, 0xD0 }, + { 41375000, 0x54, 0xC0 }, + { 45125000, 0x53, 0xB0 }, + { 49688000, 0x52, 0xA0 }, + { 55188000, 0x51, 0x90 }, + { 62125000, 0x50, 0x80 }, + { 66250000, 0x47, 0x78 }, + { 71000000, 0x46, 0x70 }, + { 76375000, 0x45, 0x68 }, + { 82750000, 0x44, 0x60 }, + { 90250000, 0x43, 0x58 }, + { 99375000, 0x42, 0x50 }, + { 110375000, 0x41, 0x48 }, + { 124250000, 0x40, 0x40 }, + { 132500000, 0x37, 0x3C }, + { 142000000, 0x36, 0x38 }, + { 152750000, 0x35, 0x34 }, + { 165500000, 0x34, 0x30 }, + { 180500000, 0x33, 0x2C }, + { 198750000, 0x32, 0x28 }, + { 220750000, 0x31, 0x24 }, + { 248500000, 0x30, 0x20 }, + { 265000000, 0x27, 0x1E }, + { 284000000, 0x26, 0x1C }, + { 305500000, 0x25, 0x1A }, + { 331000000, 0x24, 0x18 }, + { 361000000, 0x23, 0x16 }, + { 397500000, 0x22, 0x14 }, + { 441500000, 0x21, 0x12 }, + { 497000000, 0x20, 0x10 }, + { 530000000, 0x17, 0x0F }, + { 568000000, 0x16, 0x0E }, + { 611000000, 0x15, 0x0D }, + { 662000000, 0x14, 0x0C }, + { 722000000, 0x13, 0x0B }, + { 795000000, 0x12, 0x0A }, + { 883000000, 0x11, 0x09 }, + { 994000000, 0x10, 0x08 }, + { 0, 0x00, 0x00 }, /* Table End */ +}; + +static const struct SMap2 m_Cal_PLL_Map[] = { + { 33813000, 0xDD, 0xD0 }, + { 36625000, 0xDC, 0xC0 }, + { 39938000, 0xDB, 0xB0 }, + { 43938000, 0xDA, 0xA0 }, + { 48813000, 0xD9, 0x90 }, + { 54938000, 0xD8, 0x80 }, + { 62813000, 0xD3, 0x70 }, + { 67625000, 0xCD, 0x68 }, + { 73250000, 0xCC, 0x60 }, + { 79875000, 0xCB, 0x58 }, + { 87875000, 0xCA, 0x50 }, + { 97625000, 0xC9, 0x48 }, + { 109875000, 0xC8, 0x40 }, + { 125625000, 0xC3, 0x38 }, + { 135250000, 0xBD, 0x34 }, + { 146500000, 0xBC, 0x30 }, + { 159750000, 0xBB, 0x2C }, + { 175750000, 0xBA, 0x28 }, + { 195250000, 0xB9, 0x24 }, + { 219750000, 0xB8, 0x20 }, + { 251250000, 0xB3, 0x1C }, + { 270500000, 0xAD, 0x1A }, + { 293000000, 0xAC, 0x18 }, + { 319500000, 0xAB, 0x16 }, + { 351500000, 0xAA, 0x14 }, + { 390500000, 0xA9, 0x12 }, + { 439500000, 0xA8, 0x10 }, + { 502500000, 0xA3, 0x0E }, + { 541000000, 0x9D, 0x0D }, + { 586000000, 0x9C, 0x0C }, + { 639000000, 0x9B, 0x0B }, + { 703000000, 0x9A, 0x0A }, + { 781000000, 0x99, 0x09 }, + { 879000000, 0x98, 0x08 }, + { 0, 0x00, 0x00 }, /* Table End */ +}; + +static const struct SMap m_GainTaper_Map[] = { + { 45400000, 0x1F }, + { 45800000, 0x1E }, + { 46200000, 0x1D }, + { 46700000, 0x1C }, + { 47100000, 0x1B }, + { 47500000, 0x1A }, + { 47900000, 0x19 }, + { 49600000, 0x17 }, + { 51200000, 0x16 }, + { 52900000, 0x15 }, + { 54500000, 0x14 }, + { 56200000, 0x13 }, + { 57800000, 0x12 }, + { 59500000, 0x11 }, + { 61100000, 0x10 }, + { 67600000, 0x0D }, + { 74200000, 0x0C }, + { 80700000, 0x0B }, + { 87200000, 0x0A }, + { 93800000, 0x09 }, + { 100300000, 0x08 }, + { 106900000, 0x07 }, + { 113400000, 0x06 }, + { 119900000, 0x05 }, + { 126500000, 0x04 }, + { 133000000, 0x03 }, + { 139500000, 0x02 }, + { 146100000, 0x01 }, + { 152600000, 0x00 }, + { 154300000, 0x1F }, + { 156100000, 0x1E }, + { 157800000, 0x1D }, + { 159500000, 0x1C }, + { 161200000, 0x1B }, + { 163000000, 0x1A }, + { 164700000, 0x19 }, + { 170200000, 0x17 }, + { 175800000, 0x16 }, + { 181300000, 0x15 }, + { 186900000, 0x14 }, + { 192400000, 0x13 }, + { 198000000, 0x12 }, + { 203500000, 0x11 }, + { 216200000, 0x14 }, + { 228900000, 0x13 }, + { 241600000, 0x12 }, + { 254400000, 0x11 }, + { 267100000, 0x10 }, + { 279800000, 0x0F }, + { 292500000, 0x0E }, + { 305200000, 0x0D }, + { 317900000, 0x0C }, + { 330700000, 0x0B }, + { 343400000, 0x0A }, + { 356100000, 0x09 }, + { 368800000, 0x08 }, + { 381500000, 0x07 }, + { 394200000, 0x06 }, + { 406900000, 0x05 }, + { 419700000, 0x04 }, + { 432400000, 0x03 }, + { 445100000, 0x02 }, + { 457800000, 0x01 }, + { 476300000, 0x19 }, + { 494800000, 0x18 }, + { 513300000, 0x17 }, + { 531800000, 0x16 }, + { 550300000, 0x15 }, + { 568900000, 0x14 }, + { 587400000, 0x13 }, + { 605900000, 0x12 }, + { 624400000, 0x11 }, + { 642900000, 0x10 }, + { 661400000, 0x0F }, + { 679900000, 0x0E }, + { 698400000, 0x0D }, + { 716900000, 0x0C }, + { 735400000, 0x0B }, + { 753900000, 0x0A }, + { 772500000, 0x09 }, + { 791000000, 0x08 }, + { 809500000, 0x07 }, + { 828000000, 0x06 }, + { 846500000, 0x05 }, + { 865000000, 0x04 }, + { 0, 0x00 }, /* Table End */ +}; + +static const struct SMap m_RF_Cal_DC_Over_DT_Map[] = { + { 47900000, 0x00 }, + { 55000000, 0x00 }, + { 61100000, 0x0A }, + { 64000000, 0x0A }, + { 82000000, 0x14 }, + { 84000000, 0x19 }, + { 119000000, 0x1C }, + { 124000000, 0x20 }, + { 129000000, 0x2A }, + { 134000000, 0x32 }, + { 139000000, 0x39 }, + { 144000000, 0x3E }, + { 149000000, 0x3F }, + { 152600000, 0x40 }, + { 154000000, 0x40 }, + { 164700000, 0x41 }, + { 203500000, 0x32 }, + { 353000000, 0x19 }, + { 356000000, 0x1A }, + { 359000000, 0x1B }, + { 363000000, 0x1C }, + { 366000000, 0x1D }, + { 369000000, 0x1E }, + { 373000000, 0x1F }, + { 376000000, 0x20 }, + { 379000000, 0x21 }, + { 383000000, 0x22 }, + { 386000000, 0x23 }, + { 389000000, 0x24 }, + { 393000000, 0x25 }, + { 396000000, 0x26 }, + { 399000000, 0x27 }, + { 402000000, 0x28 }, + { 404000000, 0x29 }, + { 407000000, 0x2A }, + { 409000000, 0x2B }, + { 412000000, 0x2C }, + { 414000000, 0x2D }, + { 417000000, 0x2E }, + { 419000000, 0x2F }, + { 422000000, 0x30 }, + { 424000000, 0x31 }, + { 427000000, 0x32 }, + { 429000000, 0x33 }, + { 432000000, 0x34 }, + { 434000000, 0x35 }, + { 437000000, 0x36 }, + { 439000000, 0x37 }, + { 442000000, 0x38 }, + { 444000000, 0x39 }, + { 447000000, 0x3A }, + { 449000000, 0x3B }, + { 457800000, 0x3C }, + { 465000000, 0x0F }, + { 477000000, 0x12 }, + { 483000000, 0x14 }, + { 502000000, 0x19 }, + { 508000000, 0x1B }, + { 519000000, 0x1C }, + { 522000000, 0x1D }, + { 524000000, 0x1E }, + { 534000000, 0x1F }, + { 549000000, 0x20 }, + { 554000000, 0x22 }, + { 584000000, 0x24 }, + { 589000000, 0x26 }, + { 658000000, 0x27 }, + { 664000000, 0x2C }, + { 669000000, 0x2D }, + { 699000000, 0x2E }, + { 704000000, 0x30 }, + { 709000000, 0x31 }, + { 714000000, 0x32 }, + { 724000000, 0x33 }, + { 729000000, 0x36 }, + { 739000000, 0x38 }, + { 744000000, 0x39 }, + { 749000000, 0x3B }, + { 754000000, 0x3C }, + { 759000000, 0x3D }, + { 764000000, 0x3E }, + { 769000000, 0x3F }, + { 774000000, 0x40 }, + { 779000000, 0x41 }, + { 784000000, 0x43 }, + { 789000000, 0x46 }, + { 794000000, 0x48 }, + { 799000000, 0x4B }, + { 804000000, 0x4F }, + { 809000000, 0x54 }, + { 814000000, 0x59 }, + { 819000000, 0x5D }, + { 824000000, 0x61 }, + { 829000000, 0x68 }, + { 834000000, 0x6E }, + { 839000000, 0x75 }, + { 844000000, 0x7E }, + { 849000000, 0x82 }, + { 854000000, 0x84 }, + { 859000000, 0x8F }, + { 865000000, 0x9A }, + { 0, 0x00 }, /* Table End */ +}; + + +static const struct SMap m_IR_Meas_Map[] = { + { 200000000, 0x05 }, + { 400000000, 0x06 }, + { 865000000, 0x07 }, + { 0, 0x00 }, /* Table End */ +}; + +static const struct SMap2 m_CID_Target_Map[] = { + { 46000000, 0x04, 18 }, + { 52200000, 0x0A, 15 }, + { 70100000, 0x01, 40 }, + { 136800000, 0x18, 40 }, + { 156700000, 0x18, 40 }, + { 186250000, 0x0A, 40 }, + { 230000000, 0x0A, 40 }, + { 345000000, 0x18, 40 }, + { 426000000, 0x0E, 40 }, + { 489500000, 0x1E, 40 }, + { 697500000, 0x32, 40 }, + { 842000000, 0x3A, 40 }, + { 0, 0x00, 0 }, /* Table End */ +}; + +static const struct SRFBandMap m_RF_Band_Map[7] = { + { 47900000, 46000000, 0, 0}, + { 61100000, 52200000, 0, 0}, + { 152600000, 70100000, 136800000, 0}, + { 164700000, 156700000, 0, 0}, + { 203500000, 186250000, 0, 0}, + { 457800000, 230000000, 345000000, 426000000}, + { 865000000, 489500000, 697500000, 842000000}, +}; + +static u8 m_Thermometer_Map_1[16] = { + 60, 62, 66, 64, + 74, 72, 68, 70, + 90, 88, 84, 86, + 76, 78, 82, 80, +}; + +static u8 m_Thermometer_Map_2[16] = { + 92, 94, 98, 96, + 106, 104, 100, 102, + 122, 120, 116, 118, + 108, 110, 114, 112, +}; diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c new file mode 100644 index 0000000000..346be5011f --- /dev/null +++ b/drivers/media/dvb-frontends/tda665x.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + TDA665x tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "tda665x.h" + +struct tda665x_state { + struct dvb_frontend *fe; + struct i2c_adapter *i2c; + const struct tda665x_config *config; + + u32 frequency; + u32 bandwidth; +}; + +static int tda665x_read(struct tda665x_state *state, u8 *buf) +{ + const struct tda665x_config *config = state->config; + int err = 0; + struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD, .buf = buf, .len = 2 }; + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) + goto exit; + + return err; +exit: + printk(KERN_ERR "%s: I/O Error err=<%d>\n", __func__, err); + return err; +} + +static int tda665x_write(struct tda665x_state *state, u8 *buf, u8 length) +{ + const struct tda665x_config *config = state->config; + int err = 0; + struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = length }; + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) + goto exit; + + return err; +exit: + printk(KERN_ERR "%s: I/O Error err=<%d>\n", __func__, err); + return err; +} + +static int tda665x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda665x_state *state = fe->tuner_priv; + + *frequency = state->frequency; + + return 0; +} + +static int tda665x_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct tda665x_state *state = fe->tuner_priv; + u8 result = 0; + int err = 0; + + *status = 0; + + err = tda665x_read(state, &result); + if (err < 0) + goto exit; + + if ((result >> 6) & 0x01) { + printk(KERN_DEBUG "%s: Tuner Phase Locked\n", __func__); + *status = 1; + } + + return err; +exit: + printk(KERN_ERR "%s: I/O Error\n", __func__); + return err; +} + +static int tda665x_set_frequency(struct dvb_frontend *fe, + u32 new_frequency) +{ + struct tda665x_state *state = fe->tuner_priv; + const struct tda665x_config *config = state->config; + u32 frequency, status = 0; + u8 buf[4]; + int err = 0; + + if ((new_frequency < config->frequency_max) + || (new_frequency > config->frequency_min)) { + printk(KERN_ERR "%s: Frequency beyond limits, frequency=%d\n", + __func__, new_frequency); + return -EINVAL; + } + + frequency = new_frequency; + + frequency += config->frequency_offst; + frequency *= config->ref_multiplier; + frequency += config->ref_divider >> 1; + frequency /= config->ref_divider; + + buf[0] = (u8) ((frequency & 0x7f00) >> 8); + buf[1] = (u8) (frequency & 0x00ff) >> 0; + buf[2] = 0x80 | 0x40 | 0x02; + buf[3] = 0x00; + + /* restore frequency */ + frequency = new_frequency; + + if (frequency < 153000000) { + /* VHF-L */ + buf[3] |= 0x01; /* fc, Low Band, 47 - 153 MHz */ + if (frequency < 68000000) + buf[3] |= 0x40; /* 83uA */ + if (frequency < 1040000000) + buf[3] |= 0x60; /* 122uA */ + if (frequency < 1250000000) + buf[3] |= 0x80; /* 163uA */ + else + buf[3] |= 0xa0; /* 254uA */ + } else if (frequency < 438000000) { + /* VHF-H */ + buf[3] |= 0x02; /* fc, Mid Band, 153 - 438 MHz */ + if (frequency < 230000000) + buf[3] |= 0x40; + if (frequency < 300000000) + buf[3] |= 0x60; + else + buf[3] |= 0x80; + } else { + /* UHF */ + buf[3] |= 0x04; /* fc, High Band, 438 - 862 MHz */ + if (frequency < 470000000) + buf[3] |= 0x60; + if (frequency < 526000000) + buf[3] |= 0x80; + else + buf[3] |= 0xa0; + } + + /* Set params */ + err = tda665x_write(state, buf, 5); + if (err < 0) + goto exit; + + /* sleep for some time */ + printk(KERN_DEBUG "%s: Waiting to Phase LOCK\n", __func__); + msleep(20); + /* check status */ + err = tda665x_get_status(fe, &status); + if (err < 0) + goto exit; + + if (status == 1) { + printk(KERN_DEBUG "%s: Tuner Phase locked: status=%d\n", + __func__, status); + state->frequency = frequency; /* cache successful state */ + } else { + printk(KERN_ERR "%s: No Phase lock: status=%d\n", + __func__, status); + } + + return 0; +exit: + printk(KERN_ERR "%s: I/O Error\n", __func__); + return err; +} + +static int tda665x_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + tda665x_set_frequency(fe, c->frequency); + + return 0; +} + +static void tda665x_release(struct dvb_frontend *fe) +{ + struct tda665x_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); +} + +static const struct dvb_tuner_ops tda665x_ops = { + .get_status = tda665x_get_status, + .set_params = tda665x_set_params, + .get_frequency = tda665x_get_frequency, + .release = tda665x_release +}; + +struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, + const struct tda665x_config *config, + struct i2c_adapter *i2c) +{ + struct tda665x_state *state = NULL; + struct dvb_tuner_info *info; + + state = kzalloc(sizeof(struct tda665x_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->i2c = i2c; + state->fe = fe; + fe->tuner_priv = state; + fe->ops.tuner_ops = tda665x_ops; + info = &fe->ops.tuner_ops.info; + + memcpy(info->name, config->name, sizeof(config->name)); + info->frequency_min_hz = config->frequency_min; + info->frequency_max_hz = config->frequency_max; + info->frequency_step_hz = config->frequency_offst; + + printk(KERN_DEBUG "%s: Attaching TDA665x (%s) tuner\n", __func__, info->name); + + return fe; +} +EXPORT_SYMBOL_GPL(tda665x_attach); + +MODULE_DESCRIPTION("TDA665x driver"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tda665x.h b/drivers/media/dvb-frontends/tda665x.h new file mode 100644 index 0000000000..b75096c7ae --- /dev/null +++ b/drivers/media/dvb-frontends/tda665x.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + TDA665x tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +#ifndef __TDA665x_H +#define __TDA665x_H + +struct tda665x_config { + char name[128]; + + u8 addr; + u32 frequency_min; + u32 frequency_max; + u32 frequency_offst; + u32 ref_multiplier; + u32 ref_divider; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA665x) + +extern struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, + const struct tda665x_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, + const struct tda665x_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif /* CONFIG_DVB_TDA665x */ + +#endif /* __TDA665x_H */ diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c new file mode 100644 index 0000000000..44f5362455 --- /dev/null +++ b/drivers/media/dvb-frontends/tda8083.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for Philips TDA8083 based QPSK Demodulator + + Copyright (C) 2001 Convergence Integrated Media GmbH + + written by Ralph Metzler <ralph@convergence.de> + + adoption to the new DVB frontend API and diagnostic ioctl's + by Holger Waechtler <holger@convergence.de> + + +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <media/dvb_frontend.h> +#include "tda8083.h" + + +struct tda8083_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct tda8083_config* config; + struct dvb_frontend frontend; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda8083: " args); \ + } while (0) + + +static u8 tda8083_init_tab [] = { + 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, + 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, + 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, + 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, + 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + + +static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk ("%s: writereg error (reg %02x, ret == %i)\n", + __func__, reg, ret); + + return (ret != 1) ? -1 : 0; +} + +static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk ("%s: readreg error (reg %02x, ret == %i)\n", + __func__, reg1, ret); + + return ret == 2 ? 0 : -1; +} + +static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) +{ + u8 val; + + tda8083_readregs (state, reg, &val, 1); + + return val; +} + +static int tda8083_set_inversion(struct tda8083_state *state, + enum fe_spectral_inversion inversion) +{ + /* XXX FIXME: implement other modes than FEC_AUTO */ + if (inversion == INVERSION_AUTO) + return 0; + + return -EINVAL; +} + +static int tda8083_set_fec(struct tda8083_state *state, enum fe_code_rate fec) +{ + if (fec == FEC_AUTO) + return tda8083_writereg (state, 0x07, 0xff); + + if (fec >= FEC_1_2 && fec <= FEC_8_9) + return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec)); + + return -EINVAL; +} + +static enum fe_code_rate tda8083_get_fec(struct tda8083_state *state) +{ + u8 index; + static enum fe_code_rate fec_tab[] = { + FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 + }; + + index = tda8083_readreg(state, 0x0e) & 0x07; + + return fec_tab [index]; +} + +static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate) +{ + u32 ratio; + u32 tmp; + u8 filter; + + if (srate > 32000000) + srate = 32000000; + if (srate < 500000) + srate = 500000; + + filter = 0; + if (srate < 24000000) + filter = 2; + if (srate < 16000000) + filter = 3; + + tmp = 31250 << 16; + ratio = tmp / srate; + + tmp = (tmp % srate) << 8; + ratio = (ratio << 8) + tmp / srate; + + tmp = (tmp % srate) << 8; + ratio = (ratio << 8) + tmp / srate; + + dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio); + + tda8083_writereg (state, 0x05, filter); + tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff); + tda8083_writereg (state, 0x03, (ratio >> 8) & 0xff); + tda8083_writereg (state, 0x04, (ratio ) & 0xff); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 1; +} + +static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) +{ + unsigned long start = jiffies; + + while (time_is_after_jiffies(start + timeout) && + !(tda8083_readreg(state, 0x02) & 0x80)) + { + msleep(50); + } +} + +static int tda8083_set_tone(struct tda8083_state *state, + enum fe_sec_tone_mode tone) +{ + tda8083_writereg (state, 0x26, 0xf1); + + switch (tone) { + case SEC_TONE_OFF: + return tda8083_writereg (state, 0x29, 0x00); + case SEC_TONE_ON: + return tda8083_writereg (state, 0x29, 0x80); + default: + return -EINVAL; + } +} + +static int tda8083_set_voltage(struct tda8083_state *state, + enum fe_sec_voltage voltage) +{ + switch (voltage) { + case SEC_VOLTAGE_13: + return tda8083_writereg (state, 0x20, 0x00); + case SEC_VOLTAGE_18: + return tda8083_writereg (state, 0x20, 0x11); + default: + return -EINVAL; + } +} + +static int tda8083_send_diseqc_burst(struct tda8083_state *state, + enum fe_sec_mini_cmd burst) +{ + switch (burst) { + case SEC_MINI_A: + tda8083_writereg (state, 0x29, (5 << 2)); /* send burst A */ + break; + case SEC_MINI_B: + tda8083_writereg (state, 0x29, (7 << 2)); /* send B */ + break; + default: + return -EINVAL; + } + + tda8083_wait_diseqc_fifo (state, 100); + + return 0; +} + +static int tda8083_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct tda8083_state* state = fe->demodulator_priv; + int i; + + tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */ + + for (i=0; i<m->msg_len; i++) + tda8083_writereg (state, 0x23 + i, m->msg[i]); + + tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */ + + tda8083_wait_diseqc_fifo (state, 100); + + return 0; +} + +static int tda8083_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct tda8083_state* state = fe->demodulator_priv; + + u8 signal = ~tda8083_readreg (state, 0x01); + u8 sync = tda8083_readreg (state, 0x02); + + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x01) + *status |= FE_HAS_CARRIER; + + if (sync & 0x02) + *status |= FE_HAS_VITERBI; + + if (sync & 0x10) + *status |= FE_HAS_SYNC; + + if (sync & 0x20) /* frontend can not lock */ + *status |= FE_TIMEDOUT; + + if ((sync & 0x1f) == 0x1f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda8083_state* state = fe->demodulator_priv; + int ret; + u8 buf[3]; + + if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf)))) + return ret; + + *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda8083_state* state = fe->demodulator_priv; + + u8 signal = ~tda8083_readreg (state, 0x01); + *strength = (signal << 8) | signal; + + return 0; +} + +static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda8083_state* state = fe->demodulator_priv; + + u8 _snr = tda8083_readreg (state, 0x08); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda8083_state* state = fe->demodulator_priv; + + *ucblocks = tda8083_readreg(state, 0x0f); + if (*ucblocks == 0xff) + *ucblocks = 0xffffffff; + + return 0; +} + +static int tda8083_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct tda8083_state* state = fe->demodulator_priv; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + tda8083_set_inversion (state, p->inversion); + tda8083_set_fec(state, p->fec_inner); + tda8083_set_symbolrate(state, p->symbol_rate); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct tda8083_state* state = fe->demodulator_priv; + + /* FIXME: get symbolrate & frequency offset...*/ + /*p->frequency = ???;*/ + p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ? + INVERSION_ON : INVERSION_OFF; + p->fec_inner = tda8083_get_fec(state); + /*p->symbol_rate = tda8083_get_symbolrate (state);*/ + + return 0; +} + +static int tda8083_sleep(struct dvb_frontend* fe) +{ + struct tda8083_state* state = fe->demodulator_priv; + + tda8083_writereg (state, 0x00, 0x02); + return 0; +} + +static int tda8083_init(struct dvb_frontend* fe) +{ + struct tda8083_state* state = fe->demodulator_priv; + int i; + + for (i=0; i<44; i++) + tda8083_writereg (state, i, tda8083_init_tab[i]); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct tda8083_state* state = fe->demodulator_priv; + + tda8083_send_diseqc_burst (state, burst); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct tda8083_state* state = fe->demodulator_priv; + + tda8083_set_tone (state, tone); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct tda8083_state* state = fe->demodulator_priv; + + tda8083_set_voltage (state, voltage); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static void tda8083_release(struct dvb_frontend* fe) +{ + struct tda8083_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops tda8083_ops; + +struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c) +{ + struct tda8083_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is there */ + if ((tda8083_readreg(state, 0x00)) != 0x05) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda8083_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops tda8083_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Philips TDA8083 DVB-S", + .frequency_min_hz = 920 * MHz, /* TDA8060 */ + .frequency_max_hz = 2200 * MHz, /* TDA8060 */ + .frequency_stepsize_hz = 125 * kHz, + .symbol_rate_min = 12000000, + .symbol_rate_max = 30000000, + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_MUTE_TS + }, + + .release = tda8083_release, + + .init = tda8083_init, + .sleep = tda8083_sleep, + + .set_frontend = tda8083_set_frontend, + .get_frontend = tda8083_get_frontend, + + .read_status = tda8083_read_status, + .read_signal_strength = tda8083_read_signal_strength, + .read_snr = tda8083_read_snr, + .read_ber = tda8083_read_ber, + .read_ucblocks = tda8083_read_ucblocks, + + .diseqc_send_master_cmd = tda8083_send_diseqc_msg, + .diseqc_send_burst = tda8083_diseqc_send_burst, + .set_tone = tda8083_diseqc_set_tone, + .set_voltage = tda8083_diseqc_set_voltage, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(tda8083_attach); diff --git a/drivers/media/dvb-frontends/tda8083.h b/drivers/media/dvb-frontends/tda8083.h new file mode 100644 index 0000000000..3a671ec3f4 --- /dev/null +++ b/drivers/media/dvb-frontends/tda8083.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend + + Copyright (C) 2001 Convergence Integrated Media GmbH + + written by Ralph Metzler <ralph@convergence.de> + + adoption to the new DVB frontend API and diagnostic ioctl's + by Holger Waechtler <holger@convergence.de> + + +*/ + +#ifndef TDA8083_H +#define TDA8083_H + +#include <linux/dvb/frontend.h> + +struct tda8083_config +{ + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA8083) +extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_TDA8083 + +#endif // TDA8083_H diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c new file mode 100644 index 0000000000..8b06f92745 --- /dev/null +++ b/drivers/media/dvb-frontends/tda8261.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + TDA8261 8PSK/QPSK tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "tda8261.h" + +struct tda8261_state { + struct dvb_frontend *fe; + struct i2c_adapter *i2c; + const struct tda8261_config *config; + + /* state cache */ + u32 frequency; + u32 bandwidth; +}; + +static int tda8261_read(struct tda8261_state *state, u8 *buf) +{ + const struct tda8261_config *config = state->config; + int err = 0; + struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 }; + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) + pr_err("%s: read error, err=%d\n", __func__, err); + + return err; +} + +static int tda8261_write(struct tda8261_state *state, u8 *buf) +{ + const struct tda8261_config *config = state->config; + int err = 0; + struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 }; + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) + pr_err("%s: write error, err=%d\n", __func__, err); + + return err; +} + +static int tda8261_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct tda8261_state *state = fe->tuner_priv; + u8 result = 0; + int err = 0; + + *status = 0; + + if ((err = tda8261_read(state, &result)) < 0) { + pr_err("%s: I/O Error\n", __func__); + return err; + } + if ((result >> 6) & 0x01) { + pr_debug("%s: Tuner Phase Locked\n", __func__); + *status = 1; + } + + return err; +} + +static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */ +static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 }; + +static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda8261_state *state = fe->tuner_priv; + + *frequency = state->frequency; + + return 0; +} + +static int tda8261_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda8261_state *state = fe->tuner_priv; + const struct tda8261_config *config = state->config; + u32 frequency, N, status = 0; + u8 buf[4]; + int err = 0; + + /* + * N = Max VCO Frequency / Channel Spacing + * Max VCO Frequency = VCO frequency + (channel spacing - 1) + * (to account for half channel spacing on either side) + */ + frequency = c->frequency; + if ((frequency < 950000) || (frequency > 2150000)) { + pr_warn("%s: Frequency beyond limits, frequency=%d\n", + __func__, frequency); + return -EINVAL; + } + N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size]; + pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n", + __func__, config->step_size, div_tab[config->step_size], N, N); + + buf[0] = (N >> 8) & 0xff; + buf[1] = N & 0xff; + buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1); + + if (frequency < 1450000) + buf[3] = 0x00; + else if (frequency < 2000000) + buf[3] = 0x40; + else if (frequency < 2150000) + buf[3] = 0x80; + + /* Set params */ + err = tda8261_write(state, buf); + if (err < 0) { + pr_err("%s: I/O Error\n", __func__); + return err; + } + /* sleep for some time */ + pr_debug("%s: Waiting to Phase LOCK\n", __func__); + msleep(20); + /* check status */ + if ((err = tda8261_get_status(fe, &status)) < 0) { + pr_err("%s: I/O Error\n", __func__); + return err; + } + if (status == 1) { + pr_debug("%s: Tuner Phase locked: status=%d\n", __func__, + status); + state->frequency = frequency; /* cache successful state */ + } else { + pr_debug("%s: No Phase lock: status=%d\n", __func__, status); + } + + return 0; +} + +static void tda8261_release(struct dvb_frontend *fe) +{ + struct tda8261_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); +} + +static const struct dvb_tuner_ops tda8261_ops = { + + .info = { + .name = "TDA8261", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + }, + + .set_params = tda8261_set_params, + .get_frequency = tda8261_get_frequency, + .get_status = tda8261_get_status, + .release = tda8261_release +}; + +struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, + const struct tda8261_config *config, + struct i2c_adapter *i2c) +{ + struct tda8261_state *state = NULL; + + if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL) + goto exit; + + state->config = config; + state->i2c = i2c; + state->fe = fe; + fe->tuner_priv = state; + fe->ops.tuner_ops = tda8261_ops; + + fe->ops.tuner_ops.info.frequency_step_hz = div_tab[config->step_size] * kHz; + + pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); + + return fe; + +exit: + kfree(state); + return NULL; +} + +EXPORT_SYMBOL_GPL(tda8261_attach); + +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tda8261.h b/drivers/media/dvb-frontends/tda8261.h new file mode 100644 index 0000000000..d45a102a74 --- /dev/null +++ b/drivers/media/dvb-frontends/tda8261.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + TDA8261 8PSK/QPSK tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +#ifndef __TDA8261_H +#define __TDA8261_H + +enum tda8261_step { + TDA8261_STEP_2000 = 0, /* 2000 kHz */ + TDA8261_STEP_1000, /* 1000 kHz */ + TDA8261_STEP_500, /* 500 kHz */ + TDA8261_STEP_250, /* 250 kHz */ + TDA8261_STEP_125 /* 125 kHz */ +}; + +struct tda8261_config { +// u8 buf[16]; + u8 addr; + enum tda8261_step step_size; +}; + +#if IS_REACHABLE(CONFIG_DVB_TDA8261) + +extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, + const struct tda8261_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, + const struct tda8261_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif //CONFIG_DVB_TDA8261 + +#endif// __TDA8261_H diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h new file mode 100644 index 0000000000..3d26004d55 --- /dev/null +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + TDA8261 8PSK/QPSK tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + +*/ + +static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + int err = 0; + + if (tuner_ops->get_frequency) { + err = tuner_ops->get_frequency(fe, frequency); + if (err < 0) { + pr_err("%s: Invalid parameter\n", __func__); + return err; + } + pr_debug("%s: Frequency=%d\n", __func__, *frequency); + } + return 0; +} + +static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int err = 0; + + if (tuner_ops->set_params) { + err = tuner_ops->set_params(fe); + if (err < 0) { + pr_err("%s: Invalid parameter\n", __func__); + return err; + } + } + pr_debug("%s: Frequency=%d\n", __func__, c->frequency); + return 0; +} + +static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + /* FIXME! need to calculate Bandwidth */ + *bandwidth = 40000000; + + return 0; +} diff --git a/drivers/media/dvb-frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c new file mode 100644 index 0000000000..eafcf5f7da --- /dev/null +++ b/drivers/media/dvb-frontends/tda826x.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* + Driver for Philips tda8262/tda8263 DVBS Silicon tuners + + (c) 2006 Andrew de Quincey + + + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "tda826x.h" + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda826x: " args); \ + } while (0) + +struct tda826x_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u8 has_loopthrough:1; + u32 frequency; +}; + +static void tda826x_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int tda826x_sleep(struct dvb_frontend *fe) +{ + struct tda826x_priv *priv = fe->tuner_priv; + int ret; + u8 buf [] = { 0x00, 0x8d }; + struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 }; + + dprintk("%s:\n", __func__); + + if (!priv->has_loopthrough) + buf[1] = 0xad; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { + dprintk("%s: i2c error\n", __func__); + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int tda826x_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct tda826x_priv *priv = fe->tuner_priv; + int ret; + u32 div; + u32 ksyms; + u32 bandwidth; + u8 buf [11]; + struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 }; + + dprintk("%s:\n", __func__); + + div = (p->frequency + (1000-1)) / 1000; + + /* BW = ((1 + RO) * SR/2 + 5) * 1.3 [SR in MSPS, BW in MHz] */ + /* with R0 = 0.35 and some transformations: */ + ksyms = p->symbol_rate / 1000; + bandwidth = (878 * ksyms + 6500000) / 1000000 + 1; + if (bandwidth < 5) + bandwidth = 5; + else if (bandwidth > 36) + bandwidth = 36; + + buf[0] = 0x00; // subaddress + buf[1] = 0x09; // powerdown RSSI + the magic value 1 + if (!priv->has_loopthrough) + buf[1] |= 0x20; // power down loopthrough if not needed + buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO + buf[3] = div >> 7; + buf[4] = div << 1; + buf[5] = ((bandwidth - 5) << 3) | 7; /* baseband cut-off */ + buf[6] = 0xfe; // baseband gain 9 db + no RF attenuation + buf[7] = 0x83; // charge pumps at high, tests off + buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports. + buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL + buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { + dprintk("%s: i2c error\n", __func__); + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + priv->frequency = div * 1000; + + return (ret == 1) ? 0 : ret; +} + +static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda826x_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static const struct dvb_tuner_ops tda826x_tuner_ops = { + .info = { + .name = "Philips TDA826X", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz + }, + .release = tda826x_release, + .sleep = tda826x_sleep, + .set_params = tda826x_set_params, + .get_frequency = tda826x_get_frequency, +}; + +struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough) +{ + struct tda826x_priv *priv = NULL; + u8 b1 [] = { 0, 0 }; + struct i2c_msg msg[2] = { + { .addr = addr, .flags = 0, .buf = NULL, .len = 0 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } + }; + int ret; + + dprintk("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer (i2c, msg, 2); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) + return NULL; + if (!(b1[1] & 0x80)) + return NULL; + + priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = addr; + priv->i2c = i2c; + priv->has_loopthrough = has_loopthrough; + + memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + return fe; +} +EXPORT_SYMBOL_GPL(tda826x_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("DVB TDA826x driver"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h new file mode 100644 index 0000000000..e1d33edbb8 --- /dev/null +++ b/drivers/media/dvb-frontends/tda826x.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* + Driver for Philips tda8262/tda8263 DVBS Silicon tuners + + (c) 2006 Andrew de Quincey + + + */ + +#ifndef __DVB_TDA826X_H__ +#define __DVB_TDA826X_H__ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +/** + * tda826x_attach - Attach a tda826x tuner to the supplied frontend structure. + * + * @fe: Frontend to attach to. + * @addr: i2c address of the tuner. + * @i2c: i2c adapter to use. + * @has_loopthrough: Set to 1 if the card has a loopthrough RF connector. + * + * return: FE pointer on success, NULL on failure. + */ +#if IS_REACHABLE(CONFIG_DVB_TDA826X) +extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c, + int has_loopthrough); +#else +static inline struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, + int addr, + struct i2c_adapter *i2c, + int has_loopthrough) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_TDA826X + +#endif // __DVB_TDA826X_H__ diff --git a/drivers/media/dvb-frontends/tdhd1.h b/drivers/media/dvb-frontends/tdhd1.h new file mode 100644 index 0000000000..55973a5fce --- /dev/null +++ b/drivers/media/dvb-frontends/tdhd1.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * tdhd1.h - ALPS TDHD1-204A tuner support + * + * Copyright (C) 2008 Oliver Endriss <o.endriss@gmx.de> + * + * The project's page is at https://linuxtv.org + */ + +#ifndef TDHD1_H +#define TDHD1_H + +#include "tda1004x.h" + +static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name); + +static struct tda1004x_config alps_tdhd1_204a_config = { + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = alps_tdhd1_204_request_firmware +}; + +static int alps_tdhd1_204a_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct i2c_adapter *i2c = fe->tuner_priv; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + u32 div; + + div = (p->frequency + 36166666) / 166666; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85; + + if (p->frequency >= 174000000 && p->frequency <= 230000000) + data[3] = 0x02; + else if (p->frequency >= 470000000 && p->frequency <= 823000000) + data[3] = 0x0C; + else if (p->frequency > 823000000 && p->frequency <= 862000000) + data[3] = 0x8C; + else + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(i2c, &msg, 1) != 1) + return -EIO; + + return 0; +} + +#endif /* TDHD1_H */ diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c new file mode 100644 index 0000000000..a5ebce57f3 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009-2012 TurboSight.com + + */ + +#include <media/dvb_frontend.h> +#include "ts2020.h" +#include <linux/regmap.h> +#include <linux/math64.h> + +#define TS2020_XTAL_FREQ 27000 /* in kHz */ +#define FREQ_OFFSET_LOW_SYM_RATE 3000 + +struct ts2020_priv { + struct i2c_client *client; + struct mutex regmap_mutex; + struct regmap_config regmap_config; + struct regmap *regmap; + struct dvb_frontend *fe; + struct delayed_work stat_work; + int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm); + /* i2c details */ + struct i2c_adapter *i2c; + int i2c_address; + bool loop_through:1; + u8 clk_out:2; + u8 clk_out_div:5; + bool dont_poll:1; + u32 frequency_div; /* LO output divider switch frequency */ + u32 frequency_khz; /* actual used LO frequency */ +#define TS2020_M88TS2020 0 +#define TS2020_M88TS2022 1 + u8 tuner; +}; + +struct ts2020_reg_val { + u8 reg; + u8 val; +}; + +static void ts2020_stat_work(struct work_struct *work); + +static void ts2020_release(struct dvb_frontend *fe) +{ + struct ts2020_priv *priv = fe->tuner_priv; + struct i2c_client *client = priv->client; + + dev_dbg(&client->dev, "\n"); + + i2c_unregister_device(client); +} + +static int ts2020_sleep(struct dvb_frontend *fe) +{ + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + u8 u8tmp; + + if (priv->tuner == TS2020_M88TS2020) + u8tmp = 0x0a; /* XXX: probably wrong */ + else + u8tmp = 0x00; + + ret = regmap_write(priv->regmap, u8tmp, 0x00); + if (ret < 0) + return ret; + + /* stop statistics polling */ + if (!priv->dont_poll) + cancel_delayed_work_sync(&priv->stat_work); + return 0; +} + +static int ts2020_init(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->tuner_priv; + int i; + u8 u8tmp; + + if (priv->tuner == TS2020_M88TS2020) { + regmap_write(priv->regmap, 0x42, 0x73); + regmap_write(priv->regmap, 0x05, priv->clk_out_div); + regmap_write(priv->regmap, 0x20, 0x27); + regmap_write(priv->regmap, 0x07, 0x02); + regmap_write(priv->regmap, 0x11, 0xff); + regmap_write(priv->regmap, 0x60, 0xf9); + regmap_write(priv->regmap, 0x08, 0x01); + regmap_write(priv->regmap, 0x00, 0x41); + } else { + static const struct ts2020_reg_val reg_vals[] = { + {0x7d, 0x9d}, + {0x7c, 0x9a}, + {0x7a, 0x76}, + {0x3b, 0x01}, + {0x63, 0x88}, + {0x61, 0x85}, + {0x22, 0x30}, + {0x30, 0x40}, + {0x20, 0x23}, + {0x24, 0x02}, + {0x12, 0xa0}, + }; + + regmap_write(priv->regmap, 0x00, 0x01); + regmap_write(priv->regmap, 0x00, 0x03); + + switch (priv->clk_out) { + case TS2020_CLK_OUT_DISABLED: + u8tmp = 0x60; + break; + case TS2020_CLK_OUT_ENABLED: + u8tmp = 0x70; + regmap_write(priv->regmap, 0x05, priv->clk_out_div); + break; + case TS2020_CLK_OUT_ENABLED_XTALOUT: + u8tmp = 0x6c; + break; + default: + u8tmp = 0x60; + break; + } + + regmap_write(priv->regmap, 0x42, u8tmp); + + if (priv->loop_through) + u8tmp = 0xec; + else + u8tmp = 0x6c; + + regmap_write(priv->regmap, 0x62, u8tmp); + + for (i = 0; i < ARRAY_SIZE(reg_vals); i++) + regmap_write(priv->regmap, reg_vals[i].reg, + reg_vals[i].val); + } + + /* Initialise v5 stats here */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].uvalue = 0; + + /* Start statistics polling by invoking the work function */ + ts2020_stat_work(&priv->stat_work.work); + return 0; +} + +static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) +{ + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + ret = regmap_write(priv->regmap, 0x51, 0x1f - offset); + ret |= regmap_write(priv->regmap, 0x51, 0x1f); + ret |= regmap_write(priv->regmap, 0x50, offset); + ret |= regmap_write(priv->regmap, 0x50, 0x00); + msleep(20); + return ret; +} + +static int ts2020_set_tuner_rf(struct dvb_frontend *fe) +{ + struct ts2020_priv *dev = fe->tuner_priv; + int ret; + unsigned int utmp; + + ret = regmap_read(dev->regmap, 0x3d, &utmp); + if (ret) + return ret; + + utmp &= 0x7f; + if (utmp < 0x16) + utmp = 0xa1; + else if (utmp == 0x16) + utmp = 0x99; + else + utmp = 0xf9; + + regmap_write(dev->regmap, 0x60, utmp); + ret = ts2020_tuner_gate_ctrl(fe, 0x08); + + return ret; +} + +static int ts2020_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + unsigned int utmp; + u32 f3db, gdiv28; + u16 u16tmp, value, lpf_coeff; + u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf; + unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n; + unsigned int frequency_khz = c->frequency; + + /* + * Integer-N PLL synthesizer + * kHz is used for all calculations to keep calculations within 32-bit + */ + f_ref_khz = TS2020_XTAL_FREQ; + div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); + + /* select LO output divider */ + if (frequency_khz < priv->frequency_div) { + div_out = 4; + reg10 = 0x10; + } else { + div_out = 2; + reg10 = 0x00; + } + + f_vco_khz = frequency_khz * div_out; + pll_n = f_vco_khz * div_ref / f_ref_khz; + pll_n += pll_n % 2; + priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; + + pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", + priv->frequency_khz, priv->frequency_khz - c->frequency, + f_vco_khz, pll_n, div_ref, div_out); + + if (priv->tuner == TS2020_M88TS2020) { + lpf_coeff = 2766; + reg10 |= 0x01; + ret = regmap_write(priv->regmap, 0x10, reg10); + } else { + lpf_coeff = 3200; + reg10 |= 0x0b; + ret = regmap_write(priv->regmap, 0x10, reg10); + ret |= regmap_write(priv->regmap, 0x11, 0x40); + } + + u16tmp = pll_n - 1024; + buf[0] = (u16tmp >> 8) & 0xff; + buf[1] = (u16tmp >> 0) & 0xff; + buf[2] = div_ref - 8; + + ret |= regmap_write(priv->regmap, 0x01, buf[0]); + ret |= regmap_write(priv->regmap, 0x02, buf[1]); + ret |= regmap_write(priv->regmap, 0x03, buf[2]); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x10); + if (ret < 0) + return -ENODEV; + + ret |= ts2020_tuner_gate_ctrl(fe, 0x08); + + /* Tuner RF */ + if (priv->tuner == TS2020_M88TS2020) + ret |= ts2020_set_tuner_rf(fe); + + gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; + ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff); + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + if (ret < 0) + return -ENODEV; + + if (priv->tuner == TS2020_M88TS2022) { + ret = regmap_write(priv->regmap, 0x25, 0x00); + ret |= regmap_write(priv->regmap, 0x27, 0x70); + ret |= regmap_write(priv->regmap, 0x41, 0x09); + ret |= regmap_write(priv->regmap, 0x08, 0x0b); + if (ret < 0) + return -ENODEV; + } + + regmap_read(priv->regmap, 0x26, &utmp); + value = utmp; + + f3db = (c->bandwidth_hz / 1000 / 2) + 2000; + f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */ + f3db = clamp(f3db, 7000U, 40000U); + + gdiv28 = gdiv28 * 207 / (value * 2 + 151); + mlpf_max = gdiv28 * 135 / 100; + mlpf_min = gdiv28 * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + nlpf = (f3db * gdiv28 * 2 / lpf_coeff / + (TS2020_XTAL_FREQ / 1000) + 1) / 2; + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; + + if (lpf_mxdiv < mlpf_min) { + nlpf++; + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; + } + + if (lpf_mxdiv > mlpf_max) + lpf_mxdiv = mlpf_max; + + ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv); + ret |= regmap_write(priv->regmap, 0x06, nlpf); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + + ret |= ts2020_tuner_gate_ctrl(fe, 0x01); + + msleep(80); + + return (ret < 0) ? -EINVAL : 0; +} + +static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ts2020_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency_khz; + return 0; +} + +static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ + return 0; +} + +/* + * Get the tuner gain. + * @fe: The front end for which we're determining the gain + * @v_agc: The voltage of the AGC from the demodulator (0-2600mV) + * @_gain: Where to store the gain (in 0.001dB units) + * + * Returns 0 or a negative error code. + */ +static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc, + __s64 *_gain) +{ + struct ts2020_priv *priv = fe->tuner_priv; + unsigned long gain1, gain2, gain3; + unsigned utmp; + int ret; + + /* Read the RF gain */ + ret = regmap_read(priv->regmap, 0x3d, &utmp); + if (ret < 0) + return ret; + gain1 = utmp & 0x1f; + + /* Read the baseband gain */ + ret = regmap_read(priv->regmap, 0x21, &utmp); + if (ret < 0) + return ret; + gain2 = utmp & 0x1f; + + switch (priv->tuner) { + case TS2020_M88TS2020: + gain1 = clamp_t(long, gain1, 0, 15); + gain2 = clamp_t(long, gain2, 0, 13); + v_agc = clamp_t(long, v_agc, 400, 1100); + + *_gain = -((__s64)gain1 * 2330 + + gain2 * 3500 + + v_agc * 24 / 10 * 10 + + 10000); + /* gain in range -19600 to -116850 in units of 0.001dB */ + break; + + case TS2020_M88TS2022: + ret = regmap_read(priv->regmap, 0x66, &utmp); + if (ret < 0) + return ret; + gain3 = (utmp >> 3) & 0x07; + + gain1 = clamp_t(long, gain1, 0, 15); + gain2 = clamp_t(long, gain2, 2, 16); + gain3 = clamp_t(long, gain3, 0, 6); + v_agc = clamp_t(long, v_agc, 600, 1600); + + *_gain = -((__s64)gain1 * 2650 + + gain2 * 3380 + + gain3 * 2850 + + v_agc * 176 / 100 * 10 - + 30000); + /* gain in range -47320 to -158950 in units of 0.001dB */ + break; + } + + return 0; +} + +/* + * Get the AGC information from the demodulator and use that to calculate the + * tuner gain. + */ +static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain) +{ + struct ts2020_priv *priv = fe->tuner_priv; + int v_agc = 0, ret; + u8 agc_pwm; + + /* Read the AGC PWM rate from the demodulator */ + if (priv->get_agc_pwm) { + ret = priv->get_agc_pwm(fe, &agc_pwm); + if (ret < 0) + return ret; + + switch (priv->tuner) { + case TS2020_M88TS2020: + v_agc = (int)agc_pwm * 20 - 1166; + break; + case TS2020_M88TS2022: + v_agc = (int)agc_pwm * 16 - 670; + break; + } + + if (v_agc < 0) + v_agc = 0; + } + + return ts2020_read_tuner_gain(fe, v_agc, _gain); +} + +/* + * Gather statistics on a regular basis + */ +static void ts2020_stat_work(struct work_struct *work) +{ + struct ts2020_priv *priv = container_of(work, struct ts2020_priv, + stat_work.work); + struct i2c_client *client = priv->client; + struct dtv_frontend_properties *c = &priv->fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "\n"); + + ret = ts2020_get_tuner_gain(priv->fe, &c->strength.stat[0].svalue); + if (ret < 0) + goto err; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + + if (!priv->dont_poll) + schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000)); + return; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); +} + +/* + * Read TS2020 signal strength in v3 format. + */ +static int ts2020_read_signal_strength(struct dvb_frontend *fe, + u16 *_signal_strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->tuner_priv; + unsigned strength; + __s64 gain; + + if (priv->dont_poll) + ts2020_stat_work(&priv->stat_work.work); + + if (c->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + *_signal_strength = 0; + return 0; + } + + gain = c->strength.stat[0].svalue; + + /* Calculate the signal strength based on the total gain of the tuner */ + if (gain < -85000) + /* 0%: no signal or weak signal */ + strength = 0; + else if (gain < -65000) + /* 0% - 60%: weak signal */ + strength = 0 + div64_s64((85000 + gain) * 3, 1000); + else if (gain < -45000) + /* 60% - 90%: normal signal */ + strength = 60 + div64_s64((65000 + gain) * 3, 2000); + else + /* 90% - 99%: strong signal */ + strength = 90 + div64_s64((45000 + gain), 5000); + + *_signal_strength = strength * 65535 / 100; + return 0; +} + +static const struct dvb_tuner_ops ts2020_tuner_ops = { + .info = { + .name = "TS2020", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz + }, + .init = ts2020_init, + .release = ts2020_release, + .sleep = ts2020_sleep, + .set_params = ts2020_set_params, + .get_frequency = ts2020_get_frequency, + .get_if_frequency = ts2020_get_if_frequency, + .get_rf_strength = ts2020_read_signal_strength, +}; + +struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c) +{ + struct i2c_client *client; + struct i2c_board_info board_info; + + /* This is only used by ts2020_probe() so can be on the stack */ + struct ts2020_config pdata; + + memcpy(&pdata, config, sizeof(pdata)); + pdata.fe = fe; + pdata.attach_in_use = true; + + memset(&board_info, 0, sizeof(board_info)); + strscpy(board_info.type, "ts2020", I2C_NAME_SIZE); + board_info.addr = config->tuner_address; + board_info.platform_data = &pdata; + client = i2c_new_client_device(i2c, &board_info); + if (!i2c_client_has_driver(client)) + return NULL; + + return fe; +} +EXPORT_SYMBOL_GPL(ts2020_attach); + +/* + * We implement own regmap locking due to legacy DVB attach which uses frontend + * gate control callback to control I2C bus access. We can open / close gate and + * serialize whole open / I2C-operation / close sequence at the same. + */ +static void ts2020_regmap_lock(void *__dev) +{ + struct ts2020_priv *dev = __dev; + + mutex_lock(&dev->regmap_mutex); + if (dev->fe->ops.i2c_gate_ctrl) + dev->fe->ops.i2c_gate_ctrl(dev->fe, 1); +} + +static void ts2020_regmap_unlock(void *__dev) +{ + struct ts2020_priv *dev = __dev; + + if (dev->fe->ops.i2c_gate_ctrl) + dev->fe->ops.i2c_gate_ctrl(dev->fe, 0); + mutex_unlock(&dev->regmap_mutex); +} + +static int ts2020_probe(struct i2c_client *client) +{ + struct ts2020_config *pdata = client->dev.platform_data; + struct dvb_frontend *fe = pdata->fe; + struct ts2020_priv *dev; + int ret; + u8 u8tmp; + unsigned int utmp; + char *chip_str; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + /* create regmap */ + mutex_init(&dev->regmap_mutex); + dev->regmap_config.reg_bits = 8; + dev->regmap_config.val_bits = 8; + dev->regmap_config.lock = ts2020_regmap_lock; + dev->regmap_config.unlock = ts2020_regmap_unlock; + dev->regmap_config.lock_arg = dev; + dev->regmap = regmap_init_i2c(client, &dev->regmap_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + dev->i2c = client->adapter; + dev->i2c_address = client->addr; + dev->loop_through = pdata->loop_through; + dev->clk_out = pdata->clk_out; + dev->clk_out_div = pdata->clk_out_div; + dev->dont_poll = pdata->dont_poll; + dev->frequency_div = pdata->frequency_div; + dev->fe = fe; + dev->get_agc_pwm = pdata->get_agc_pwm; + fe->tuner_priv = dev; + dev->client = client; + INIT_DELAYED_WORK(&dev->stat_work, ts2020_stat_work); + + /* check if the tuner is there */ + ret = regmap_read(dev->regmap, 0x00, &utmp); + if (ret) + goto err_regmap_exit; + + if ((utmp & 0x03) == 0x00) { + ret = regmap_write(dev->regmap, 0x00, 0x01); + if (ret) + goto err_regmap_exit; + + usleep_range(2000, 50000); + } + + ret = regmap_write(dev->regmap, 0x00, 0x03); + if (ret) + goto err_regmap_exit; + + usleep_range(2000, 50000); + + ret = regmap_read(dev->regmap, 0x00, &utmp); + if (ret) + goto err_regmap_exit; + + dev_dbg(&client->dev, "chip_id=%02x\n", utmp); + + switch (utmp) { + case 0x01: + case 0x41: + case 0x81: + dev->tuner = TS2020_M88TS2020; + chip_str = "TS2020"; + if (!dev->frequency_div) + dev->frequency_div = 1060000; + break; + case 0xc3: + case 0x83: + dev->tuner = TS2020_M88TS2022; + chip_str = "TS2022"; + if (!dev->frequency_div) + dev->frequency_div = 1103000; + break; + default: + ret = -ENODEV; + goto err_regmap_exit; + } + + if (dev->tuner == TS2020_M88TS2022) { + switch (dev->clk_out) { + case TS2020_CLK_OUT_DISABLED: + u8tmp = 0x60; + break; + case TS2020_CLK_OUT_ENABLED: + u8tmp = 0x70; + ret = regmap_write(dev->regmap, 0x05, dev->clk_out_div); + if (ret) + goto err_regmap_exit; + break; + case TS2020_CLK_OUT_ENABLED_XTALOUT: + u8tmp = 0x6c; + break; + default: + ret = -EINVAL; + goto err_regmap_exit; + } + + ret = regmap_write(dev->regmap, 0x42, u8tmp); + if (ret) + goto err_regmap_exit; + + if (dev->loop_through) + u8tmp = 0xec; + else + u8tmp = 0x6c; + + ret = regmap_write(dev->regmap, 0x62, u8tmp); + if (ret) + goto err_regmap_exit; + } + + /* sleep */ + ret = regmap_write(dev->regmap, 0x00, 0x00); + if (ret) + goto err_regmap_exit; + + dev_info(&client->dev, + "Montage Technology %s successfully identified\n", chip_str); + + memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, + sizeof(struct dvb_tuner_ops)); + if (!pdata->attach_in_use) + fe->ops.tuner_ops.release = NULL; + + i2c_set_clientdata(client, dev); + return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static void ts2020_remove(struct i2c_client *client) +{ + struct ts2020_priv *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + /* stop statistics polling */ + if (!dev->dont_poll) + cancel_delayed_work_sync(&dev->stat_work); + + regmap_exit(dev->regmap); + kfree(dev); +} + +static const struct i2c_device_id ts2020_id_table[] = { + {"ts2020", 0}, + {"ts2022", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ts2020_id_table); + +static struct i2c_driver ts2020_driver = { + .driver = { + .name = "ts2020", + }, + .probe = ts2020_probe, + .remove = ts2020_remove, + .id_table = ts2020_id_table, +}; + +module_i2c_driver(ts2020_driver); + +MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>"); +MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h new file mode 100644 index 0000000000..84c2dc8c3a --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> + + Copyright (C) 2009-2012 TurboSight.com + + */ + +#ifndef TS2020_H +#define TS2020_H + +#include <linux/dvb/frontend.h> + +struct ts2020_config { + u8 tuner_address; + u32 frequency_div; + + /* + * RF loop-through + */ + bool loop_through:1; + + /* + * clock output + */ +#define TS2020_CLK_OUT_DISABLED 0 +#define TS2020_CLK_OUT_ENABLED 1 +#define TS2020_CLK_OUT_ENABLED_XTALOUT 2 + u8 clk_out:2; + + /* + * clock output divider + * 1 - 31 + */ + u8 clk_out_div:5; + + /* Set to true to suppress stat polling */ + bool dont_poll:1; + + /* + * pointer to DVB frontend + */ + struct dvb_frontend *fe; + + /* + * driver private, do not set value + */ + u8 attach_in_use:1; + + /* Operation to be called by the ts2020 driver to get the value of the + * AGC PWM tuner input as theoretically output by the demodulator. + */ + int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm); +}; + +/* Do not add new ts2020_attach() users! Use I2C bindings instead. */ +#if IS_REACHABLE(CONFIG_DVB_TS2020) +extern struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* TS2020_H */ diff --git a/drivers/media/dvb-frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c new file mode 100644 index 0000000000..41dd9b6d31 --- /dev/null +++ b/drivers/media/dvb-frontends/tua6100.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Infineon tua6100 pll. + * + * (c) 2006 Andrew de Quincey + * + * Based on code found in budget-av.c, which has the following: + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & + * Andrew de Quincey <adq_dvb@lidskialf.net> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "tua6100.h" + +struct tua6100_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u32 frequency; +}; + +static void tua6100_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; +} + +static int tua6100_sleep(struct dvb_frontend *fe) +{ + struct tua6100_priv *priv = fe->tuner_priv; + int ret; + u8 reg0[] = { 0x00, 0x00 }; + struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { + printk("%s: i2c error\n", __func__); + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int tua6100_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tua6100_priv *priv = fe->tuner_priv; + u32 div; + u32 prediv; + u8 reg0[] = { 0x00, 0x00 }; + u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 }; + u8 reg2[] = { 0x02, 0x00, 0x00 }; + struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; + struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 }; + struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 }; + +#define _R_VAL 4 +#define _P_VAL 32 +#define _ri 4000000 + + // setup register 0 + if (c->frequency < 2000000) + reg0[1] = 0x03; + else + reg0[1] = 0x07; + + // setup register 1 + if (c->frequency < 1630000) + reg1[1] = 0x2c; + else + reg1[1] = 0x0c; + + if (_P_VAL == 64) + reg1[1] |= 0x40; + if (c->frequency >= 1525000) + reg1[1] |= 0x80; + + // register 2 + reg2[1] = (_R_VAL >> 8) & 0x03; + reg2[2] = _R_VAL; + if (c->frequency < 1455000) + reg2[1] |= 0x1c; + else if (c->frequency < 1630000) + reg2[1] |= 0x0c; + else + reg2[1] |= 0x1c; + + /* + * The N divisor ratio (note: c->frequency is in kHz, but we + * need it in Hz) + */ + prediv = (c->frequency * _R_VAL) / (_ri / 1000); + div = prediv / _P_VAL; + reg1[1] |= (div >> 9) & 0x03; + reg1[2] = div >> 1; + reg1[3] = (div << 7); + priv->frequency = ((div * _P_VAL) * (_ri / 1000)) / _R_VAL; + + // Finally, calculate and store the value for A + reg1[3] |= (prediv - (div*_P_VAL)) & 0x7f; + +#undef _R_VAL +#undef _P_VAL +#undef _ri + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c, &msg0, 1) != 1) + return -EIO; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c, &msg2, 1) != 1) + return -EIO; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c, &msg1, 1) != 1) + return -EIO; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tua6100_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static const struct dvb_tuner_ops tua6100_tuner_ops = { + .info = { + .name = "Infineon TUA6100", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, + }, + .release = tua6100_release, + .sleep = tua6100_sleep, + .set_params = tua6100_set_params, + .get_frequency = tua6100_get_frequency, +}; + +struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) +{ + struct tua6100_priv *priv = NULL; + u8 b1 [] = { 0x80 }; + u8 b2 [] = { 0x00 }; + struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } }; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer (i2c, msg, 2); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) + return NULL; + + priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = addr; + priv->i2c = i2c; + + memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL_GPL(tua6100_attach); + +MODULE_DESCRIPTION("DVB tua6100 driver"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/tua6100.h b/drivers/media/dvb-frontends/tua6100.h new file mode 100644 index 0000000000..2acbf4c221 --- /dev/null +++ b/drivers/media/dvb-frontends/tua6100.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Infineon tua6100 PLL. + * + * (c) 2006 Andrew de Quincey + * + * Based on code found in budget-av.c, which has the following: + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & + * Andrew de Quincey <adq_dvb@lidskialf.net> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + */ + +#ifndef __DVB_TUA6100_H__ +#define __DVB_TUA6100_H__ + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +#if IS_REACHABLE(CONFIG_DVB_TUA6100) +extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_TUA6100 + +#endif diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c new file mode 100644 index 0000000000..ee5620e731 --- /dev/null +++ b/drivers/media/dvb-frontends/ves1820.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + VES1820 - Single Chip Cable Channel Receiver driver module + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + +*/ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "ves1820.h" + + + +struct ves1820_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct ves1820_config* config; + struct dvb_frontend frontend; + + /* private demodulator data */ + u8 reg0; + u8 pwm; +}; + + +static int verbose; + +static u8 ves1820_inittab[] = { + 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A, + 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20, + 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40 +}; + +static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data) +{ + u8 buf[] = { 0x00, reg, data }; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 }; + int ret; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("ves1820: %s(): writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static u8 ves1820_readreg(struct ves1820_state *state, u8 reg) +{ + u8 b0[] = { 0x00, reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} + }; + int ret; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk("ves1820: %s(): readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static int ves1820_setup_reg0(struct ves1820_state *state, + u8 reg0, enum fe_spectral_inversion inversion) +{ + reg0 |= state->reg0 & 0x62; + + if (INVERSION_ON == inversion) { + if (!state->config->invert) reg0 |= 0x20; + else reg0 &= ~0x20; + } else if (INVERSION_OFF == inversion) { + if (!state->config->invert) reg0 &= ~0x20; + else reg0 |= 0x20; + } + + ves1820_writereg(state, 0x00, reg0 & 0xfe); + ves1820_writereg(state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + + return 0; +} + +static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) +{ + s32 BDR; + s32 BDRI; + s16 SFIL = 0; + u16 NDEC = 0; + u32 ratio; + u32 fin; + u32 tmp; + u64 fptmp; + u64 fpxin; + + if (symbolrate > state->config->xin / 2) + symbolrate = state->config->xin / 2; + + if (symbolrate < 500000) + symbolrate = 500000; + + if (symbolrate < state->config->xin / 16) + NDEC = 1; + if (symbolrate < state->config->xin / 32) + NDEC = 2; + if (symbolrate < state->config->xin / 64) + NDEC = 3; + + /* yeuch! */ + fpxin = state->config->xin * 10ULL; + fptmp = fpxin; do_div(fptmp, 123); + if (symbolrate < fptmp) + SFIL = 1; + fptmp = fpxin; do_div(fptmp, 160); + if (symbolrate < fptmp) + SFIL = 0; + fptmp = fpxin; do_div(fptmp, 246); + if (symbolrate < fptmp) + SFIL = 1; + fptmp = fpxin; do_div(fptmp, 320); + if (symbolrate < fptmp) + SFIL = 0; + fptmp = fpxin; do_div(fptmp, 492); + if (symbolrate < fptmp) + SFIL = 1; + fptmp = fpxin; do_div(fptmp, 640); + if (symbolrate < fptmp) + SFIL = 0; + fptmp = fpxin; do_div(fptmp, 984); + if (symbolrate < fptmp) + SFIL = 1; + + fin = state->config->xin >> 4; + symbolrate <<= NDEC; + ratio = (symbolrate << 4) / fin; + tmp = ((symbolrate << 4) % fin) << 8; + ratio = (ratio << 8) + tmp / fin; + tmp = (tmp % fin) << 8; + ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin); + + BDR = ratio; + BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2; + + if (BDRI > 0xFF) + BDRI = 0xFF; + + SFIL = (SFIL << 4) | ves1820_inittab[0x0E]; + + NDEC = (NDEC << 6) | ves1820_inittab[0x03]; + + ves1820_writereg(state, 0x03, NDEC); + ves1820_writereg(state, 0x0a, BDR & 0xff); + ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff); + ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f); + + ves1820_writereg(state, 0x0d, BDRI); + ves1820_writereg(state, 0x0e, SFIL); + + return 0; +} + +static int ves1820_init(struct dvb_frontend* fe) +{ + struct ves1820_state* state = fe->demodulator_priv; + int i; + + ves1820_writereg(state, 0, 0); + + for (i = 0; i < sizeof(ves1820_inittab); i++) + ves1820_writereg(state, i, ves1820_inittab[i]); + if (state->config->selagc) + ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08); + + ves1820_writereg(state, 0x34, state->pwm); + + return 0; +} + +static int ves1820_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ves1820_state* state = fe->demodulator_priv; + static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 }; + static const u8 reg0x01[] = { 140, 140, 106, 100, 92 }; + static const u8 reg0x05[] = { 135, 100, 70, 54, 38 }; + static const u8 reg0x08[] = { 162, 116, 67, 52, 35 }; + static const u8 reg0x09[] = { 145, 150, 106, 126, 107 }; + int real_qam = p->modulation - QAM_16; + + if (real_qam < 0 || real_qam > 4) + return -EINVAL; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + ves1820_set_symbolrate(state, p->symbol_rate); + ves1820_writereg(state, 0x34, state->pwm); + + ves1820_writereg(state, 0x01, reg0x01[real_qam]); + ves1820_writereg(state, 0x05, reg0x05[real_qam]); + ves1820_writereg(state, 0x08, reg0x08[real_qam]); + ves1820_writereg(state, 0x09, reg0x09[real_qam]); + + ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion); + ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0)); + return 0; +} + +static int ves1820_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct ves1820_state* state = fe->demodulator_priv; + int sync; + + *status = 0; + sync = ves1820_readreg(state, 0x11); + + if (sync & 1) + *status |= FE_HAS_SIGNAL; + + if (sync & 2) + *status |= FE_HAS_CARRIER; + + if (sync & 2) /* XXX FIXME! */ + *status |= FE_HAS_VITERBI; + + if (sync & 4) + *status |= FE_HAS_SYNC; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct ves1820_state* state = fe->demodulator_priv; + + u32 _ber = ves1820_readreg(state, 0x14) | + (ves1820_readreg(state, 0x15) << 8) | + ((ves1820_readreg(state, 0x16) & 0x0f) << 16); + *ber = 10 * _ber; + + return 0; +} + +static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct ves1820_state* state = fe->demodulator_priv; + + u8 gain = ves1820_readreg(state, 0x17); + *strength = (gain << 8) | gain; + + return 0; +} + +static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct ves1820_state* state = fe->demodulator_priv; + + u8 quality = ~ves1820_readreg(state, 0x18); + *snr = (quality << 8) | quality; + + return 0; +} + +static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct ves1820_state* state = fe->demodulator_priv; + + *ucblocks = ves1820_readreg(state, 0x13) & 0x7f; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; + + /* reset uncorrected block counter */ + ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf); + ves1820_writereg(state, 0x10, ves1820_inittab[0x10]); + + return 0; +} + +static int ves1820_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct ves1820_state* state = fe->demodulator_priv; + int sync; + s8 afc = 0; + + sync = ves1820_readreg(state, 0x11); + afc = ves1820_readreg(state, 0x19); + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" : + "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10); + } + + if (!state->config->invert) { + p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF; + } else { + p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF; + } + + p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->fec_inner = FEC_NONE; + + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + if (sync & 2) + p->frequency -= ((s32) p->symbol_rate * afc) >> 10; + + return 0; +} + +static int ves1820_sleep(struct dvb_frontend* fe) +{ + struct ves1820_state* state = fe->demodulator_priv; + + ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ + ves1820_writereg(state, 0x00, 0x80); /* standby */ + + return 0; +} + +static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + + fesettings->min_delay_ms = 200; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void ves1820_release(struct dvb_frontend* fe) +{ + struct ves1820_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops ves1820_ops; + +struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, + u8 pwm) +{ + struct ves1820_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->reg0 = ves1820_inittab[0]; + state->config = config; + state->i2c = i2c; + state->pwm = pwm; + + /* check if the demod is there */ + if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70) + goto error; + + if (verbose) + printk("ves1820: pwm=0x%02x\n", state->pwm); + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */ + state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */ + state->frontend.demodulator_priv = state; + + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops ves1820_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "VLSI VES1820 DVB-C", + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = ves1820_release, + + .init = ves1820_init, + .sleep = ves1820_sleep, + + .set_frontend = ves1820_set_parameters, + .get_frontend = ves1820_get_frontend, + .get_tune_settings = ves1820_get_tune_settings, + + .read_status = ves1820_read_status, + .read_ber = ves1820_read_ber, + .read_signal_strength = ves1820_read_signal_strength, + .read_snr = ves1820_read_snr, + .read_ucblocks = ves1820_read_ucblocks, +}; + +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); + +MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(ves1820_attach); diff --git a/drivers/media/dvb-frontends/ves1820.h b/drivers/media/dvb-frontends/ves1820.h new file mode 100644 index 0000000000..73316eb417 --- /dev/null +++ b/drivers/media/dvb-frontends/ves1820.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + VES1820 - Single Chip Cable Channel Receiver driver module + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + +*/ + +#ifndef VES1820_H +#define VES1820_H + +#include <linux/dvb/frontend.h> + +#define VES1820_SELAGC_PWM 0 +#define VES1820_SELAGC_SIGNAMPERR 1 + +struct ves1820_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* value of XIN to use */ + u32 xin; + + /* does inversion need inverted? */ + u8 invert:1; + + /* SELAGC control */ + u8 selagc:1; +}; + +#if IS_REACHABLE(CONFIG_DVB_VES1820) +extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, u8 pwm); +#else +static inline struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_VES1820 + +#endif // VES1820_H diff --git a/drivers/media/dvb-frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c new file mode 100644 index 0000000000..c60e21d26b --- /dev/null +++ b/drivers/media/dvb-frontends/ves1x93.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for VES1893 and VES1993 QPSK Demodulators + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> + Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de> + Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.org> + + +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#include <media/dvb_frontend.h> +#include "ves1x93.h" + + +struct ves1x93_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct ves1x93_config* config; + struct dvb_frontend frontend; + + /* previous uncorrected block counter */ + enum fe_spectral_inversion inversion; + u8 *init_1x93_tab; + u8 *init_1x93_wtab; + u8 tab_size; + u8 demod_type; + u32 frequency; +}; + +static int debug; +#define dprintk if (debug) printk + +#define DEMOD_VES1893 0 +#define DEMOD_VES1993 1 + +static u8 init_1893_tab [] = { + 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4, + 0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7f, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x21, 0xb0, 0x14, 0x00, 0xdc, 0x00, + 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x00, 0x00, 0x7f, 0x00 +}; + +static u8 init_1993_tab [] = { + 0x00, 0x9c, 0x35, 0x80, 0x6a, 0x09, 0x72, 0x8c, + 0x09, 0x6b, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x40, 0x21, 0xb0, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x0e, 0x80, 0x00 +}; + +static u8 init_1893_wtab[] = +{ + 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, + 0,1,0,0,0,0,0,0, 1,0,1,1,0,0,0,1, + 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0, + 1,1,1,0,1,1 +}; + +static u8 init_1993_wtab[] = +{ + 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, + 0,1,0,0,0,0,0,0, 1,1,1,1,0,0,0,1, + 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0, + 1,1,1,0,1,1,1,1, 1,1,1,1,1 +}; + +static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data) +{ + u8 buf [] = { 0x00, reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 }; + int err; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static u8 ves1x93_readreg (struct ves1x93_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { 0x00, reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) return ret; + + return b1[0]; +} + +static int ves1x93_clr_bit (struct ves1x93_state* state) +{ + msleep(10); + ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe); + ves1x93_writereg (state, 0, state->init_1x93_tab[0]); + msleep(50); + return 0; +} + +static int ves1x93_set_inversion(struct ves1x93_state *state, + enum fe_spectral_inversion inversion) +{ + u8 val; + + /* + * inversion on/off are interchanged because i and q seem to + * be swapped on the hardware + */ + + switch (inversion) { + case INVERSION_OFF: + val = 0xc0; + break; + case INVERSION_ON: + val = 0x80; + break; + case INVERSION_AUTO: + val = 0x00; + break; + default: + return -EINVAL; + } + + return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val); +} + +static int ves1x93_set_fec(struct ves1x93_state *state, enum fe_code_rate fec) +{ + if (fec == FEC_AUTO) + return ves1x93_writereg (state, 0x0d, 0x08); + else if (fec < FEC_1_2 || fec > FEC_8_9) + return -EINVAL; + else + return ves1x93_writereg (state, 0x0d, fec - FEC_1_2); +} + +static enum fe_code_rate ves1x93_get_fec(struct ves1x93_state *state) +{ + return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7); +} + +static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate) +{ + u32 BDR; + u32 ratio; + u8 ADCONF, FCONF, FNR, AGCR; + u32 BDRI; + u32 tmp; + u32 FIN; + + dprintk("%s: srate == %d\n", __func__, (unsigned int) srate); + + if (srate > state->config->xin/2) + srate = state->config->xin/2; + + if (srate < 500000) + srate = 500000; + +#define MUL (1UL<<26) + + FIN = (state->config->xin + 6000) >> 4; + + tmp = srate << 6; + ratio = tmp / FIN; + + tmp = (tmp % FIN) << 8; + ratio = (ratio << 8) + tmp / FIN; + + tmp = (tmp % FIN) << 8; + ratio = (ratio << 8) + tmp / FIN; + + FNR = 0xff; + + if (ratio < MUL/3) FNR = 0; + if (ratio < (MUL*11)/50) FNR = 1; + if (ratio < MUL/6) FNR = 2; + if (ratio < MUL/9) FNR = 3; + if (ratio < MUL/12) FNR = 4; + if (ratio < (MUL*11)/200) FNR = 5; + if (ratio < MUL/24) FNR = 6; + if (ratio < (MUL*27)/1000) FNR = 7; + if (ratio < MUL/48) FNR = 8; + if (ratio < (MUL*137)/10000) FNR = 9; + + if (FNR == 0xff) { + ADCONF = 0x89; + FCONF = 0x80; + FNR = 0; + } else { + ADCONF = 0x81; + FCONF = 0x88 | (FNR >> 1) | ((FNR & 0x01) << 5); + /*FCONF = 0x80 | ((FNR & 0x01) << 5) | (((FNR > 1) & 0x03) << 3) | ((FNR >> 1) & 0x07);*/ + } + + BDR = (( (ratio << (FNR >> 1)) >> 4) + 1) >> 1; + BDRI = ( ((FIN << 8) / ((srate << (FNR >> 1)) >> 2)) + 1) >> 1; + + dprintk("FNR= %d\n", FNR); + dprintk("ratio= %08x\n", (unsigned int) ratio); + dprintk("BDR= %08x\n", (unsigned int) BDR); + dprintk("BDRI= %02x\n", (unsigned int) BDRI); + + if (BDRI > 0xff) + BDRI = 0xff; + + ves1x93_writereg (state, 0x06, 0xff & BDR); + ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8)); + ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16)); + + ves1x93_writereg (state, 0x09, BDRI); + ves1x93_writereg (state, 0x20, ADCONF); + ves1x93_writereg (state, 0x21, FCONF); + + AGCR = state->init_1x93_tab[0x05]; + if (state->config->invert_pwm) + AGCR |= 0x20; + + if (srate < 6000000) + AGCR |= 0x80; + else + AGCR &= ~0x80; + + ves1x93_writereg (state, 0x05, AGCR); + + /* ves1993 hates this, will lose lock */ + if (state->demod_type != DEMOD_VES1993) + ves1x93_clr_bit (state); + + return 0; +} + +static int ves1x93_init (struct dvb_frontend* fe) +{ + struct ves1x93_state* state = fe->demodulator_priv; + int i; + int val; + + dprintk("%s: init chip\n", __func__); + + for (i = 0; i < state->tab_size; i++) { + if (state->init_1x93_wtab[i]) { + val = state->init_1x93_tab[i]; + + if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */ + ves1x93_writereg (state, i, val); + } + } + + return 0; +} + +static int ves1x93_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + return ves1x93_writereg (state, 0x1f, 0x20); + case SEC_VOLTAGE_18: + return ves1x93_writereg (state, 0x1f, 0x30); + case SEC_VOLTAGE_OFF: + return ves1x93_writereg (state, 0x1f, 0x00); + default: + return -EINVAL; + } +} + +static int ves1x93_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + u8 sync = ves1x93_readreg (state, 0x0e); + + /* + * The ves1893 sometimes returns sync values that make no sense, + * because, e.g., the SIGNAL bit is 0, while some of the higher + * bits are 1 (and how can there be a CARRIER w/o a SIGNAL?). + * Tests showed that the VITERBI and SYNC bits are returned + * reliably, while the SIGNAL and CARRIER bits ar sometimes wrong. + * If such a case occurs, we read the value again, until we get a + * valid value. + */ + int maxtry = 10; /* just for safety - let's not get stuck here */ + while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) { + msleep(10); + sync = ves1x93_readreg (state, 0x0e); + } + + *status = 0; + + if (sync & 1) + *status |= FE_HAS_SIGNAL; + + if (sync & 2) + *status |= FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_SYNC; + + if ((sync & 0x1f) == 0x1f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + *ber = ves1x93_readreg (state, 0x15); + *ber |= (ves1x93_readreg (state, 0x16) << 8); + *ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16); + *ber *= 10; + + return 0; +} + +static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + u8 signal = ~ves1x93_readreg (state, 0x0b); + *strength = (signal << 8) | signal; + + return 0; +} + +static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + u8 _snr = ~ves1x93_readreg (state, 0x1c); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + *ucblocks = ves1x93_readreg (state, 0x18) & 0x7f; + + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; /* counter overflow... */ + + ves1x93_writereg (state, 0x18, 0x00); /* reset the counter */ + ves1x93_writereg (state, 0x18, 0x80); /* dto. */ + + return 0; +} + +static int ves1x93_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ves1x93_state* state = fe->demodulator_priv; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + ves1x93_set_inversion (state, p->inversion); + ves1x93_set_fec(state, p->fec_inner); + ves1x93_set_symbolrate(state, p->symbol_rate); + state->inversion = p->inversion; + state->frequency = p->frequency; + + return 0; +} + +static int ves1x93_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct ves1x93_state* state = fe->demodulator_priv; + int afc; + + afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2; + afc = (afc * (int)(p->symbol_rate/1000/8))/16; + + p->frequency = state->frequency - afc; + + /* + * inversion indicator is only valid + * if auto inversion was used + */ + if (state->inversion == INVERSION_AUTO) + p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ? + INVERSION_OFF : INVERSION_ON; + p->fec_inner = ves1x93_get_fec(state); + /* XXX FIXME: timing offset !! */ + + return 0; +} + +static int ves1x93_sleep(struct dvb_frontend* fe) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + return ves1x93_writereg (state, 0x00, 0x08); +} + +static void ves1x93_release(struct dvb_frontend* fe) +{ + struct ves1x93_state* state = fe->demodulator_priv; + kfree(state); +} + +static int ves1x93_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + if (enable) { + return ves1x93_writereg(state, 0x00, 0x11); + } else { + return ves1x93_writereg(state, 0x00, 0x01); + } +} + +static const struct dvb_frontend_ops ves1x93_ops; + +struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c) +{ + struct ves1x93_state* state = NULL; + u8 identity; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->inversion = INVERSION_OFF; + + /* check if the demod is there + identify it */ + identity = ves1x93_readreg(state, 0x1e); + switch (identity) { + case 0xdc: /* VES1893A rev1 */ + printk("ves1x93: Detected ves1893a rev1\n"); + state->demod_type = DEMOD_VES1893; + state->init_1x93_tab = init_1893_tab; + state->init_1x93_wtab = init_1893_wtab; + state->tab_size = sizeof(init_1893_tab); + break; + + case 0xdd: /* VES1893A rev2 */ + printk("ves1x93: Detected ves1893a rev2\n"); + state->demod_type = DEMOD_VES1893; + state->init_1x93_tab = init_1893_tab; + state->init_1x93_wtab = init_1893_wtab; + state->tab_size = sizeof(init_1893_tab); + break; + + case 0xde: /* VES1993 */ + printk("ves1x93: Detected ves1993\n"); + state->demod_type = DEMOD_VES1993; + state->init_1x93_tab = init_1993_tab; + state->init_1x93_wtab = init_1993_wtab; + state->tab_size = sizeof(init_1993_tab); + break; + + default: + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops ves1x93_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "VLSI VES1x93 DVB-S", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .frequency_tolerance_hz = 29500 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* .symbol_rate_tolerance = ???,*/ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = ves1x93_release, + + .init = ves1x93_init, + .sleep = ves1x93_sleep, + .i2c_gate_ctrl = ves1x93_i2c_gate_ctrl, + + .set_frontend = ves1x93_set_frontend, + .get_frontend = ves1x93_get_frontend, + + .read_status = ves1x93_read_status, + .read_ber = ves1x93_read_ber, + .read_signal_strength = ves1x93_read_signal_strength, + .read_snr = ves1x93_read_snr, + .read_ucblocks = ves1x93_read_ucblocks, + + .set_voltage = ves1x93_set_voltage, +}; + +module_param(debug, int, 0644); + +MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver"); +MODULE_AUTHOR("Ralph Metzler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(ves1x93_attach); diff --git a/drivers/media/dvb-frontends/ves1x93.h b/drivers/media/dvb-frontends/ves1x93.h new file mode 100644 index 0000000000..c95ea75db4 --- /dev/null +++ b/drivers/media/dvb-frontends/ves1x93.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for VES1893 and VES1993 QPSK Demodulators + + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> + Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de> + Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.org> + + +*/ + +#ifndef VES1X93_H +#define VES1X93_H + +#include <linux/dvb/frontend.h> + +struct ves1x93_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* value of XIN to use */ + u32 xin; + + /* should PWM be inverted? */ + u8 invert_pwm:1; +}; + +#if IS_REACHABLE(CONFIG_DVB_VES1X93) +extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_VES1X93 + +#endif // VES1X93_H diff --git a/drivers/media/dvb-frontends/z0194a.h b/drivers/media/dvb-frontends/z0194a.h new file mode 100644 index 0000000000..3446ccbf3c --- /dev/null +++ b/drivers/media/dvb-frontends/z0194a.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* z0194a.h Sharp z0194a tuner support +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information +*/ + +#ifndef Z0194A +#define Z0194A + +static int sharp_z0194a_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; bclk = 0x47; } + else if (srate < 3000000) { + aclk = 0xb7; bclk = 0x4b; } + else if (srate < 7000000) { + aclk = 0xb7; bclk = 0x4f; } + else if (srate < 14000000) { + aclk = 0xb7; bclk = 0x53; } + else if (srate < 30000000) { + aclk = 0xb6; bclk = 0x53; } + else if (srate < 45000000) { + aclk = 0xb4; bclk = 0x51; } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static u8 sharp_z0194a_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, /* AGC2 0x3d */ + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, /* lock detector threshold */ + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, /* out imp: normal out type: parallel FEC mode:0 */ + 0x29, 0x1e, /* 1/2 threshold */ + 0x2a, 0x14, /* 2/3 threshold */ + 0x2b, 0x0f, /* 3/4 threshold */ + 0x2c, 0x09, /* 5/6 threshold */ + 0x2d, 0x05, /* 7/8 threshold */ + 0x2e, 0x01, + 0x31, 0x1f, /* test all FECs */ + 0x32, 0x19, /* viterbi and synchro search */ + 0x33, 0xfc, /* rs control */ + 0x34, 0x93, /* error control */ + 0x0f, 0x52, + 0xff, 0xff +}; + +#endif diff --git a/drivers/media/dvb-frontends/zd1301_demod.c b/drivers/media/dvb-frontends/zd1301_demod.c new file mode 100644 index 0000000000..17f6e373c1 --- /dev/null +++ b/drivers/media/dvb-frontends/zd1301_demod.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ZyDAS ZD1301 driver (demodulator) + * + * Copyright (C) 2015 Antti Palosaari <crope@iki.fi> + */ + +#include "zd1301_demod.h" + +static u8 zd1301_demod_gain = 0x38; +module_param_named(gain, zd1301_demod_gain, byte, 0644); +MODULE_PARM_DESC(gain, "gain (value: 0x00 - 0x70, default: 0x38)"); + +struct zd1301_demod_dev { + struct platform_device *pdev; + struct dvb_frontend frontend; + struct i2c_adapter adapter; + u8 gain; +}; + +static int zd1301_demod_wreg(struct zd1301_demod_dev *dev, u16 reg, u8 val) +{ + struct platform_device *pdev = dev->pdev; + struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data; + + return pdata->reg_write(pdata->reg_priv, reg, val); +} + +static int zd1301_demod_rreg(struct zd1301_demod_dev *dev, u16 reg, u8 *val) +{ + struct platform_device *pdev = dev->pdev; + struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data; + + return pdata->reg_read(pdata->reg_priv, reg, val); +} + +static int zd1301_demod_set_frontend(struct dvb_frontend *fe) +{ + struct zd1301_demod_dev *dev = fe->demodulator_priv; + struct platform_device *pdev = dev->pdev; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u32 if_frequency; + u8 r6a50_val; + + dev_dbg(&pdev->dev, "frequency=%u bandwidth_hz=%u\n", + c->frequency, c->bandwidth_hz); + + /* Program tuner */ + if (fe->ops.tuner_ops.set_params && + fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto err; + } else { + ret = -EINVAL; + goto err; + } + + dev_dbg(&pdev->dev, "if_frequency=%u\n", if_frequency); + if (if_frequency != 36150000) { + ret = -EINVAL; + goto err; + } + + switch (c->bandwidth_hz) { + case 6000000: + r6a50_val = 0x78; + break; + case 7000000: + r6a50_val = 0x68; + break; + case 8000000: + r6a50_val = 0x58; + break; + default: + ret = -EINVAL; + goto err; + } + + ret = zd1301_demod_wreg(dev, 0x6a60, 0x11); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a47, 0x46); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a48, 0x46); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a4a, 0x15); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a4b, 0x63); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a5b, 0x99); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a3b, 0x10); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6806, 0x01); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a41, 0x08); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a42, 0x46); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a44, 0x14); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a45, 0x67); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a38, 0x00); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a4c, 0x52); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a49, 0x2a); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6840, 0x2e); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a50, r6a50_val); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a38, 0x07); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&pdev->dev, "failed=%d\n", ret); + return ret; +} + +static int zd1301_demod_sleep(struct dvb_frontend *fe) +{ + struct zd1301_demod_dev *dev = fe->demodulator_priv; + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + ret = zd1301_demod_wreg(dev, 0x6a43, 0x70); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x684e, 0x00); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6849, 0x00); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x68e2, 0xd7); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x68e0, 0x39); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6840, 0x21); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&pdev->dev, "failed=%d\n", ret); + return ret; +} + +static int zd1301_demod_init(struct dvb_frontend *fe) +{ + struct zd1301_demod_dev *dev = fe->demodulator_priv; + struct platform_device *pdev = dev->pdev; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + ret = zd1301_demod_wreg(dev, 0x6840, 0x26); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x68e0, 0xff); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x68e2, 0xd8); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6849, 0x4e); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x684e, 0x01); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6a43, zd1301_demod_gain); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&pdev->dev, "failed=%d\n", ret); + return ret; +} + +static int zd1301_demod_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *settings) +{ + struct zd1301_demod_dev *dev = fe->demodulator_priv; + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "\n"); + + /* ~180ms seems to be enough */ + settings->min_delay_ms = 400; + + return 0; +} + +static int zd1301_demod_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct zd1301_demod_dev *dev = fe->demodulator_priv; + struct platform_device *pdev = dev->pdev; + int ret; + u8 u8tmp; + + ret = zd1301_demod_rreg(dev, 0x6a24, &u8tmp); + if (ret) + goto err; + if (u8tmp > 0x00 && u8tmp < 0x20) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + + dev_dbg(&pdev->dev, "lock byte=%02x\n", u8tmp); + + /* + * Interesting registers here are: + * 0x6a05: get some gain value + * 0x6a06: get about same gain value than set to 0x6a43 + * 0x6a07: get some gain value + * 0x6a43: set gain value by driver + * 0x6a24: get demod lock bits (FSM stage?) + * + * Driver should implement some kind of algorithm to calculate suitable + * value for register 0x6a43, based likely values from register 0x6a05 + * and 0x6a07. Looks like gain register 0x6a43 value could be from + * range 0x00 - 0x70. + */ + + if (dev->gain != zd1301_demod_gain) { + dev->gain = zd1301_demod_gain; + + ret = zd1301_demod_wreg(dev, 0x6a43, dev->gain); + if (ret) + goto err; + } + + return 0; +err: + dev_dbg(&pdev->dev, "failed=%d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops zd1301_demod_ops = { + .delsys = {SYS_DVBT}, + .info = { + .name = "ZyDAS ZD1301", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS + }, + + .sleep = zd1301_demod_sleep, + .init = zd1301_demod_init, + .set_frontend = zd1301_demod_set_frontend, + .get_tune_settings = zd1301_demod_get_tune_settings, + .read_status = zd1301_demod_read_status, +}; + +struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *pdev) +{ + struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "\n"); + + return &dev->frontend; +} +EXPORT_SYMBOL(zd1301_demod_get_dvb_frontend); + +static int zd1301_demod_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msg[], int num) +{ + struct zd1301_demod_dev *dev = i2c_get_adapdata(adapter); + struct platform_device *pdev = dev->pdev; + int ret, i; + unsigned long timeout; + u8 u8tmp; + + #define I2C_XFER_TIMEOUT 5 + #define ZD1301_IS_I2C_XFER_WRITE_READ(_msg, _num) \ + (_num == 2 && !(_msg[0].flags & I2C_M_RD) && (_msg[1].flags & I2C_M_RD)) + #define ZD1301_IS_I2C_XFER_WRITE(_msg, _num) \ + (_num == 1 && !(_msg[0].flags & I2C_M_RD)) + #define ZD1301_IS_I2C_XFER_READ(_msg, _num) \ + (_num == 1 && (_msg[0].flags & I2C_M_RD)) + if (ZD1301_IS_I2C_XFER_WRITE_READ(msg, num)) { + dev_dbg(&pdev->dev, "write&read msg[0].len=%u msg[1].len=%u\n", + msg[0].len, msg[1].len); + if (msg[0].len > 1 || msg[1].len > 8) { + ret = -EOPNOTSUPP; + goto err; + } + + ret = zd1301_demod_wreg(dev, 0x6811, 0x80); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6812, 0x05); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6813, msg[1].addr << 1); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6801, msg[0].buf[0]); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6802, 0x00); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6803, 0x06); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6805, 0x00); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6804, msg[1].len); + if (ret) + goto err; + + /* Poll xfer ready */ + timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT); + for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) { + usleep_range(500, 800); + + ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp); + if (ret) + goto err; + } + + for (i = 0; i < msg[1].len; i++) { + ret = zd1301_demod_rreg(dev, 0x0600 + i, &msg[1].buf[i]); + if (ret) + goto err; + } + } else if (ZD1301_IS_I2C_XFER_WRITE(msg, num)) { + dev_dbg(&pdev->dev, "write msg[0].len=%u\n", msg[0].len); + if (msg[0].len > 1 + 8) { + ret = -EOPNOTSUPP; + goto err; + } + + ret = zd1301_demod_wreg(dev, 0x6811, 0x80); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6812, 0x01); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6813, msg[0].addr << 1); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6800, msg[0].buf[0]); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6802, 0x00); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6803, 0x06); + if (ret) + goto err; + + for (i = 0; i < msg[0].len - 1; i++) { + ret = zd1301_demod_wreg(dev, 0x0600 + i, msg[0].buf[1 + i]); + if (ret) + goto err; + } + + ret = zd1301_demod_wreg(dev, 0x6805, 0x80); + if (ret) + goto err; + ret = zd1301_demod_wreg(dev, 0x6804, msg[0].len - 1); + if (ret) + goto err; + + /* Poll xfer ready */ + timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT); + for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) { + usleep_range(500, 800); + + ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp); + if (ret) + goto err; + } + } else { + dev_dbg(&pdev->dev, "unknown msg[0].len=%u\n", msg[0].len); + ret = -EOPNOTSUPP; + goto err; + } + + return num; +err: + dev_dbg(&pdev->dev, "failed=%d\n", ret); + return ret; +} + +static u32 zd1301_demod_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm zd1301_demod_i2c_algorithm = { + .master_xfer = zd1301_demod_i2c_master_xfer, + .functionality = zd1301_demod_i2c_functionality, +}; + +struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *pdev) +{ + struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "\n"); + + return &dev->adapter; +} +EXPORT_SYMBOL(zd1301_demod_get_i2c_adapter); + +/* Platform driver interface */ +static int zd1301_demod_probe(struct platform_device *pdev) +{ + struct zd1301_demod_dev *dev; + struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data; + int ret; + + dev_dbg(&pdev->dev, "\n"); + + if (!pdata) { + ret = -EINVAL; + dev_err(&pdev->dev, "cannot proceed without platform data\n"); + goto err; + } + if (!pdev->dev.parent->driver) { + ret = -EINVAL; + dev_dbg(&pdev->dev, "no parent device\n"); + goto err; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + /* Setup the state */ + dev->pdev = pdev; + dev->gain = zd1301_demod_gain; + + /* Sleep */ + ret = zd1301_demod_wreg(dev, 0x6840, 0x21); + if (ret) + goto err_kfree; + ret = zd1301_demod_wreg(dev, 0x6a38, 0x07); + if (ret) + goto err_kfree; + + /* Create I2C adapter */ + strscpy(dev->adapter.name, "ZyDAS ZD1301 demod", + sizeof(dev->adapter.name)); + dev->adapter.algo = &zd1301_demod_i2c_algorithm; + dev->adapter.algo_data = NULL; + dev->adapter.dev.parent = pdev->dev.parent; + i2c_set_adapdata(&dev->adapter, dev); + ret = i2c_add_adapter(&dev->adapter); + if (ret) { + dev_err(&pdev->dev, "I2C adapter add failed %d\n", ret); + goto err_kfree; + } + + /* Create dvb frontend */ + memcpy(&dev->frontend.ops, &zd1301_demod_ops, sizeof(dev->frontend.ops)); + dev->frontend.demodulator_priv = dev; + platform_set_drvdata(pdev, dev); + dev_info(&pdev->dev, "ZyDAS ZD1301 demod attached\n"); + + return 0; +err_kfree: + kfree(dev); +err: + dev_dbg(&pdev->dev, "failed=%d\n", ret); + return ret; +} + +static void zd1301_demod_remove(struct platform_device *pdev) +{ + struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "\n"); + + i2c_del_adapter(&dev->adapter); + kfree(dev); +} + +static struct platform_driver zd1301_demod_driver = { + .driver = { + .name = "zd1301_demod", + .suppress_bind_attrs = true, + }, + .probe = zd1301_demod_probe, + .remove_new = zd1301_demod_remove, +}; +module_platform_driver(zd1301_demod_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("ZyDAS ZD1301 demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/zd1301_demod.h b/drivers/media/dvb-frontends/zd1301_demod.h new file mode 100644 index 0000000000..01eaacf76a --- /dev/null +++ b/drivers/media/dvb-frontends/zd1301_demod.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ZyDAS ZD1301 driver (demodulator) + * + * Copyright (C) 2015 Antti Palosaari <crope@iki.fi> + */ + +#ifndef ZD1301_DEMOD_H +#define ZD1301_DEMOD_H + +#include <linux/platform_device.h> +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +/** + * struct zd1301_demod_platform_data - Platform data for the zd1301_demod driver + * @reg_priv: First argument of reg_read and reg_write callbacks. + * @reg_read: Register read callback. + * @reg_write: Register write callback. + */ +struct zd1301_demod_platform_data { + void *reg_priv; + int (*reg_read)(void *, u16, u8 *); + int (*reg_write)(void *, u16, u8); +}; + +#if IS_REACHABLE(CONFIG_DVB_ZD1301_DEMOD) +/** + * zd1301_demod_get_dvb_frontend() - Get pointer to DVB frontend + * @pdev: Pointer to platform device + * + * Return: Pointer to DVB frontend which given platform device owns. + */ +struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *pdev); + +/** + * zd1301_demod_get_i2c_adapter() - Get pointer to I2C adapter + * @pdev: Pointer to platform device + * + * Return: Pointer to I2C adapter which given platform device owns. + */ +struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *pdev); + +#else + +static inline struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *dev) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + + return NULL; +} +static inline struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *dev) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + + return NULL; +} + +#endif + +#endif /* ZD1301_DEMOD_H */ diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c new file mode 100644 index 0000000000..7ba575e9c5 --- /dev/null +++ b/drivers/media/dvb-frontends/zl10036.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Zarlink zl10036 DVB-S silicon tuner + * + * Copyright (C) 2006 Tino Reichardt + * Copyright (C) 2007-2009 Matthias Schwarzott <zzam@gentoo.de> + * + ** + * The data sheet for this tuner can be found at: + * http://www.mcmilk.de/projects/dvb-card/datasheets/ZL10036.pdf + * + * This one is working: (at my Avermedia DVB-S Pro) + * - zl10036 (40pin, FTA) + * + * A driver for zl10038 should be very similar. + */ + +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "zl10036.h" + +static int zl10036_debug; +#define dprintk(level, args...) \ + do { if (zl10036_debug & level) printk(KERN_DEBUG "zl10036: " args); \ + } while (0) + +#define deb_info(args...) dprintk(0x01, args) +#define deb_i2c(args...) dprintk(0x02, args) + +struct zl10036_state { + struct i2c_adapter *i2c; + const struct zl10036_config *config; + u32 frequency; + u8 br, bf; +}; + + +/* This driver assumes the tuner is driven by a 10.111MHz Cristal */ +#define _XTAL 10111 + +/* Some of the possible dividers: + * 64, (write 0x05 to reg), freq step size 158kHz + * 10, (write 0x0a to reg), freq step size 1.011kHz (used here) + * 5, (write 0x09 to reg), freq step size 2.022kHz + */ + +#define _RDIV 10 +#define _RDIV_REG 0x0a +#define _FR (_XTAL/_RDIV) + +#define STATUS_POR 0x80 /* Power on Reset */ +#define STATUS_FL 0x40 /* Frequency & Phase Lock */ + +/* read/write for zl10036 and zl10038 */ + +static int zl10036_read_status_reg(struct zl10036_state *state) +{ + u8 status; + struct i2c_msg msg[1] = { + { .addr = state->config->tuner_address, .flags = I2C_M_RD, + .buf = &status, .len = sizeof(status) }, + }; + + if (i2c_transfer(state->i2c, msg, 1) != 1) { + printk(KERN_ERR "%s: i2c read failed at addr=%02x\n", + __func__, state->config->tuner_address); + return -EIO; + } + + deb_i2c("R(status): %02x [FL=%d]\n", status, + (status & STATUS_FL) ? 1 : 0); + if (status & STATUS_POR) + deb_info("%s: Power-On-Reset bit enabled - need to initialize the tuner\n", + __func__); + + return status; +} + +static int zl10036_write(struct zl10036_state *state, u8 buf[], u8 count) +{ + struct i2c_msg msg[1] = { + { .addr = state->config->tuner_address, .flags = 0, + .buf = buf, .len = count }, + }; + u8 reg = 0; + int ret; + + if (zl10036_debug & 0x02) { + /* every 8bit-value satisifes this! + * so only check for debug log */ + if ((buf[0] & 0x80) == 0x00) + reg = 2; + else if ((buf[0] & 0xc0) == 0x80) + reg = 4; + else if ((buf[0] & 0xf0) == 0xc0) + reg = 6; + else if ((buf[0] & 0xf0) == 0xd0) + reg = 8; + else if ((buf[0] & 0xf0) == 0xe0) + reg = 10; + else if ((buf[0] & 0xf0) == 0xf0) + reg = 12; + + deb_i2c("W(%d):", reg); + { + int i; + for (i = 0; i < count; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + ret = i2c_transfer(state->i2c, msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s: i2c error, ret=%d\n", __func__, ret); + return -EIO; + } + + return 0; +} + +static void zl10036_release(struct dvb_frontend *fe) +{ + struct zl10036_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); +} + +static int zl10036_sleep(struct dvb_frontend *fe) +{ + struct zl10036_state *state = fe->tuner_priv; + u8 buf[] = { 0xf0, 0x80 }; /* regs 12/13 */ + int ret; + + deb_info("%s\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = zl10036_write(state, buf, sizeof(buf)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return ret; +} + +/* + * register map of the ZL10036/ZL10038 + * + * reg[default] content + * 2[0x00]: 0 | N14 | N13 | N12 | N11 | N10 | N9 | N8 + * 3[0x00]: N7 | N6 | N5 | N4 | N3 | N2 | N1 | N0 + * 4[0x80]: 1 | 0 | RFG | BA1 | BA0 | BG1 | BG0 | LEN + * 5[0x00]: P0 | C1 | C0 | R4 | R3 | R2 | R1 | R0 + * 6[0xc0]: 1 | 1 | 0 | 0 | RSD | 0 | 0 | 0 + * 7[0x20]: P1 | BF6 | BF5 | BF4 | BF3 | BF2 | BF1 | 0 + * 8[0xdb]: 1 | 1 | 0 | 1 | 0 | CC | 1 | 1 + * 9[0x30]: VSD | V2 | V1 | V0 | S3 | S2 | S1 | S0 + * 10[0xe1]: 1 | 1 | 1 | 0 | 0 | LS2 | LS1 | LS0 + * 11[0xf5]: WS | WH2 | WH1 | WH0 | WL2 | WL1 | WL0 | WRE + * 12[0xf0]: 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 + * 13[0x28]: PD | BR4 | BR3 | BR2 | BR1 | BR0 | CLR | TL + */ + +static int zl10036_set_frequency(struct zl10036_state *state, u32 frequency) +{ + u8 buf[2]; + u32 div, foffset; + + div = (frequency + _FR/2) / _FR; + state->frequency = div * _FR; + + foffset = frequency - state->frequency; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = (div >> 0) & 0xff; + + deb_info("%s: ftodo=%u fpriv=%u ferr=%d div=%u\n", __func__, + frequency, state->frequency, foffset, div); + + return zl10036_write(state, buf, sizeof(buf)); +} + +static int zl10036_set_bandwidth(struct zl10036_state *state, u32 fbw) +{ + /* fbw is measured in kHz */ + u8 br, bf; + int ret; + u8 buf_bf[] = { + 0xc0, 0x00, /* 6/7: rsd=0 bf=0 */ + }; + u8 buf_br[] = { + 0xf0, 0x00, /* 12/13: br=0xa clr=0 tl=0*/ + }; + u8 zl10036_rsd_off[] = { 0xc8 }; /* set RSD=1 */ + + /* ensure correct values */ + if (fbw > 35000) + fbw = 35000; + if (fbw < 8000) + fbw = 8000; + +#define _BR_MAXIMUM (_XTAL/575) /* _XTAL / 575kHz = 17 */ + + /* <= 28,82 MHz */ + if (fbw <= 28820) { + br = _BR_MAXIMUM; + } else { + /* + * f(bw)=34,6MHz f(xtal)=10.111MHz + * br = (10111/34600) * 63 * 1/K = 14; + */ + br = ((_XTAL * 21 * 1000) / (fbw * 419)); + } + + /* ensure correct values */ + if (br < 4) + br = 4; + if (br > _BR_MAXIMUM) + br = _BR_MAXIMUM; + + /* + * k = 1.257 + * bf = fbw/_XTAL * br * k - 1 */ + + bf = (fbw * br * 1257) / (_XTAL * 1000) - 1; + + /* ensure correct values */ + if (bf > 62) + bf = 62; + + buf_bf[1] = (bf << 1) & 0x7e; + buf_br[1] = (br << 2) & 0x7c; + deb_info("%s: BW=%d br=%u bf=%u\n", __func__, fbw, br, bf); + + if (br != state->br) { + ret = zl10036_write(state, buf_br, sizeof(buf_br)); + if (ret < 0) + return ret; + } + + if (bf != state->bf) { + ret = zl10036_write(state, buf_bf, sizeof(buf_bf)); + if (ret < 0) + return ret; + + /* time = br/(32* fxtal) */ + /* minimal sleep time to be calculated + * maximum br is 63 -> max time = 2 /10 MHz = 2e-7 */ + msleep(1); + + ret = zl10036_write(state, zl10036_rsd_off, + sizeof(zl10036_rsd_off)); + if (ret < 0) + return ret; + } + + state->br = br; + state->bf = bf; + + return 0; +} + +static int zl10036_set_gain_params(struct zl10036_state *state, + int c) +{ + u8 buf[2]; + u8 rfg, ba, bg; + + /* default values */ + rfg = 0; /* enable when using an lna */ + ba = 1; + bg = 1; + + /* reg 4 */ + buf[0] = 0x80 | ((rfg << 5) & 0x20) + | ((ba << 3) & 0x18) | ((bg << 1) & 0x06); + + if (!state->config->rf_loop_enable) + buf[0] |= 0x01; + + /* P0=0 */ + buf[1] = _RDIV_REG | ((c << 5) & 0x60); + + deb_info("%s: c=%u rfg=%u ba=%u bg=%u\n", __func__, c, rfg, ba, bg); + return zl10036_write(state, buf, sizeof(buf)); +} + +static int zl10036_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct zl10036_state *state = fe->tuner_priv; + int ret = 0; + u32 frequency = p->frequency; + u32 fbw; + int i; + u8 c; + + /* ensure correct values + * maybe redundant as core already checks this */ + if ((frequency < fe->ops.info.frequency_min_hz / kHz) + || (frequency > fe->ops.info.frequency_max_hz / kHz)) + return -EINVAL; + + /* + * alpha = 1.35 for dvb-s + * fBW = (alpha*symbolrate)/(2*0.8) + * 1.35 / (2*0.8) = 27 / 32 + */ + fbw = (27 * p->symbol_rate) / 32; + + /* scale to kHz */ + fbw /= 1000; + + /* Add safe margin of 3MHz */ + fbw += 3000; + + /* setting the charge pump - guessed values */ + if (frequency < 950000) + return -EINVAL; + else if (frequency < 1250000) + c = 0; + else if (frequency < 1750000) + c = 1; + else if (frequency < 2175000) + c = 2; + else + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = zl10036_set_gain_params(state, c); + if (ret < 0) + goto error; + + ret = zl10036_set_frequency(state, p->frequency); + if (ret < 0) + goto error; + + ret = zl10036_set_bandwidth(state, fbw); + if (ret < 0) + goto error; + + /* wait for tuner lock - no idea if this is really needed */ + for (i = 0; i < 20; i++) { + ret = zl10036_read_status_reg(state); + if (ret < 0) + goto error; + + /* check Frequency & Phase Lock Bit */ + if (ret & STATUS_FL) + break; + + msleep(10); + } + +error: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return ret; +} + +static int zl10036_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct zl10036_state *state = fe->tuner_priv; + + *frequency = state->frequency; + + return 0; +} + +static int zl10036_init_regs(struct zl10036_state *state) +{ + int ret; + int i; + + /* could also be one block from reg 2 to 13 and additional 10/11 */ + u8 zl10036_init_tab[][2] = { + { 0x04, 0x00 }, /* 2/3: div=0x400 - arbitrary value */ + { 0x8b, _RDIV_REG }, /* 4/5: rfg=0 ba=1 bg=1 len=? */ + /* p0=0 c=0 r=_RDIV_REG */ + { 0xc0, 0x20 }, /* 6/7: rsd=0 bf=0x10 */ + { 0xd3, 0x40 }, /* 8/9: from datasheet */ + { 0xe3, 0x5b }, /* 10/11: lock window level */ + { 0xf0, 0x28 }, /* 12/13: br=0xa clr=0 tl=0*/ + { 0xe3, 0xf9 }, /* 10/11: unlock window level */ + }; + + /* invalid values to trigger writing */ + state->br = 0xff; + state->bf = 0xff; + + if (!state->config->rf_loop_enable) + zl10036_init_tab[1][0] |= 0x01; + + deb_info("%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(zl10036_init_tab); i++) { + ret = zl10036_write(state, zl10036_init_tab[i], 2); + if (ret < 0) + return ret; + } + + return 0; +} + +static int zl10036_init(struct dvb_frontend *fe) +{ + struct zl10036_state *state = fe->tuner_priv; + int ret = 0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = zl10036_read_status_reg(state); + if (ret < 0) + return ret; + + /* Only init if Power-on-Reset bit is set? */ + ret = zl10036_init_regs(state); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return ret; +} + +static const struct dvb_tuner_ops zl10036_tuner_ops = { + .info = { + .name = "Zarlink ZL10036", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz + }, + .init = zl10036_init, + .release = zl10036_release, + .sleep = zl10036_sleep, + .set_params = zl10036_set_params, + .get_frequency = zl10036_get_frequency, +}; + +struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, + const struct zl10036_config *config, + struct i2c_adapter *i2c) +{ + struct zl10036_state *state; + int ret; + + if (!config) { + printk(KERN_ERR "%s: no config specified", __func__); + return NULL; + } + + state = kzalloc(sizeof(struct zl10036_state), GFP_KERNEL); + if (!state) + return NULL; + + state->config = config; + state->i2c = i2c; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = zl10036_read_status_reg(state); + if (ret < 0) { + printk(KERN_ERR "%s: No zl10036 found\n", __func__); + goto error; + } + + ret = zl10036_init_regs(state); + if (ret < 0) { + printk(KERN_ERR "%s: tuner initialization failed\n", + __func__); + goto error; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + fe->tuner_priv = state; + + memcpy(&fe->ops.tuner_ops, &zl10036_tuner_ops, + sizeof(struct dvb_tuner_ops)); + printk(KERN_INFO "%s: tuner initialization (%s addr=0x%02x) ok\n", + __func__, fe->ops.tuner_ops.info.name, config->tuner_address); + + return fe; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(zl10036_attach); + +module_param_named(debug, zl10036_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +MODULE_DESCRIPTION("DVB ZL10036 driver"); +MODULE_AUTHOR("Tino Reichardt"); +MODULE_AUTHOR("Matthias Schwarzott"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/zl10036.h b/drivers/media/dvb-frontends/zl10036.h new file mode 100644 index 0000000000..ad83e6344e --- /dev/null +++ b/drivers/media/dvb-frontends/zl10036.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Driver for Zarlink ZL10036 DVB-S silicon tuner + * + * Copyright (C) 2006 Tino Reichardt + * Copyright (C) 2007-2009 Matthias Schwarzott <zzam@gentoo.de> + */ + +#ifndef DVB_ZL10036_H +#define DVB_ZL10036_H + +#include <linux/i2c.h> +#include <media/dvb_frontend.h> + +struct zl10036_config { + u8 tuner_address; + int rf_loop_enable; +}; + +#if IS_REACHABLE(CONFIG_DVB_ZL10036) +/** + * zl10036_attach - Attach a zl10036 tuner to the supplied frontend structure. + * + * @fe: Frontend to attach to. + * @config: zl10036_config structure. + * @i2c: pointer to struct i2c_adapter. + * return: FE pointer on success, NULL on failure. + */ +extern struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, + const struct zl10036_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, + const struct zl10036_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* DVB_ZL10036_H */ diff --git a/drivers/media/dvb-frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c new file mode 100644 index 0000000000..a3e4d21940 --- /dev/null +++ b/drivers/media/dvb-frontends/zl10039.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Zarlink ZL10039 DVB-S tuner + * + * Copyright 2007 Jan D. Louw <jd.louw@mweb.co.za> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/dvb/frontend.h> + +#include <media/dvb_frontend.h> +#include "zl10039.h" + +static int debug; + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE 64 + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG args); \ + } while (0) + +enum zl10039_model_id { + ID_ZL10039 = 1 +}; + +struct zl10039_state { + struct i2c_adapter *i2c; + u8 i2c_addr; + u8 id; +}; + +enum zl10039_reg_addr { + PLL0 = 0, + PLL1, + PLL2, + PLL3, + RFFE, + BASE0, + BASE1, + BASE2, + LO0, + LO1, + LO2, + LO3, + LO4, + LO5, + LO6, + GENERAL +}; + +static int zl10039_read(const struct zl10039_state *state, + const enum zl10039_reg_addr reg, u8 *buf, + const size_t count) +{ + u8 regbuf[] = { reg }; + struct i2c_msg msg[] = { + {/* Write register address */ + .addr = state->i2c_addr, + .flags = 0, + .buf = regbuf, + .len = 1, + }, {/* Read count bytes */ + .addr = state->i2c_addr, + .flags = I2C_M_RD, + .buf = buf, + .len = count, + }, + }; + + dprintk("%s\n", __func__); + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + dprintk("%s: i2c read error\n", __func__); + return -EREMOTEIO; + } + + return 0; /* Success */ +} + +static int zl10039_write(struct zl10039_state *state, + const enum zl10039_reg_addr reg, const u8 *src, + const size_t count) +{ + u8 buf[MAX_XFER_SIZE]; + struct i2c_msg msg = { + .addr = state->i2c_addr, + .flags = 0, + .buf = buf, + .len = count + 1, + }; + + if (1 + count > sizeof(buf)) { + printk(KERN_WARNING + "%s: i2c wr reg=%04x: len=%zu is too big!\n", + KBUILD_MODNAME, reg, count); + return -EINVAL; + } + + dprintk("%s\n", __func__); + /* Write register address and data in one go */ + buf[0] = reg; + memcpy(&buf[1], src, count); + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + dprintk("%s: i2c write error\n", __func__); + return -EREMOTEIO; + } + + return 0; /* Success */ +} + +static inline int zl10039_readreg(struct zl10039_state *state, + const enum zl10039_reg_addr reg, u8 *val) +{ + return zl10039_read(state, reg, val, 1); +} + +static inline int zl10039_writereg(struct zl10039_state *state, + const enum zl10039_reg_addr reg, + const u8 val) +{ + const u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */ + + return zl10039_write(state, reg, &tmp, 1); +} + +static int zl10039_init(struct dvb_frontend *fe) +{ + struct zl10039_state *state = fe->tuner_priv; + int ret; + + dprintk("%s\n", __func__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + /* Reset logic */ + ret = zl10039_writereg(state, GENERAL, 0x40); + if (ret < 0) { + dprintk("Note: i2c write error normal when resetting the tuner\n"); + } + /* Wake up */ + ret = zl10039_writereg(state, GENERAL, 0x01); + if (ret < 0) { + dprintk("Tuner power up failed\n"); + return ret; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int zl10039_sleep(struct dvb_frontend *fe) +{ + struct zl10039_state *state = fe->tuner_priv; + int ret; + + dprintk("%s\n", __func__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = zl10039_writereg(state, GENERAL, 0x80); + if (ret < 0) { + dprintk("Tuner sleep failed\n"); + return ret; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int zl10039_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct zl10039_state *state = fe->tuner_priv; + u8 buf[6]; + u8 bf; + u32 fbw; + u32 div; + int ret; + + dprintk("%s\n", __func__); + dprintk("Set frequency = %d, symbol rate = %d\n", + c->frequency, c->symbol_rate); + + /* Assumed 10.111 MHz crystal oscillator */ + /* Cancelled num/den 80 to prevent overflow */ + div = (c->frequency * 1000) / 126387; + fbw = (c->symbol_rate * 27) / 32000; + /* Cancelled num/den 10 to prevent overflow */ + bf = ((fbw * 5088) / 1011100) - 1; + + /*PLL divider*/ + buf[0] = (div >> 8) & 0x7f; + buf[1] = (div >> 0) & 0xff; + /*Reference divider*/ + /* Select reference ratio of 80 */ + buf[2] = 0x1D; + /*PLL test modes*/ + buf[3] = 0x40; + /*RF Control register*/ + buf[4] = 0x6E; /* Bypass enable */ + /*Baseband filter cutoff */ + buf[5] = bf; + + /* Open i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + /* BR = 10, Enable filter adjustment */ + ret = zl10039_writereg(state, BASE1, 0x0A); + if (ret < 0) + goto error; + /* Write new config values */ + ret = zl10039_write(state, PLL0, buf, sizeof(buf)); + if (ret < 0) + goto error; + /* BR = 10, Disable filter adjustment */ + ret = zl10039_writereg(state, BASE1, 0x6A); + if (ret < 0) + goto error; + + /* Close i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + return 0; +error: + dprintk("Error setting tuner\n"); + return ret; +} + +static void zl10039_release(struct dvb_frontend *fe) +{ + struct zl10039_state *state = fe->tuner_priv; + + dprintk("%s\n", __func__); + kfree(state); + fe->tuner_priv = NULL; +} + +static const struct dvb_tuner_ops zl10039_ops = { + .release = zl10039_release, + .init = zl10039_init, + .sleep = zl10039_sleep, + .set_params = zl10039_set_params, +}; + +struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, + u8 i2c_addr, struct i2c_adapter *i2c) +{ + struct zl10039_state *state = NULL; + + dprintk("%s\n", __func__); + state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->i2c = i2c; + state->i2c_addr = i2c_addr; + + /* Open i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + /* check if this is a valid tuner */ + if (zl10039_readreg(state, GENERAL, &state->id) < 0) { + /* Close i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + goto error; + } + /* Close i2c gate */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + state->id = state->id & 0x0f; + switch (state->id) { + case ID_ZL10039: + strscpy(fe->ops.tuner_ops.info.name, + "Zarlink ZL10039 DVB-S tuner", + sizeof(fe->ops.tuner_ops.info.name)); + break; + default: + dprintk("Chip ID=%x does not match a known type\n", state->id); + goto error; + } + + memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = state; + dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr); + return fe; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(zl10039_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver"); +MODULE_AUTHOR("Jan D. Louw <jd.louw@mweb.co.za>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/zl10039.h b/drivers/media/dvb-frontends/zl10039.h new file mode 100644 index 0000000000..a7fcdfba80 --- /dev/null +++ b/drivers/media/dvb-frontends/zl10039.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for Zarlink ZL10039 DVB-S tuner + + Copyright (C) 2007 Jan D. Louw <jd.louw@mweb.co.za> + +*/ + +#ifndef ZL10039_H +#define ZL10039_H + +#if IS_REACHABLE(CONFIG_DVB_ZL10039) +struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, + u8 i2c_addr, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, + u8 i2c_addr, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ZL10039 */ + +#endif /* ZL10039_H */ diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c new file mode 100644 index 0000000000..8849d05475 --- /dev/null +++ b/drivers/media/dvb-frontends/zl10353.c @@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Zarlink DVB-T ZL10353 demodulator + * + * Copyright (C) 2006, 2007 Christopher Pascoe <c.pascoe@itee.uq.edu.au> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <asm/div64.h> + +#include <media/dvb_frontend.h> +#include "zl10353_priv.h" +#include "zl10353.h" + +struct zl10353_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + + struct zl10353_config config; + + u32 bandwidth; + u32 ucblocks; + u32 frequency; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "zl10353: " args); \ + } while (0) + +static int debug_regs; + +static int zl10353_single_write(struct dvb_frontend *fe, u8 reg, u8 val) +{ + struct zl10353_state *state = fe->demodulator_priv; + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, + .buf = buf, .len = 2 }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk("zl10353: write to reg %x failed (err = %d)!\n", reg, err); + return err; + } + return 0; +} + +static int zl10353_write(struct dvb_frontend *fe, const u8 ibuf[], int ilen) +{ + int err, i; + for (i = 0; i < ilen - 1; i++) + if ((err = zl10353_single_write(fe, ibuf[0] + i, ibuf[i + 1]))) + return err; + + return 0; +} + +static int zl10353_read_register(struct zl10353_state *state, u8 reg) +{ + int ret; + u8 b0[1] = { reg }; + u8 b1[1] = { 0 }; + struct i2c_msg msg[2] = { { .addr = state->config.demod_address, + .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config.demod_address, + .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk("%s: readreg error (reg=%d, ret==%i)\n", + __func__, reg, ret); + return ret; + } + + return b1[0]; +} + +static void zl10353_dump_regs(struct dvb_frontend *fe) +{ + struct zl10353_state *state = fe->demodulator_priv; + int ret; + u8 reg; + + /* Dump all registers. */ + for (reg = 0; ; reg++) { + if (reg % 16 == 0) { + if (reg) + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); + } + ret = zl10353_read_register(state, reg); + if (ret >= 0) + printk(KERN_CONT " %02x", (u8)ret); + else + printk(KERN_CONT " --"); + if (reg == 0xff) + break; + } + printk(KERN_CONT "\n"); +} + +static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, + u32 bandwidth, + u16 *nominal_rate) +{ + struct zl10353_state *state = fe->demodulator_priv; + u32 adc_clock = 450560; /* 45.056 MHz */ + u64 value; + u8 bw = bandwidth / 1000000; + + if (state->config.adc_clock) + adc_clock = state->config.adc_clock; + + value = (u64)10 * (1 << 23) / 7 * 125; + value = (bw * value) + adc_clock / 2; + *nominal_rate = div_u64(value, adc_clock); + + dprintk("%s: bw %d, adc_clock %d => 0x%x\n", + __func__, bw, adc_clock, *nominal_rate); +} + +static void zl10353_calc_input_freq(struct dvb_frontend *fe, + u16 *input_freq) +{ + struct zl10353_state *state = fe->demodulator_priv; + u32 adc_clock = 450560; /* 45.056 MHz */ + int if2 = 361667; /* 36.1667 MHz */ + int ife; + u64 value; + + if (state->config.adc_clock) + adc_clock = state->config.adc_clock; + if (state->config.if2) + if2 = state->config.if2; + + if (adc_clock >= if2 * 2) + ife = if2; + else { + ife = adc_clock - (if2 % adc_clock); + if (ife > adc_clock / 2) + ife = adc_clock - ife; + } + value = div_u64((u64)65536 * ife + adc_clock / 2, adc_clock); + *input_freq = -value; + + dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", + __func__, if2, ife, adc_clock, -(int)value, *input_freq); +} + +static int zl10353_sleep(struct dvb_frontend *fe) +{ + static u8 zl10353_softdown[] = { 0x50, 0x0C, 0x44 }; + + zl10353_write(fe, zl10353_softdown, sizeof(zl10353_softdown)); + return 0; +} + +static int zl10353_set_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct zl10353_state *state = fe->demodulator_priv; + u16 nominal_rate, input_freq; + u8 pllbuf[6] = { 0x67 }, acq_ctl = 0; + u16 tps = 0; + + state->frequency = c->frequency; + + zl10353_single_write(fe, RESET, 0x80); + udelay(200); + zl10353_single_write(fe, 0xEA, 0x01); + udelay(200); + zl10353_single_write(fe, 0xEA, 0x00); + + zl10353_single_write(fe, AGC_TARGET, 0x28); + + if (c->transmission_mode != TRANSMISSION_MODE_AUTO) + acq_ctl |= (1 << 0); + if (c->guard_interval != GUARD_INTERVAL_AUTO) + acq_ctl |= (1 << 1); + zl10353_single_write(fe, ACQ_CTL, acq_ctl); + + switch (c->bandwidth_hz) { + case 6000000: + /* These are extrapolated from the 7 and 8MHz values */ + zl10353_single_write(fe, MCLK_RATIO, 0x97); + zl10353_single_write(fe, 0x64, 0x34); + zl10353_single_write(fe, 0xcc, 0xdd); + break; + case 7000000: + zl10353_single_write(fe, MCLK_RATIO, 0x86); + zl10353_single_write(fe, 0x64, 0x35); + zl10353_single_write(fe, 0xcc, 0x73); + break; + default: + c->bandwidth_hz = 8000000; + fallthrough; + case 8000000: + zl10353_single_write(fe, MCLK_RATIO, 0x75); + zl10353_single_write(fe, 0x64, 0x36); + zl10353_single_write(fe, 0xcc, 0x73); + } + + zl10353_calc_nominal_rate(fe, c->bandwidth_hz, &nominal_rate); + zl10353_single_write(fe, TRL_NOMINAL_RATE_1, msb(nominal_rate)); + zl10353_single_write(fe, TRL_NOMINAL_RATE_0, lsb(nominal_rate)); + state->bandwidth = c->bandwidth_hz; + + zl10353_calc_input_freq(fe, &input_freq); + zl10353_single_write(fe, INPUT_FREQ_1, msb(input_freq)); + zl10353_single_write(fe, INPUT_FREQ_0, lsb(input_freq)); + + /* Hint at TPS settings */ + switch (c->code_rate_HP) { + case FEC_2_3: + tps |= (1 << 7); + break; + case FEC_3_4: + tps |= (2 << 7); + break; + case FEC_5_6: + tps |= (3 << 7); + break; + case FEC_7_8: + tps |= (4 << 7); + break; + case FEC_1_2: + case FEC_AUTO: + break; + default: + return -EINVAL; + } + + switch (c->code_rate_LP) { + case FEC_2_3: + tps |= (1 << 4); + break; + case FEC_3_4: + tps |= (2 << 4); + break; + case FEC_5_6: + tps |= (3 << 4); + break; + case FEC_7_8: + tps |= (4 << 4); + break; + case FEC_1_2: + case FEC_AUTO: + break; + case FEC_NONE: + if (c->hierarchy == HIERARCHY_AUTO || + c->hierarchy == HIERARCHY_NONE) + break; + fallthrough; + default: + return -EINVAL; + } + + switch (c->modulation) { + case QPSK: + break; + case QAM_AUTO: + case QAM_16: + tps |= (1 << 13); + break; + case QAM_64: + tps |= (2 << 13); + break; + default: + return -EINVAL; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + break; + case TRANSMISSION_MODE_8K: + tps |= (1 << 0); + break; + default: + return -EINVAL; + } + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + break; + case GUARD_INTERVAL_1_16: + tps |= (1 << 2); + break; + case GUARD_INTERVAL_1_8: + tps |= (2 << 2); + break; + case GUARD_INTERVAL_1_4: + tps |= (3 << 2); + break; + default: + return -EINVAL; + } + + switch (c->hierarchy) { + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + tps |= (1 << 10); + break; + case HIERARCHY_2: + tps |= (2 << 10); + break; + case HIERARCHY_4: + tps |= (3 << 10); + break; + default: + return -EINVAL; + } + + zl10353_single_write(fe, TPS_GIVEN_1, msb(tps)); + zl10353_single_write(fe, TPS_GIVEN_0, lsb(tps)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* + * If there is no tuner attached to the secondary I2C bus, we call + * set_params to program a potential tuner attached somewhere else. + * Otherwise, we update the PLL registers via calc_regs. + */ + if (state->config.no_tuner) { + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + } else if (fe->ops.tuner_ops.calc_regs) { + fe->ops.tuner_ops.calc_regs(fe, pllbuf + 1, 5); + pllbuf[1] <<= 1; + zl10353_write(fe, pllbuf, sizeof(pllbuf)); + } + + zl10353_single_write(fe, 0x5F, 0x13); + + /* If no attached tuner or invalid PLL registers, just start the FSM. */ + if (state->config.no_tuner || fe->ops.tuner_ops.calc_regs == NULL) + zl10353_single_write(fe, FSM_GO, 0x01); + else + zl10353_single_write(fe, TUNER_GO, 0x01); + + return 0; +} + +static int zl10353_get_parameters(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) +{ + struct zl10353_state *state = fe->demodulator_priv; + int s6, s9; + u16 tps; + static const u8 tps_fec_to_api[8] = { + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_5_6, + FEC_7_8, + FEC_AUTO, + FEC_AUTO, + FEC_AUTO + }; + + s6 = zl10353_read_register(state, STATUS_6); + s9 = zl10353_read_register(state, STATUS_9); + if (s6 < 0 || s9 < 0) + return -EREMOTEIO; + if ((s6 & (1 << 5)) == 0 || (s9 & (1 << 4)) == 0) + return -EINVAL; /* no FE or TPS lock */ + + tps = zl10353_read_register(state, TPS_RECEIVED_1) << 8 | + zl10353_read_register(state, TPS_RECEIVED_0); + + c->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; + c->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; + + switch ((tps >> 13) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + default: + c->modulation = QAM_AUTO; + break; + } + + c->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : + TRANSMISSION_MODE_2K; + + switch ((tps >> 2) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + c->guard_interval = GUARD_INTERVAL_AUTO; + break; + } + + switch ((tps >> 10) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + default: + c->hierarchy = HIERARCHY_AUTO; + break; + } + + c->frequency = state->frequency; + c->bandwidth_hz = state->bandwidth; + c->inversion = INVERSION_AUTO; + + return 0; +} + +static int zl10353_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct zl10353_state *state = fe->demodulator_priv; + int s6, s7, s8; + + if ((s6 = zl10353_read_register(state, STATUS_6)) < 0) + return -EREMOTEIO; + if ((s7 = zl10353_read_register(state, STATUS_7)) < 0) + return -EREMOTEIO; + if ((s8 = zl10353_read_register(state, STATUS_8)) < 0) + return -EREMOTEIO; + + *status = 0; + if (s6 & (1 << 2)) + *status |= FE_HAS_CARRIER; + if (s6 & (1 << 1)) + *status |= FE_HAS_VITERBI; + if (s6 & (1 << 5)) + *status |= FE_HAS_LOCK; + if (s7 & (1 << 4)) + *status |= FE_HAS_SYNC; + if (s8 & (1 << 6)) + *status |= FE_HAS_SIGNAL; + + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} + +static int zl10353_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct zl10353_state *state = fe->demodulator_priv; + + *ber = zl10353_read_register(state, RS_ERR_CNT_2) << 16 | + zl10353_read_register(state, RS_ERR_CNT_1) << 8 | + zl10353_read_register(state, RS_ERR_CNT_0); + + return 0; +} + +static int zl10353_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct zl10353_state *state = fe->demodulator_priv; + + u16 signal = zl10353_read_register(state, AGC_GAIN_1) << 10 | + zl10353_read_register(state, AGC_GAIN_0) << 2 | 3; + + *strength = ~signal; + + return 0; +} + +static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct zl10353_state *state = fe->demodulator_priv; + u8 _snr; + + if (debug_regs) + zl10353_dump_regs(fe); + + _snr = zl10353_read_register(state, SNR); + *snr = 10 * _snr / 8; + + return 0; +} + +static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct zl10353_state *state = fe->demodulator_priv; + u32 ubl = 0; + + ubl = zl10353_read_register(state, RS_UBC_1) << 8 | + zl10353_read_register(state, RS_UBC_0); + + state->ucblocks += ubl; + *ucblocks = state->ucblocks; + + return 0; +} + +static int zl10353_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 1000; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + + return 0; +} + +static int zl10353_init(struct dvb_frontend *fe) +{ + struct zl10353_state *state = fe->demodulator_priv; + u8 zl10353_reset_attach[6] = { 0x50, 0x03, 0x64, 0x46, 0x15, 0x0F }; + + if (debug_regs) + zl10353_dump_regs(fe); + if (state->config.parallel_ts) + zl10353_reset_attach[2] &= ~0x20; + if (state->config.clock_ctl_1) + zl10353_reset_attach[3] = state->config.clock_ctl_1; + if (state->config.pll_0) + zl10353_reset_attach[4] = state->config.pll_0; + + /* Do a "hard" reset if not already done */ + if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] || + zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) { + zl10353_write(fe, zl10353_reset_attach, + sizeof(zl10353_reset_attach)); + if (debug_regs) + zl10353_dump_regs(fe); + } + + return 0; +} + +static int zl10353_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct zl10353_state *state = fe->demodulator_priv; + u8 val = 0x0a; + + if (state->config.disable_i2c_gate_ctrl) { + /* No tuner attached to the internal I2C bus */ + /* If set enable I2C bridge, the main I2C bus stopped hardly */ + return 0; + } + + if (enable) + val |= 0x10; + + return zl10353_single_write(fe, 0x62, val); +} + +static void zl10353_release(struct dvb_frontend *fe) +{ + struct zl10353_state *state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops zl10353_ops; + +struct dvb_frontend *zl10353_attach(const struct zl10353_config *config, + struct i2c_adapter *i2c) +{ + struct zl10353_state *state = NULL; + int id; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct zl10353_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct zl10353_config)); + + /* check if the demod is there */ + id = zl10353_read_register(state, CHIP_ID); + if ((id != ID_ZL10353) && (id != ID_CE6230) && (id != ID_CE6231)) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &zl10353_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops zl10353_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Zarlink ZL10353 DVB-T", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = zl10353_release, + + .init = zl10353_init, + .sleep = zl10353_sleep, + .i2c_gate_ctrl = zl10353_i2c_gate_ctrl, + .write = zl10353_write, + + .set_frontend = zl10353_set_parameters, + .get_frontend = zl10353_get_parameters, + .get_tune_settings = zl10353_get_tune_settings, + + .read_status = zl10353_read_status, + .read_ber = zl10353_read_ber, + .read_signal_strength = zl10353_read_signal_strength, + .read_snr = zl10353_read_snr, + .read_ucblocks = zl10353_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +module_param(debug_regs, int, 0644); +MODULE_PARM_DESC(debug_regs, "Turn on/off frontend register dumps (default:off)."); + +MODULE_DESCRIPTION("Zarlink ZL10353 DVB-T demodulator driver"); +MODULE_AUTHOR("Chris Pascoe"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(zl10353_attach); diff --git a/drivers/media/dvb-frontends/zl10353.h b/drivers/media/dvb-frontends/zl10353.h new file mode 100644 index 0000000000..3debd82d4a --- /dev/null +++ b/drivers/media/dvb-frontends/zl10353.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Zarlink DVB-T ZL10353 demodulator + * + * Copyright (C) 2006, 2007 Christopher Pascoe <c.pascoe@itee.uq.edu.au> + */ + +#ifndef ZL10353_H +#define ZL10353_H + +#include <linux/dvb/frontend.h> + +struct zl10353_config +{ + /* demodulator's I2C address */ + u8 demod_address; + + /* frequencies in units of 0.1kHz */ + int adc_clock; /* default: 450560 (45.056 MHz) */ + int if2; /* default: 361667 (36.1667 MHz) */ + + /* set if no pll is connected to the secondary i2c bus */ + int no_tuner; + + /* set if parallel ts output is required */ + int parallel_ts; + + /* set if i2c_gate_ctrl disable is required */ + u8 disable_i2c_gate_ctrl:1; + + /* clock control registers (0x51-0x54) */ + u8 clock_ctl_1; /* default: 0x46 */ + u8 pll_0; /* default: 0x15 */ +}; + +#if IS_REACHABLE(CONFIG_DVB_ZL10353) +extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ZL10353 */ + +#endif /* ZL10353_H */ diff --git a/drivers/media/dvb-frontends/zl10353_priv.h b/drivers/media/dvb-frontends/zl10353_priv.h new file mode 100644 index 0000000000..4ac499d5aa --- /dev/null +++ b/drivers/media/dvb-frontends/zl10353_priv.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Zarlink DVB-T ZL10353 demodulator + * + * Copyright (C) 2006, 2007 Christopher Pascoe <c.pascoe@itee.uq.edu.au> + */ + +#ifndef _ZL10353_PRIV_ +#define _ZL10353_PRIV_ + +#define ID_ZL10353 0x14 /* Zarlink ZL10353 */ +#define ID_CE6230 0x18 /* Intel CE6230 */ +#define ID_CE6231 0x19 /* Intel CE6231 */ + +#define msb(x) (((x) >> 8) & 0xff) +#define lsb(x) ((x) & 0xff) + +enum zl10353_reg_addr { + INTERRUPT_0 = 0x00, + INTERRUPT_1 = 0x01, + INTERRUPT_2 = 0x02, + INTERRUPT_3 = 0x03, + INTERRUPT_4 = 0x04, + INTERRUPT_5 = 0x05, + STATUS_6 = 0x06, + STATUS_7 = 0x07, + STATUS_8 = 0x08, + STATUS_9 = 0x09, + AGC_GAIN_1 = 0x0A, + AGC_GAIN_0 = 0x0B, + SNR = 0x10, + RS_ERR_CNT_2 = 0x11, + RS_ERR_CNT_1 = 0x12, + RS_ERR_CNT_0 = 0x13, + RS_UBC_1 = 0x14, + RS_UBC_0 = 0x15, + TPS_RECEIVED_1 = 0x1D, + TPS_RECEIVED_0 = 0x1E, + TPS_CURRENT_1 = 0x1F, + TPS_CURRENT_0 = 0x20, + CLOCK_CTL_0 = 0x51, + CLOCK_CTL_1 = 0x52, + PLL_0 = 0x53, + PLL_1 = 0x54, + RESET = 0x55, + AGC_TARGET = 0x56, + MCLK_RATIO = 0x5C, + ACQ_CTL = 0x5E, + TRL_NOMINAL_RATE_1 = 0x65, + TRL_NOMINAL_RATE_0 = 0x66, + INPUT_FREQ_1 = 0x6C, + INPUT_FREQ_0 = 0x6D, + TPS_GIVEN_1 = 0x6E, + TPS_GIVEN_0 = 0x6F, + TUNER_GO = 0x70, + FSM_GO = 0x71, + CHIP_ID = 0x7F, + CHAN_STEP_1 = 0xE4, + CHAN_STEP_0 = 0xE5, + OFDM_LOCK_TIME = 0xE7, + FEC_LOCK_TIME = 0xE8, + ACQ_DELAY = 0xE9, +}; + +#endif /* _ZL10353_PRIV_ */ |